You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ye...@apache.org on 2010/05/10 18:11:51 UTC
svn commit: r942809 [3/3] - in /poi/trunk: ./
src/contrib/src/org/apache/poi/hssf/contrib/
src/documentation/content/xdocs/ src/examples/src/org/apache/poi/hssf/view/
src/examples/src/org/apache/poi/hssf/view/brush/
src/examples/src/org/apache/poi/ss/e...
Added: poi/trunk/src/java/org/apache/poi/ss/format/CellFormatResult.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/format/CellFormatResult.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/format/CellFormatResult.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/format/CellFormatResult.java Mon May 10 16:11:50 2010
@@ -0,0 +1,58 @@
+/* ====================================================================
+ 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.format;
+
+import java.awt.*;
+
+/**
+ * This object contains the result of applying a cell format or cell format part
+ * to a value.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ * @see CellFormatPart#apply(Object)
+ * @see CellFormat#apply(Object)
+ */
+public class CellFormatResult {
+ /**
+ * This is <tt>true</tt> if no condition was given that applied to the
+ * value, or if the condition is satisfied. If a condition is relevant, and
+ * when applied the value fails the test, this is <tt>false</tt>.
+ */
+ public final boolean applies;
+
+ /** The resulting text. This will never be <tt>null</tt>. */
+ public final String text;
+
+ /**
+ * The color the format sets, or <tt>null</tt> if the format sets no color.
+ * This will always be <tt>null</tt> if {@link #applies} is <tt>false</tt>.
+ */
+ public final Color textColor;
+
+ /**
+ * Creates a new format result object.
+ *
+ * @param applies The value for {@link #applies}.
+ * @param text The value for {@link #text}.
+ * @param textColor The value for {@link #textColor}.
+ */
+ public CellFormatResult(boolean applies, String text, Color textColor) {
+ this.applies = applies;
+ this.text = text;
+ this.textColor = (applies ? textColor : null);
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/ss/format/CellFormatType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/format/CellFormatType.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/format/CellFormatType.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/format/CellFormatType.java Mon May 10 16:11:50 2010
@@ -0,0 +1,74 @@
+package org.apache.poi.ss.format;
+
+/**
+ * The different kinds of formats that the formatter understands.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ */
+public enum CellFormatType {
+
+ /** The general (default) format; also used for <tt>"General"</tt>. */
+ GENERAL {
+ CellFormatter formatter(String pattern) {
+ return new CellGeneralFormatter();
+ }
+ boolean isSpecial(char ch) {
+ return false;
+ }
+ },
+ /** A numeric format. */
+ NUMBER {
+ boolean isSpecial(char ch) {
+ return false;
+ }
+ CellFormatter formatter(String pattern) {
+ return new CellNumberFormatter(pattern);
+ }
+ },
+ /** A date format. */
+ DATE {
+ boolean isSpecial(char ch) {
+ return ch == '\'' || (ch <= '\u007f' && Character.isLetter(ch));
+ }
+ CellFormatter formatter(String pattern) {
+ return new CellDateFormatter(pattern);
+ }
+ },
+ /** An elapsed time format. */
+ ELAPSED {
+ boolean isSpecial(char ch) {
+ return false;
+ }
+ CellFormatter formatter(String pattern) {
+ return new CellElapsedFormatter(pattern);
+ }
+ },
+ /** A text format. */
+ TEXT {
+ boolean isSpecial(char ch) {
+ return false;
+ }
+ CellFormatter formatter(String pattern) {
+ return new CellTextFormatter(pattern);
+ }
+ };
+
+ /**
+ * Returns <tt>true</tt> if the format is special and needs to be quoted.
+ *
+ * @param ch The character to test.
+ *
+ * @return <tt>true</tt> if the format is special and needs to be quoted.
+ */
+ abstract boolean isSpecial(char ch);
+
+ /**
+ * Returns a new formatter of the appropriate type, for the given pattern.
+ * The pattern must be appropriate for the type.
+ *
+ * @param pattern The pattern to use.
+ *
+ * @return A new formatter of the appropriate type, for the given pattern.
+ */
+ abstract CellFormatter formatter(String pattern);
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/ss/format/CellFormatter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/format/CellFormatter.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/format/CellFormatter.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/format/CellFormatter.java Mon May 10 16:11:50 2010
@@ -0,0 +1,102 @@
+/* ====================================================================
+ 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.format;
+
+import java.util.Locale;
+import java.util.logging.Logger;
+
+/**
+ * This is the abstract supertype for the various cell formatters.
+ *
+ * @@author Ken Arnold, Industrious Media LLC
+ */
+public abstract class CellFormatter {
+ /** The original specified format. */
+ protected final String format;
+
+ /**
+ * This is the locale used to get a consistent format result from which to
+ * work.
+ */
+ public static final Locale LOCALE = Locale.US;
+
+ /**
+ * Creates a new formatter object, storing the format in {@link #format}.
+ *
+ * @param format The format.
+ */
+ public CellFormatter(String format) {
+ this.format = format;
+ }
+
+ /** The logger to use in the formatting code. */
+ static final Logger logger = Logger.getLogger(
+ CellFormatter.class.getName());
+
+ /**
+ * Format a value according the format string.
+ *
+ * @param toAppendTo The buffer to append to.
+ * @param value The value to format.
+ */
+ public abstract void formatValue(StringBuffer toAppendTo, Object value);
+
+ /**
+ * Format a value according to the type, in the most basic way.
+ *
+ * @param toAppendTo The buffer to append to.
+ * @param value The value to format.
+ */
+ public abstract void simpleValue(StringBuffer toAppendTo, Object value);
+
+ /**
+ * Formats the value, returning the resulting string.
+ *
+ * @param value The value to format.
+ *
+ * @return The value, formatted.
+ */
+ public String format(Object value) {
+ StringBuffer sb = new StringBuffer();
+ formatValue(sb, value);
+ return sb.toString();
+ }
+
+ /**
+ * Formats the value in the most basic way, returning the resulting string.
+ *
+ * @param value The value to format.
+ *
+ * @return The value, formatted.
+ */
+ public String simpleFormat(Object value) {
+ StringBuffer sb = new StringBuffer();
+ simpleValue(sb, value);
+ return sb.toString();
+ }
+
+ /**
+ * Returns the input string, surrounded by quotes.
+ *
+ * @param str The string to quote.
+ *
+ * @return The input string, surrounded by quotes.
+ */
+ static String quote(String str) {
+ return '"' + str + '"';
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/ss/format/CellGeneralFormatter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/format/CellGeneralFormatter.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/format/CellGeneralFormatter.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/format/CellGeneralFormatter.java Mon May 10 16:11:50 2010
@@ -0,0 +1,84 @@
+/* ====================================================================
+ 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.format;
+
+import java.util.Formatter;
+
+/**
+ * A formatter for the default "General" cell format.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ */
+public class CellGeneralFormatter extends CellFormatter {
+ /** Creates a new general formatter. */
+ public CellGeneralFormatter() {
+ super("General");
+ }
+
+ /**
+ * The general style is not quite the same as any other, or any combination
+ * of others.
+ *
+ * @param toAppendTo The buffer to append to.
+ * @param value The value to format.
+ */
+ public void formatValue(StringBuffer toAppendTo, Object value) {
+ if (value instanceof Number) {
+ double val = ((Number) value).doubleValue();
+ if (val == 0) {
+ toAppendTo.append('0');
+ return;
+ }
+
+ String fmt;
+ double exp = Math.log10(Math.abs(val));
+ boolean stripZeros = true;
+ if (exp > 10 || exp < -9)
+ fmt = "%1.5E";
+ else if ((long) val != val)
+ fmt = "%1.9f";
+ else {
+ fmt = "%1.0f";
+ stripZeros = false;
+ }
+
+ Formatter formatter = new Formatter(toAppendTo);
+ formatter.format(LOCALE, fmt, value);
+ if (stripZeros) {
+ // strip off trailing zeros
+ int removeFrom;
+ if (fmt.endsWith("E"))
+ removeFrom = toAppendTo.lastIndexOf("E") - 1;
+ else
+ removeFrom = toAppendTo.length() - 1;
+ while (toAppendTo.charAt(removeFrom) == '0') {
+ toAppendTo.deleteCharAt(removeFrom--);
+ }
+ if (toAppendTo.charAt(removeFrom) == '.') {
+ toAppendTo.deleteCharAt(removeFrom--);
+ }
+ }
+ } else {
+ toAppendTo.append(value.toString());
+ }
+ }
+
+ /** Equivalent to {@link #formatValue(StringBuffer,Object)}. {@inheritDoc}. */
+ public void simpleValue(StringBuffer toAppendTo, Object value) {
+ formatValue(toAppendTo, value);
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/ss/format/CellNumberFormatter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/format/CellNumberFormatter.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/format/CellNumberFormatter.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/format/CellNumberFormatter.java Mon May 10 16:11:50 2010
@@ -0,0 +1,1085 @@
+/* ====================================================================
+ 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.format;
+
+import org.apache.poi.ss.format.CellFormatPart.PartHandler;
+
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Formatter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+
+/**
+ * This class implements printing out a value using a number format.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ */
+public class CellNumberFormatter extends CellFormatter {
+ private final String desc;
+ private String printfFmt;
+ private double scale;
+ private Special decimalPoint;
+ private Special slash;
+ private Special exponent;
+ private Special numerator;
+ private Special afterInteger;
+ private Special afterFractional;
+ private boolean integerCommas;
+ private final List<Special> specials;
+ private List<Special> integerSpecials;
+ private List<Special> fractionalSpecials;
+ private List<Special> numeratorSpecials;
+ private List<Special> denominatorSpecials;
+ private List<Special> exponentSpecials;
+ private List<Special> exponentDigitSpecials;
+ private int maxDenominator;
+ private String numeratorFmt;
+ private String denominatorFmt;
+ private boolean improperFraction;
+ private DecimalFormat decimalFmt;
+
+ static final CellFormatter SIMPLE_NUMBER = new CellFormatter("General") {
+ public void formatValue(StringBuffer toAppendTo, Object value) {
+ if (value == null)
+ return;
+ if (value instanceof Number) {
+ Number num = (Number) value;
+ if (num.doubleValue() % 1.0 == 0)
+ SIMPLE_INT.formatValue(toAppendTo, value);
+ else
+ SIMPLE_FLOAT.formatValue(toAppendTo, value);
+ } else {
+ CellTextFormatter.SIMPLE_TEXT.formatValue(toAppendTo, value);
+ }
+ }
+
+ public void simpleValue(StringBuffer toAppendTo, Object value) {
+ formatValue(toAppendTo, value);
+ }
+ };
+
+ private static final CellFormatter SIMPLE_INT = new CellNumberFormatter(
+ "#");
+ private static final CellFormatter SIMPLE_FLOAT = new CellNumberFormatter(
+ "#.#");
+
+ /**
+ * This class is used to mark where the special characters in the format
+ * are, as opposed to the other characters that are simply printed.
+ */
+ static class Special {
+ final char ch;
+ int pos;
+
+ Special(char ch, int pos) {
+ this.ch = ch;
+ this.pos = pos;
+ }
+
+ @Override
+ public String toString() {
+ return "'" + ch + "' @ " + pos;
+ }
+ }
+
+ /**
+ * This class represents a single modification to a result string. The way
+ * this works is complicated, but so is numeric formatting. In general, for
+ * most formats, we use a DecimalFormat object that will put the string out
+ * in a known format, usually with all possible leading and trailing zeros.
+ * We then walk through the result and the orginal format, and note any
+ * modifications that need to be made. Finally, we go through and apply
+ * them all, dealing with overlapping modifications.
+ */
+ static class StringMod implements Comparable<StringMod> {
+ final Special special;
+ final int op;
+ CharSequence toAdd;
+ Special end;
+ boolean startInclusive;
+ boolean endInclusive;
+
+ public static final int BEFORE = 1;
+ public static final int AFTER = 2;
+ public static final int REPLACE = 3;
+
+ private StringMod(Special special, CharSequence toAdd, int op) {
+ this.special = special;
+ this.toAdd = toAdd;
+ this.op = op;
+ }
+
+ public StringMod(Special start, boolean startInclusive, Special end,
+ boolean endInclusive, char toAdd) {
+ this(start, startInclusive, end, endInclusive);
+ this.toAdd = toAdd + "";
+ }
+
+ public StringMod(Special start, boolean startInclusive, Special end,
+ boolean endInclusive) {
+ special = start;
+ this.startInclusive = startInclusive;
+ this.end = end;
+ this.endInclusive = endInclusive;
+ op = REPLACE;
+ toAdd = "";
+ }
+
+ public int compareTo(StringMod that) {
+ int diff = special.pos - that.special.pos;
+ if (diff != 0)
+ return diff;
+ else
+ return op - that.op;
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ try {
+ return compareTo((StringMod) that) == 0;
+ } catch (RuntimeException ignored) {
+ // NullPointerException or CastException
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return special.hashCode() + op;
+ }
+ }
+
+ private class NumPartHandler implements PartHandler {
+ private char insertSignForExponent;
+
+ public String handlePart(Matcher m, String part, CellFormatType type,
+ StringBuffer desc) {
+ int pos = desc.length();
+ char firstCh = part.charAt(0);
+ switch (firstCh) {
+ case 'e':
+ case 'E':
+ // See comment in writeScientific -- exponent handling is complex.
+ // (1) When parsing the format, remove the sign from after the 'e' and
+ // put it before the first digit of the exponent.
+ if (exponent == null && specials.size() > 0) {
+ specials.add(exponent = new Special('.', pos));
+ insertSignForExponent = part.charAt(1);
+ return part.substring(0, 1);
+ }
+ break;
+
+ case '0':
+ case '?':
+ case '#':
+ if (insertSignForExponent != '\0') {
+ specials.add(new Special(insertSignForExponent, pos));
+ desc.append(insertSignForExponent);
+ insertSignForExponent = '\0';
+ pos++;
+ }
+ for (int i = 0; i < part.length(); i++) {
+ char ch = part.charAt(i);
+ specials.add(new Special(ch, pos + i));
+ }
+ break;
+
+ case '.':
+ if (decimalPoint == null && specials.size() > 0)
+ specials.add(decimalPoint = new Special('.', pos));
+ break;
+
+ case '/':
+ //!! This assumes there is a numerator and a denominator, but these are actually optional
+ if (slash == null && specials.size() > 0) {
+ numerator = previousNumber();
+ // If the first number in the whole format is the numerator, the
+ // entire number should be printed as an improper fraction
+ if (numerator == firstDigit(specials))
+ improperFraction = true;
+ specials.add(slash = new Special('.', pos));
+ }
+ break;
+
+ case '%':
+ // don't need to remember because we don't need to do anything with these
+ scale *= 100;
+ break;
+
+ default:
+ return null;
+ }
+ return part;
+ }
+ }
+
+ /**
+ * Creates a new cell number formatter.
+ *
+ * @param format The format to parse.
+ */
+ public CellNumberFormatter(String format) {
+ super(format);
+
+ scale = 1;
+
+ specials = new LinkedList<Special>();
+
+ NumPartHandler partHandler = new NumPartHandler();
+ StringBuffer descBuf = CellFormatPart.parseFormat(format,
+ CellFormatType.NUMBER, partHandler);
+
+ // These are inconsistent settings, so ditch 'em
+ if ((decimalPoint != null || exponent != null) && slash != null) {
+ slash = null;
+ numerator = null;
+ }
+
+ interpretCommas(descBuf);
+
+ int precision;
+ int fractionPartWidth = 0;
+ if (decimalPoint == null) {
+ precision = 0;
+ } else {
+ precision = interpretPrecision();
+ fractionPartWidth = 1 + precision;
+ if (precision == 0) {
+ // This means the format has a ".", but that output should have no decimals after it.
+ // We just stop treating it specially
+ specials.remove(decimalPoint);
+ decimalPoint = null;
+ }
+ }
+
+ if (precision == 0)
+ fractionalSpecials = Collections.emptyList();
+ else
+ fractionalSpecials = specials.subList(specials.indexOf(
+ decimalPoint) + 1, fractionalEnd());
+ if (exponent == null)
+ exponentSpecials = Collections.emptyList();
+ else {
+ int exponentPos = specials.indexOf(exponent);
+ exponentSpecials = specialsFor(exponentPos, 2);
+ exponentDigitSpecials = specialsFor(exponentPos + 2);
+ }
+
+ if (slash == null) {
+ numeratorSpecials = Collections.emptyList();
+ denominatorSpecials = Collections.emptyList();
+ } else {
+ if (numerator == null)
+ numeratorSpecials = Collections.emptyList();
+ else
+ numeratorSpecials = specialsFor(specials.indexOf(numerator));
+
+ denominatorSpecials = specialsFor(specials.indexOf(slash) + 1);
+ if (denominatorSpecials.isEmpty()) {
+ // no denominator follows the slash, drop the fraction idea
+ numeratorSpecials = Collections.emptyList();
+ } else {
+ maxDenominator = maxValue(denominatorSpecials);
+ numeratorFmt = singleNumberFormat(numeratorSpecials);
+ denominatorFmt = singleNumberFormat(denominatorSpecials);
+ }
+ }
+
+ integerSpecials = specials.subList(0, integerEnd());
+
+ if (exponent == null) {
+ StringBuffer fmtBuf = new StringBuffer("%");
+
+ int integerPartWidth = calculateIntegerPartWidth();
+ int totalWidth = integerPartWidth + fractionPartWidth;
+
+ fmtBuf.append('0').append(totalWidth).append('.').append(precision);
+
+ fmtBuf.append("f");
+ printfFmt = fmtBuf.toString();
+ } else {
+ StringBuffer fmtBuf = new StringBuffer();
+ boolean first = true;
+ List<Special> specialList = integerSpecials;
+ if (integerSpecials.size() == 1) {
+ // If we don't do this, we get ".6e5" instead of "6e4"
+ fmtBuf.append("0");
+ first = false;
+ } else
+ for (Special s : specialList) {
+ if (isDigitFmt(s)) {
+ fmtBuf.append(first ? '#' : '0');
+ first = false;
+ }
+ }
+ if (fractionalSpecials.size() > 0) {
+ fmtBuf.append('.');
+ for (Special s : fractionalSpecials) {
+ if (isDigitFmt(s)) {
+ if (!first)
+ fmtBuf.append('0');
+ first = false;
+ }
+ }
+ }
+ fmtBuf.append('E');
+ placeZeros(fmtBuf, exponentSpecials.subList(2,
+ exponentSpecials.size()));
+ decimalFmt = new DecimalFormat(fmtBuf.toString());
+ }
+
+ if (exponent != null)
+ scale =
+ 1; // in "e" formats,% and trailing commas have no scaling effect
+
+ desc = descBuf.toString();
+ }
+
+ private static void placeZeros(StringBuffer sb, List<Special> specials) {
+ for (Special s : specials) {
+ if (isDigitFmt(s))
+ sb.append('0');
+ }
+ }
+
+ private static Special firstDigit(List<Special> specials) {
+ for (Special s : specials) {
+ if (isDigitFmt(s))
+ return s;
+ }
+ return null;
+ }
+
+ static StringMod insertMod(Special special, CharSequence toAdd, int where) {
+ return new StringMod(special, toAdd, where);
+ }
+
+ static StringMod deleteMod(Special start, boolean startInclusive,
+ Special end, boolean endInclusive) {
+
+ return new StringMod(start, startInclusive, end, endInclusive);
+ }
+
+ static StringMod replaceMod(Special start, boolean startInclusive,
+ Special end, boolean endInclusive, char withChar) {
+
+ return new StringMod(start, startInclusive, end, endInclusive,
+ withChar);
+ }
+
+ private static String singleNumberFormat(List<Special> numSpecials) {
+ return "%0" + numSpecials.size() + "d";
+ }
+
+ private static int maxValue(List<Special> s) {
+ return (int) Math.round(Math.pow(10, s.size()) - 1);
+ }
+
+ private List<Special> specialsFor(int pos, int takeFirst) {
+ if (pos >= specials.size())
+ return Collections.emptyList();
+ ListIterator<Special> it = specials.listIterator(pos + takeFirst);
+ Special last = it.next();
+ int end = pos + takeFirst;
+ while (it.hasNext()) {
+ Special s = it.next();
+ if (!isDigitFmt(s) || s.pos - last.pos > 1)
+ break;
+ end++;
+ last = s;
+ }
+ return specials.subList(pos, end + 1);
+ }
+
+ private List<Special> specialsFor(int pos) {
+ return specialsFor(pos, 0);
+ }
+
+ private static boolean isDigitFmt(Special s) {
+ return s.ch == '0' || s.ch == '?' || s.ch == '#';
+ }
+
+ private Special previousNumber() {
+ ListIterator<Special> it = specials.listIterator(specials.size());
+ while (it.hasPrevious()) {
+ Special s = it.previous();
+ if (isDigitFmt(s)) {
+ Special numStart = s;
+ Special last = s;
+ while (it.hasPrevious()) {
+ s = it.previous();
+ if (last.pos - s.pos > 1) // it has to be continuous digits
+ break;
+ if (isDigitFmt(s))
+ numStart = s;
+ else
+ break;
+ last = s;
+ }
+ return numStart;
+ }
+ }
+ return null;
+ }
+
+ private int calculateIntegerPartWidth() {
+ ListIterator<Special> it = specials.listIterator();
+ int digitCount = 0;
+ while (it.hasNext()) {
+ Special s = it.next();
+ //!! Handle fractions: The previous set of digits before that is the numerator, so we should stop short of that
+ if (s == afterInteger)
+ break;
+ else if (isDigitFmt(s))
+ digitCount++;
+ }
+ return digitCount;
+ }
+
+ private int interpretPrecision() {
+ if (decimalPoint == null) {
+ return -1;
+ } else {
+ int precision = 0;
+ ListIterator<Special> it = specials.listIterator(specials.indexOf(
+ decimalPoint));
+ if (it.hasNext())
+ it.next(); // skip over the decimal point itself
+ while (it.hasNext()) {
+ Special s = it.next();
+ if (isDigitFmt(s))
+ precision++;
+ else
+ break;
+ }
+ return precision;
+ }
+ }
+
+ private void interpretCommas(StringBuffer sb) {
+ // In the integer part, commas at the end are scaling commas; other commas mean to show thousand-grouping commas
+ ListIterator<Special> it = specials.listIterator(integerEnd());
+
+ boolean stillScaling = true;
+ integerCommas = false;
+ while (it.hasPrevious()) {
+ Special s = it.previous();
+ if (s.ch != ',') {
+ stillScaling = false;
+ } else {
+ if (stillScaling) {
+ scale /= 1000;
+ } else {
+ integerCommas = true;
+ }
+ }
+ }
+
+ if (decimalPoint != null) {
+ it = specials.listIterator(fractionalEnd());
+ while (it.hasPrevious()) {
+ Special s = it.previous();
+ if (s.ch != ',') {
+ break;
+ } else {
+ scale /= 1000;
+ }
+ }
+ }
+
+ // Now strip them out -- we only need their interpretation, not their presence
+ it = specials.listIterator();
+ int removed = 0;
+ while (it.hasNext()) {
+ Special s = it.next();
+ s.pos -= removed;
+ if (s.ch == ',') {
+ removed++;
+ it.remove();
+ sb.deleteCharAt(s.pos);
+ }
+ }
+ }
+
+ private int integerEnd() {
+ if (decimalPoint != null)
+ afterInteger = decimalPoint;
+ else if (exponent != null)
+ afterInteger = exponent;
+ else if (numerator != null)
+ afterInteger = numerator;
+ else
+ afterInteger = null;
+ return afterInteger == null ? specials.size() : specials.indexOf(
+ afterInteger);
+ }
+
+ private int fractionalEnd() {
+ int end;
+ if (exponent != null)
+ afterFractional = exponent;
+ else if (numerator != null)
+ afterInteger = numerator;
+ else
+ afterFractional = null;
+ end = afterFractional == null ? specials.size() : specials.indexOf(
+ afterFractional);
+ return end;
+ }
+
+ /** {@inheritDoc} */
+ public void formatValue(StringBuffer toAppendTo, Object valueObject) {
+ double value = ((Number) valueObject).doubleValue();
+ value *= scale;
+
+ // the '-' sign goes at the front, always, so we pick it out
+ boolean negative = value < 0;
+ if (negative)
+ value = -value;
+
+ // Split out the fractional part if we need to print a fraction
+ double fractional = 0;
+ if (slash != null) {
+ if (improperFraction) {
+ fractional = value;
+ value = 0;
+ } else {
+ fractional = value % 1.0;
+ //noinspection SillyAssignment
+ value = (long) value;
+ }
+ }
+
+ Set<StringMod> mods = new TreeSet<StringMod>();
+ StringBuffer output = new StringBuffer(desc);
+
+ if (exponent != null) {
+ writeScientific(value, output, mods);
+ } else if (improperFraction) {
+ writeFraction(value, null, fractional, output, mods);
+ } else {
+ StringBuffer result = new StringBuffer();
+ Formatter f = new Formatter(result);
+ f.format(LOCALE, printfFmt, value);
+
+ if (numerator == null) {
+ writeFractional(result, output);
+ writeInteger(result, output, integerSpecials, mods,
+ integerCommas);
+ } else {
+ writeFraction(value, result, fractional, output, mods);
+ }
+ }
+
+ // Now strip out any remaining '#'s and add any pending text ...
+ ListIterator<Special> it = specials.listIterator();
+ Iterator<StringMod> changes = mods.iterator();
+ StringMod nextChange = (changes.hasNext() ? changes.next() : null);
+ int adjust = 0;
+ BitSet deletedChars = new BitSet(); // records chars already deleted
+ while (it.hasNext()) {
+ Special s = it.next();
+ int adjustedPos = s.pos + adjust;
+ if (!deletedChars.get(s.pos) && output.charAt(adjustedPos) == '#') {
+ output.deleteCharAt(adjustedPos);
+ adjust--;
+ deletedChars.set(s.pos);
+ }
+ while (nextChange != null && s == nextChange.special) {
+ int lenBefore = output.length();
+ int modPos = s.pos + adjust;
+ int posTweak = 0;
+ switch (nextChange.op) {
+ case StringMod.AFTER:
+ // ignore adding a comma after a deleted char (which was a '#')
+ if (nextChange.toAdd.equals(",") && deletedChars.get(s.pos))
+ break;
+ posTweak = 1;
+ //noinspection fallthrough
+ case StringMod.BEFORE:
+ output.insert(modPos + posTweak, nextChange.toAdd);
+ break;
+
+ case StringMod.REPLACE:
+ int delPos =
+ s.pos; // delete starting pos in original coordinates
+ if (!nextChange.startInclusive) {
+ delPos++;
+ modPos++;
+ }
+
+ // Skip over anything already deleted
+ while (deletedChars.get(delPos)) {
+ delPos++;
+ modPos++;
+ }
+
+ int delEndPos =
+ nextChange.end.pos; // delete end point in original
+ if (nextChange.endInclusive)
+ delEndPos++;
+
+ int modEndPos =
+ delEndPos + adjust; // delete end point in current
+
+ if (modPos < modEndPos) {
+ if (nextChange.toAdd == "")
+ output.delete(modPos, modEndPos);
+ else {
+ char fillCh = nextChange.toAdd.charAt(0);
+ for (int i = modPos; i < modEndPos; i++)
+ output.setCharAt(i, fillCh);
+ }
+ deletedChars.set(delPos, delEndPos);
+ }
+ break;
+
+ default:
+ throw new IllegalStateException(
+ "Unknown op: " + nextChange.op);
+ }
+ adjust += output.length() - lenBefore;
+
+ if (changes.hasNext())
+ nextChange = changes.next();
+ else
+ nextChange = null;
+ }
+ }
+
+ // Finally, add it to the string
+ if (negative)
+ toAppendTo.append('-');
+ toAppendTo.append(output);
+ }
+
+ private void writeScientific(double value, StringBuffer output,
+ Set<StringMod> mods) {
+
+ StringBuffer result = new StringBuffer();
+ FieldPosition fractionPos = new FieldPosition(
+ DecimalFormat.FRACTION_FIELD);
+ decimalFmt.format(value, result, fractionPos);
+ writeInteger(result, output, integerSpecials, mods, integerCommas);
+ writeFractional(result, output);
+
+ /*
+ * Exponent sign handling is complex.
+ *
+ * In DecimalFormat, you never put the sign in the format, and the sign only
+ * comes out of the format if it is negative.
+ *
+ * In Excel, you always say whether to always show the sign ("e+") or only
+ * show negative signs ("e-").
+ *
+ * Also in Excel, where you put the sign in the format is NOT where it comes
+ * out in the result. In the format, the sign goes with the "e"; in the
+ * output it goes with the exponent value. That is, if you say "#e-|#" you
+ * get "1e|-5", not "1e-|5". This makes sense I suppose, but it complicates
+ * things.
+ *
+ * Finally, everything else in this formatting code assumes that the base of
+ * the result is the original format, and that starting from that situation,
+ * the indexes of the original special characters can be used to place the new
+ * characters. As just described, this is not true for the exponent's sign.
+ * <p/>
+ * So here is how we handle it:
+ *
+ * (1) When parsing the format, remove the sign from after the 'e' and put it
+ * before the first digit of the exponent (where it will be shown).
+ *
+ * (2) Determine the result's sign.
+ *
+ * (3) If it's missing, put the sign into the output to keep the result
+ * lined up with the output. (In the result, "after the 'e'" and "before the
+ * first digit" are the same because the result has no extra chars to be in
+ * the way.)
+ *
+ * (4) In the output, remove the sign if it should not be shown ("e-" was used
+ * and the sign is negative) or set it to the correct value.
+ */
+
+ // (2) Determine the result's sign.
+ int ePos = fractionPos.getEndIndex();
+ int signPos = ePos + 1;
+ char expSignRes = result.charAt(signPos);
+ if (expSignRes != '-') {
+ // not a sign, so it's a digit, and therefore a positive exponent
+ expSignRes = '+';
+ // (3) If it's missing, put the sign into the output to keep the result
+ // lined up with the output.
+ result.insert(signPos, '+');
+ }
+
+ // Now the result lines up like it is supposed to with the specials' indexes
+ ListIterator<Special> it = exponentSpecials.listIterator(1);
+ Special expSign = it.next();
+ char expSignFmt = expSign.ch;
+
+ // (4) In the output, remove the sign if it should not be shown or set it to
+ // the correct value.
+ if (expSignRes == '-' || expSignFmt == '+')
+ mods.add(replaceMod(expSign, true, expSign, true, expSignRes));
+ else
+ mods.add(deleteMod(expSign, true, expSign, true));
+
+ StringBuffer exponentNum = new StringBuffer(result.substring(
+ signPos + 1));
+ writeInteger(exponentNum, output, exponentDigitSpecials, mods, false);
+ }
+
+ private void writeFraction(double value, StringBuffer result,
+ double fractional, StringBuffer output, Set<StringMod> mods) {
+
+ // Figure out if we are to suppress either the integer or fractional part.
+ // With # the suppressed part is removed; with ? it is replaced with spaces.
+ if (!improperFraction) {
+ // If fractional part is zero, and numerator doesn't have '0', write out
+ // only the integer part and strip the rest.
+ if (fractional == 0 && !hasChar('0', numeratorSpecials)) {
+ writeInteger(result, output, integerSpecials, mods, false);
+
+ Special start = integerSpecials.get(integerSpecials.size() - 1);
+ Special end = denominatorSpecials.get(
+ denominatorSpecials.size() - 1);
+ if (hasChar('?', integerSpecials, numeratorSpecials,
+ denominatorSpecials)) {
+ //if any format has '?', then replace the fraction with spaces
+ mods.add(replaceMod(start, false, end, true, ' '));
+ } else {
+ // otherwise, remove the fraction
+ mods.add(deleteMod(start, false, end, true));
+ }
+
+ // That's all, just return
+ return;
+ } else {
+ // New we check to see if we should remove the integer part
+ boolean allZero = (value == 0 && fractional == 0);
+ boolean willShowFraction = fractional != 0 || hasChar('0',
+ numeratorSpecials);
+ boolean removeBecauseZero = allZero && (hasOnly('#',
+ integerSpecials) || !hasChar('0', numeratorSpecials));
+ boolean removeBecauseFraction =
+ !allZero && value == 0 && willShowFraction && !hasChar(
+ '0', integerSpecials);
+ if (removeBecauseZero || removeBecauseFraction) {
+ Special start = integerSpecials.get(
+ integerSpecials.size() - 1);
+ if (hasChar('?', integerSpecials, numeratorSpecials)) {
+ mods.add(replaceMod(start, true, numerator, false,
+ ' '));
+ } else {
+ mods.add(deleteMod(start, true, numerator, false));
+ }
+ } else {
+ // Not removing the integer part -- print it out
+ writeInteger(result, output, integerSpecials, mods, false);
+ }
+ }
+ }
+
+ // Calculate and print the actual fraction (improper or otherwise)
+ try {
+ int n;
+ int d;
+ // the "fractional % 1" captures integer values in improper fractions
+ if (fractional == 0 || (improperFraction && fractional % 1 == 0)) {
+ // 0 as a fraction is reported by excel as 0/1
+ n = (int) Math.round(fractional);
+ d = 1;
+ } else {
+ Fraction frac = new Fraction(fractional, maxDenominator);
+ n = frac.getNumerator();
+ d = frac.getDenominator();
+ }
+ if (improperFraction)
+ n += Math.round(value * d);
+ writeSingleInteger(numeratorFmt, n, output, numeratorSpecials,
+ mods);
+ writeSingleInteger(denominatorFmt, d, output, denominatorSpecials,
+ mods);
+ } catch (RuntimeException ignored) {
+ ignored.printStackTrace();
+ }
+ }
+
+ private static boolean hasChar(char ch, List<Special>... numSpecials) {
+ for (List<Special> specials : numSpecials) {
+ for (Special s : specials) {
+ if (s.ch == ch) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasOnly(char ch, List<Special>... numSpecials) {
+ for (List<Special> specials : numSpecials) {
+ for (Special s : specials) {
+ if (s.ch != ch) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void writeSingleInteger(String fmt, int num, StringBuffer output,
+ List<Special> numSpecials, Set<StringMod> mods) {
+
+ StringBuffer sb = new StringBuffer();
+ Formatter formatter = new Formatter(sb);
+ formatter.format(LOCALE, fmt, num);
+ writeInteger(sb, output, numSpecials, mods, false);
+ }
+
+ private void writeInteger(StringBuffer result, StringBuffer output,
+ List<Special> numSpecials, Set<StringMod> mods,
+ boolean showCommas) {
+
+ int pos = result.indexOf(".") - 1;
+ if (pos < 0) {
+ if (exponent != null && numSpecials == integerSpecials)
+ pos = result.indexOf("E") - 1;
+ else
+ pos = result.length() - 1;
+ }
+
+ int strip;
+ for (strip = 0; strip < pos; strip++) {
+ char resultCh = result.charAt(strip);
+ if (resultCh != '0' && resultCh != ',')
+ break;
+ }
+
+ ListIterator<Special> it = numSpecials.listIterator(numSpecials.size());
+ boolean followWithComma = false;
+ Special lastOutputIntegerDigit = null;
+ int digit = 0;
+ while (it.hasPrevious()) {
+ char resultCh;
+ if (pos >= 0)
+ resultCh = result.charAt(pos);
+ else {
+ // If result is shorter than field, pretend there are leading zeros
+ resultCh = '0';
+ }
+ Special s = it.previous();
+ followWithComma = showCommas && digit > 0 && digit % 3 == 0;
+ boolean zeroStrip = false;
+ if (resultCh != '0' || s.ch == '0' || s.ch == '?' || pos >= strip) {
+ zeroStrip = s.ch == '?' && pos < strip;
+ output.setCharAt(s.pos, (zeroStrip ? ' ' : resultCh));
+ lastOutputIntegerDigit = s;
+ }
+ if (followWithComma) {
+ mods.add(insertMod(s, zeroStrip ? " " : ",", StringMod.AFTER));
+ followWithComma = false;
+ }
+ digit++;
+ --pos;
+ }
+ StringBuffer extraLeadingDigits = new StringBuffer();
+ if (pos >= 0) {
+ // We ran out of places to put digits before we ran out of digits; put this aside so we can add it later
+ ++pos; // pos was decremented at the end of the loop above when the iterator was at its end
+ extraLeadingDigits = new StringBuffer(result.substring(0, pos));
+ if (showCommas) {
+ while (pos > 0) {
+ if (digit > 0 && digit % 3 == 0)
+ extraLeadingDigits.insert(pos, ',');
+ digit++;
+ --pos;
+ }
+ }
+ mods.add(insertMod(lastOutputIntegerDigit, extraLeadingDigits,
+ StringMod.BEFORE));
+ }
+ }
+
+ private void writeFractional(StringBuffer result, StringBuffer output) {
+ int digit;
+ int strip;
+ ListIterator<Special> it;
+ if (fractionalSpecials.size() > 0) {
+ digit = result.indexOf(".") + 1;
+ if (exponent != null)
+ strip = result.indexOf("e") - 1;
+ else
+ strip = result.length() - 1;
+ while (strip > digit && result.charAt(strip) == '0')
+ strip--;
+ it = fractionalSpecials.listIterator();
+ while (it.hasNext()) {
+ Special s = it.next();
+ char resultCh = result.charAt(digit);
+ if (resultCh != '0' || s.ch == '0' || digit < strip)
+ output.setCharAt(s.pos, resultCh);
+ else if (s.ch == '?') {
+ // This is when we're in trailing zeros, and the format is '?'. We still strip out remaining '#'s later
+ output.setCharAt(s.pos, ' ');
+ }
+ digit++;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * For a number, this is <tt>"#"</tt> for integer values, and <tt>"#.#"</tt>
+ * for floating-point values.
+ */
+ public void simpleValue(StringBuffer toAppendTo, Object value) {
+ SIMPLE_NUMBER.formatValue(toAppendTo, value);
+ }
+
+ /**
+ * Based on org.apache.commons.math.fraction.Fraction from Apache Commons-Math.
+ * YK: The only reason of having this inner class is to avoid dependency on the Commons-Math jar.
+ */
+ private static class Fraction {
+ /** The denominator. */
+ private final int denominator;
+
+ /** The numerator. */
+ private final int numerator;
+
+ /**
+ * Create a fraction given the double value and either the maximum error
+ * allowed or the maximum number of denominator digits.
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within
+ * <code>epsilon</code> of <code>value</code>, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents
+ * @throws RuntimeException if the continued fraction failed to
+ * converge.
+ */
+ private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
+ {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long)Math.floor(r0);
+ if (a0 > overflow) {
+ throw new IllegalArgumentException("Overflow trying to convert "+value+" to fraction ("+a0+"/"+1l+")");
+ }
+
+ // check for (almost) integer arguments, which should not go
+ // to iterations.
+ if (Math.abs(a0 - value) < epsilon) {
+ this.numerator = (int) a0;
+ this.denominator = 1;
+ return;
+ }
+
+ long p0 = 1;
+ long q0 = 0;
+ long p1 = a0;
+ long q1 = 1;
+
+ long p2;
+ long q2;
+
+ int n = 0;
+ boolean stop = false;
+ do {
+ ++n;
+ double r1 = 1.0 / (r0 - a0);
+ long a1 = (long)Math.floor(r1);
+ p2 = (a1 * p1) + p0;
+ q2 = (a1 * q1) + q0;
+ if ((p2 > overflow) || (q2 > overflow)) {
+ throw new RuntimeException("Overflow trying to convert "+value+" to fraction ("+p2+"/"+q2+")");
+ }
+
+ double convergent = (double)p2 / (double)q2;
+ if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+ a0 = a1;
+ r0 = r1;
+ } else {
+ stop = true;
+ }
+ } while (!stop);
+
+ if (n >= maxIterations) {
+ throw new RuntimeException("Unable to convert "+value+" to fraction after "+maxIterations+" iterations");
+ }
+
+ if (q2 < maxDenominator) {
+ this.numerator = (int) p2;
+ this.denominator = (int) q2;
+ } else {
+ this.numerator = (int) p1;
+ this.denominator = (int) q1;
+ }
+
+ }
+
+ /**
+ * Create a fraction given the double value and maximum denominator.
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
+ * Continued Fraction</a> equations (11) and (22)-(26)</li>
+ * </ul>
+ * </p>
+ * @param value the double value to convert to a fraction.
+ * @param maxDenominator The maximum allowed value for denominator
+ * @throws RuntimeException if the continued fraction failed to
+ * converge
+ */
+ public Fraction(double value, int maxDenominator)
+ {
+ this(value, 0, maxDenominator, 100);
+ }
+
+ /**
+ * Access the denominator.
+ * @return the denominator.
+ */
+ public int getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Access the numerator.
+ * @return the numerator.
+ */
+ public int getNumerator() {
+ return numerator;
+ }
+
+ }
+
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/ss/format/CellTextFormatter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/format/CellTextFormatter.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/format/CellTextFormatter.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/format/CellTextFormatter.java Mon May 10 16:11:50 2010
@@ -0,0 +1,79 @@
+/* ====================================================================
+ 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.format;
+
+import org.apache.poi.ss.format.CellFormatPart.PartHandler;
+
+import java.util.regex.Matcher;
+
+/**
+ * This class implements printing out text.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ */
+public class CellTextFormatter extends CellFormatter {
+ private final int[] textPos;
+ private final String desc;
+
+ static final CellFormatter SIMPLE_TEXT = new CellTextFormatter("@");
+
+ public CellTextFormatter(String format) {
+ super(format);
+
+ final int[] numPlaces = new int[1];
+
+ desc = CellFormatPart.parseFormat(format, CellFormatType.TEXT,
+ new PartHandler() {
+ public String handlePart(Matcher m, String part,
+ CellFormatType type, StringBuffer desc) {
+ if (part.equals("@")) {
+ numPlaces[0]++;
+ return "\u0000";
+ }
+ return null;
+ }
+ }).toString();
+
+ // Remember the "@" positions in last-to-first order (to make insertion easier)
+ textPos = new int[numPlaces[0]];
+ int pos = desc.length() - 1;
+ for (int i = 0; i < textPos.length; i++) {
+ textPos[i] = desc.lastIndexOf("\u0000", pos);
+ pos = textPos[i] - 1;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void formatValue(StringBuffer toAppendTo, Object obj) {
+ int start = toAppendTo.length();
+ String text = obj.toString();
+ toAppendTo.append(desc);
+ for (int i = 0; i < textPos.length; i++) {
+ int pos = start + textPos[i];
+ toAppendTo.replace(pos, pos + 1, text);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * For text, this is just printing the text.
+ */
+ public void simpleValue(StringBuffer toAppendTo, Object value) {
+ SIMPLE_TEXT.formatValue(toAppendTo, value);
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/ss/format/package.html
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/format/package.html?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/format/package.html (added)
+++ poi/trunk/src/java/org/apache/poi/ss/format/package.html Mon May 10 16:11:50 2010
@@ -0,0 +1,3 @@
+<body>
+This package contains classes that implement cell formatting
+</body>
Added: poi/trunk/src/ooxml/testcases/org/apache/poi/ss/format/TestCellFormatPart.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/ss/format/TestCellFormatPart.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/ss/format/TestCellFormatPart.java (added)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/ss/format/TestCellFormatPart.java Mon May 10 16:11:50 2010
@@ -0,0 +1,126 @@
+/* ====================================================================
+ 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.format;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.xssf.XSSFITestDataProvider;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Test the individual CellFormatPart types. */
+public class TestCellFormatPart extends CellFormatTestBase {
+ private static final Pattern NUMBER_EXTRACT_FMT = Pattern.compile(
+ "([-+]?[0-9]+)(\\.[0-9]+)?.*(?:(e).*?([+-]?[0-9]+))",
+ Pattern.CASE_INSENSITIVE);
+
+ public TestCellFormatPart() {
+ super(XSSFITestDataProvider.instance);
+ }
+
+ public void testGeneralFormat() throws Exception {
+ runFormatTests("GeneralFormatTests.xlsx", new CellValue() {
+ public Object getValue(Cell cell) {
+ int type = CellFormat.ultimateType(cell);
+ if (type == Cell.CELL_TYPE_BOOLEAN)
+ return cell.getBooleanCellValue() ? "TRUE" : "FALSE";
+ else if (type == Cell.CELL_TYPE_NUMERIC)
+ return cell.getNumericCellValue();
+ else
+ return cell.getStringCellValue();
+ }
+ });
+ }
+
+ public void testNumberFormat() throws Exception {
+ runFormatTests("NumberFormatTests.xlsx", new CellValue() {
+ public Object getValue(Cell cell) {
+ return cell.getNumericCellValue();
+ }
+ });
+ }
+
+ public void testNumberApproxFormat() throws Exception {
+ runFormatTests("NumberFormatApproxTests.xlsx", new CellValue() {
+ public Object getValue(Cell cell) {
+ return cell.getNumericCellValue();
+ }
+
+ @Override
+ void equivalent(String expected, String actual,
+ CellFormatPart format) {
+ double expectedVal = extractNumber(expected);
+ double actualVal = extractNumber(actual);
+ // equal within 1%
+ double delta = expectedVal / 100;
+ assertEquals("format \"" + format + "\"," + expected + " ~= " +
+ actual, expectedVal, actualVal, delta);
+ }
+ });
+ }
+
+ public void testDateFormat() throws Exception {
+ runFormatTests("DateFormatTests.xlsx", new CellValue() {
+ public Object getValue(Cell cell) {
+ return cell.getDateCellValue();
+ }
+ });
+ }
+
+ public void testElapsedFormat() throws Exception {
+ runFormatTests("ElapsedFormatTests.xlsx", new CellValue() {
+ public Object getValue(Cell cell) {
+ return cell.getNumericCellValue();
+ }
+ });
+ }
+
+ public void testTextFormat() throws Exception {
+ runFormatTests("TextFormatTests.xlsx", new CellValue() {
+ public Object getValue(Cell cell) {
+ if (CellFormat.ultimateType(cell) == Cell.CELL_TYPE_BOOLEAN)
+ return cell.getBooleanCellValue() ? "TRUE" : "FALSE";
+ else
+ return cell.getStringCellValue();
+ }
+ });
+ }
+
+ public void testConditions() throws Exception {
+ runFormatTests("FormatConditionTests.xlsx", new CellValue() {
+ Object getValue(Cell cell) {
+ return cell.getNumericCellValue();
+ }
+ });
+ }
+
+ private double extractNumber(String str) {
+ Matcher m = NUMBER_EXTRACT_FMT.matcher(str);
+ if (!m.find())
+ throw new IllegalArgumentException(
+ "Cannot find numer in \"" + str + "\"");
+
+ StringBuffer sb = new StringBuffer();
+ // The groups in the pattern are the parts of the number
+ for (int i = 1; i <= m.groupCount(); i++) {
+ String part = m.group(i);
+ if (part != null)
+ sb.append(part);
+ }
+ return Double.valueOf(sb.toString());
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/testcases/org/apache/poi/ss/format/CellFormatTestBase.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/format/CellFormatTestBase.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/format/CellFormatTestBase.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/ss/format/CellFormatTestBase.java Mon May 10 16:11:50 2010
@@ -0,0 +1,293 @@
+/* ====================================================================
+ 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.format;
+
+import junit.framework.TestCase;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.ITestDataProvider;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.*;
+
+import static java.awt.Color.*;
+import java.io.IOException;
+
+/**
+ * This class is a base class for spreadsheet-based tests, such as are used for
+ * cell formatting. This reads tests from the spreadsheet, as well as reading
+ * flags that can be used to paramterize these tests.
+ * <p/>
+ * Each test has four parts: The expected result (column A), the format string
+ * (column B), the value to format (column C), and a comma-separated list of
+ * categores that this test falls in. Normally all tests are run, but if the
+ * flag "Categories" is not empty, only tests that have at least one category
+ * listed in "Categories" are run.
+ */
+@SuppressWarnings(
+ {"JUnitTestCaseWithNoTests", "JUnitTestClassNamingConvention"})
+public class CellFormatTestBase extends TestCase {
+ private final ITestDataProvider _testDataProvider;
+
+ protected Workbook workbook;
+
+ private String testFile;
+ private Map<String, String> testFlags;
+ private boolean tryAllColors;
+ private JLabel label;
+
+ private static final String[] COLOR_NAMES =
+ {"Black", "Red", "Green", "Blue", "Yellow", "Cyan", "Magenta",
+ "White"};
+ private static final Color[] COLORS =
+ {BLACK, RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA, WHITE};
+
+ public static final Color TEST_COLOR = ORANGE.darker();
+
+ protected CellFormatTestBase(ITestDataProvider testDataProvider) {
+ _testDataProvider = testDataProvider;
+ }
+
+ abstract static class CellValue {
+ abstract Object getValue(Cell cell);
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ Color getColor(Cell cell) {
+ return TEST_COLOR;
+ }
+
+ void equivalent(String expected, String actual, CellFormatPart format) {
+ assertEquals("format \"" + format + "\"", '"' + expected + '"',
+ '"' + actual + '"');
+ }
+ }
+
+ protected void runFormatTests(String workbookName, CellValue valueGetter)
+ throws IOException {
+
+ openWorkbook(workbookName);
+
+ readFlags(workbook);
+
+ Set<String> runCategories = new TreeSet<String>(
+ String.CASE_INSENSITIVE_ORDER);
+ String runCategoryList = flagString("Categories", "");
+ if (runCategoryList != null) {
+ runCategories.addAll(Arrays.asList(runCategoryList.split(
+ "\\s*,\\s*")));
+ runCategories.remove(""); // this can be found and means nothing
+ }
+
+ Sheet sheet = workbook.getSheet("Tests");
+ int end = sheet.getLastRowNum();
+ // Skip the header row, therefore "+ 1"
+ for (int r = sheet.getFirstRowNum() + 1; r <= end; r++) {
+ Row row = sheet.getRow(r);
+ if (row == null)
+ continue;
+ int cellnum = 0;
+ String expectedText = row.getCell(cellnum).getStringCellValue();
+ String format = row.getCell(1).getStringCellValue();
+ String testCategoryList = row.getCell(3).getStringCellValue();
+ boolean byCategory = runByCategory(runCategories, testCategoryList);
+ if ((!expectedText.isEmpty() || !format.isEmpty()) && byCategory) {
+ Cell cell = row.getCell(2);
+ tryFormat(r, expectedText, format, valueGetter, cell);
+ }
+ }
+ }
+
+ /**
+ * Open a given workbook.
+ *
+ * @param workbookName The workbook name. This is presumed to live in the
+ * "spreadsheets" directory under the directory named in
+ * the Java property "POI.testdata.path".
+ *
+ * @throws IOException
+ */
+ protected void openWorkbook(String workbookName)
+ throws IOException {
+ workbook = _testDataProvider.openSampleWorkbook(workbookName);
+ workbook.setMissingCellPolicy(Row.CREATE_NULL_AS_BLANK);
+ testFile = workbookName;
+ }
+
+ /**
+ * Read the flags from the workbook. Flags are on the sheet named "Flags",
+ * and consist of names in column A and values in column B. These are put
+ * into a map that can be queried later.
+ *
+ * @param wb The workbook to look in.
+ */
+ private void readFlags(Workbook wb) {
+ Sheet flagSheet = wb.getSheet("Flags");
+ testFlags = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+ if (flagSheet != null) {
+ int end = flagSheet.getLastRowNum();
+ // Skip the header row, therefore "+ 1"
+ for (int r = flagSheet.getFirstRowNum() + 1; r <= end; r++) {
+ Row row = flagSheet.getRow(r);
+ if (row == null)
+ continue;
+ String flagName = row.getCell(0).getStringCellValue();
+ String flagValue = row.getCell(1).getStringCellValue();
+ if (flagName.length() > 0) {
+ testFlags.put(flagName, flagValue);
+ }
+ }
+ }
+
+ tryAllColors = flagBoolean("AllColors", true);
+ }
+
+ /**
+ * Returns <tt>true</tt> if any of the categories for this run are contained
+ * in the test's listed categories.
+ *
+ * @param categories The categories of tests to be run. If this is
+ * empty, then all tests will be run.
+ * @param testCategories The categories that this test is in. This is a
+ * comma-separated list. If <em>any</em> tests in
+ * this list are in <tt>categories</tt>, the test will
+ * be run.
+ *
+ * @return <tt>true</tt> if the test should be run.
+ */
+ private boolean runByCategory(Set<String> categories,
+ String testCategories) {
+
+ if (categories.isEmpty())
+ return true;
+ // If there are specified categories, find out if this has one of them
+ for (String category : testCategories.split("\\s*,\\s*")) {
+ if (categories.contains(category)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void tryFormat(int row, String expectedText, String desc,
+ CellValue getter, Cell cell) {
+
+ Object value = getter.getValue(cell);
+ Color testColor = getter.getColor(cell);
+ if (testColor == null)
+ testColor = TEST_COLOR;
+
+ if (label == null)
+ label = new JLabel();
+ label.setForeground(testColor);
+ label.setText("xyzzy");
+
+ System.out.printf("Row %d: \"%s\" -> \"%s\": expected \"%s\"", row + 1,
+ String.valueOf(value), desc, expectedText);
+ System.out.flush();
+ String actualText = tryColor(desc, null, getter, value, expectedText,
+ testColor);
+ System.out.printf(", actual \"%s\")%n", actualText);
+ System.out.flush();
+
+ if (tryAllColors && testColor != TEST_COLOR) {
+ for (int i = 0; i < COLOR_NAMES.length; i++) {
+ String cname = COLOR_NAMES[i];
+ tryColor(desc, cname, getter, value, expectedText, COLORS[i]);
+ }
+ }
+ }
+
+ private String tryColor(String desc, String cname, CellValue getter,
+ Object value, String expectedText, Color expectedColor) {
+
+ if (cname != null)
+ desc = "[" + cname + "]" + desc;
+ Color origColor = label.getForeground();
+ CellFormatPart format = new CellFormatPart(desc);
+ if (!format.apply(label, value).applies) {
+ // If this doesn't apply, no color change is expected
+ expectedColor = origColor;
+ }
+
+ String actualText = label.getText();
+ Color actualColor = label.getForeground();
+ getter.equivalent(expectedText, actualText, format);
+ assertEquals(cname == null ? "no color" : "color " + cname,
+ expectedColor, actualColor);
+ return actualText;
+ }
+
+ /**
+ * Returns the value for the given flag. The flag has the value of
+ * <tt>true</tt> if the text value is <tt>"true"</tt>, <tt>"yes"</tt>, or
+ * <tt>"on"</tt> (ignoring case).
+ *
+ * @param flagName The name of the flag to fetch.
+ * @param expected The value for the flag that is expected when the tests
+ * are run for a full test. If the current value is not the
+ * expected one, you will get a warning in the test output.
+ * This is so that you do not accidentally leave a flag set
+ * to a value that prevents running some tests, thereby
+ * letting you accidentally release code that is not fully
+ * tested.
+ *
+ * @return The value for the flag.
+ */
+ protected boolean flagBoolean(String flagName, boolean expected) {
+ String value = testFlags.get(flagName);
+ boolean isSet;
+ if (value == null)
+ isSet = false;
+ else {
+ isSet = value.equalsIgnoreCase("true") || value.equalsIgnoreCase(
+ "yes") || value.equalsIgnoreCase("on");
+ }
+ warnIfUnexpected(flagName, expected, isSet);
+ return isSet;
+ }
+
+ /**
+ * Returns the value for the given flag.
+ *
+ * @param flagName The name of the flag to fetch.
+ * @param expected The value for the flag that is expected when the tests
+ * are run for a full test. If the current value is not the
+ * expected one, you will get a warning in the test output.
+ * This is so that you do not accidentally leave a flag set
+ * to a value that prevents running some tests, thereby
+ * letting you accidentally release code that is not fully
+ * tested.
+ *
+ * @return The value for the flag.
+ */
+ protected String flagString(String flagName, String expected) {
+ String value = testFlags.get(flagName);
+ if (value == null)
+ value = "";
+ warnIfUnexpected(flagName, expected, value);
+ return value;
+ }
+
+ private void warnIfUnexpected(String flagName, Object expected,
+ Object actual) {
+ if (!actual.equals(expected)) {
+ System.err.println(
+ "WARNING: " + testFile + ": " + "Flag " + flagName +
+ " = \"" + actual + "\" [not \"" + expected + "\"]");
+ }
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormat.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormat.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormat.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormat.java Mon May 10 16:11:50 2010
@@ -0,0 +1,32 @@
+/* ====================================================================
+ 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.format;
+
+import org.apache.poi.ss.format.CellFormat;
+
+import javax.swing.*;
+
+import junit.framework.TestCase;
+
+public class TestCellFormat extends TestCase {
+ public void testSome() {
+ JLabel l = new JLabel();
+ CellFormat fmt = CellFormat.getInstance(
+ "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)");
+ fmt.apply(l, 1.1);
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormatCondition.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormatCondition.java?rev=942809&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormatCondition.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/ss/format/TestCellFormatCondition.java Mon May 10 16:11:50 2010
@@ -0,0 +1,64 @@
+/* ====================================================================
+ 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.format;
+
+import junit.framework.TestCase;
+import org.apache.poi.ss.format.CellFormatCondition;
+
+public class TestCellFormatCondition extends TestCase {
+ public void testSVConditions() {
+ CellFormatCondition lt = CellFormatCondition.getInstance("<", "1.5");
+ assertTrue(lt.pass(1.4));
+ assertFalse(lt.pass(1.5));
+ assertFalse(lt.pass(1.6));
+
+ CellFormatCondition le = CellFormatCondition.getInstance("<=", "1.5");
+ assertTrue(le.pass(1.4));
+ assertTrue(le.pass(1.5));
+ assertFalse(le.pass(1.6));
+
+ CellFormatCondition gt = CellFormatCondition.getInstance(">", "1.5");
+ assertFalse(gt.pass(1.4));
+ assertFalse(gt.pass(1.5));
+ assertTrue(gt.pass(1.6));
+
+ CellFormatCondition ge = CellFormatCondition.getInstance(">=", "1.5");
+ assertFalse(ge.pass(1.4));
+ assertTrue(ge.pass(1.5));
+ assertTrue(ge.pass(1.6));
+
+ CellFormatCondition eqs = CellFormatCondition.getInstance("=", "1.5");
+ assertFalse(eqs.pass(1.4));
+ assertTrue(eqs.pass(1.5));
+ assertFalse(eqs.pass(1.6));
+
+ CellFormatCondition eql = CellFormatCondition.getInstance("==", "1.5");
+ assertFalse(eql.pass(1.4));
+ assertTrue(eql.pass(1.5));
+ assertFalse(eql.pass(1.6));
+
+ CellFormatCondition neo = CellFormatCondition.getInstance("<>", "1.5");
+ assertTrue(neo.pass(1.4));
+ assertFalse(neo.pass(1.5));
+ assertTrue(neo.pass(1.6));
+
+ CellFormatCondition nen = CellFormatCondition.getInstance("!=", "1.5");
+ assertTrue(nen.pass(1.4));
+ assertFalse(nen.pass(1.5));
+ assertTrue(nen.pass(1.6));
+ }
+}
\ No newline at end of file
Added: poi/trunk/test-data/spreadsheet/DateFormatTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/DateFormatTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/DateFormatTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/ElapsedFormatTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/ElapsedFormatTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/ElapsedFormatTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/FormatChoiceTests.xls
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/FormatChoiceTests.xls?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/FormatChoiceTests.xls
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/FormatChoiceTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/FormatChoiceTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/FormatChoiceTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/FormatConditionTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/FormatConditionTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/FormatConditionTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/GeneralFormatTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/GeneralFormatTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/GeneralFormatTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/NumberFormatApproxTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/NumberFormatApproxTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/NumberFormatApproxTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/NumberFormatTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/NumberFormatTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/NumberFormatTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: poi/trunk/test-data/spreadsheet/TextFormatTests.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/TextFormatTests.xlsx?rev=942809&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/spreadsheet/TextFormatTests.xlsx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org