Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-680505 via svnmerge from

https://svn.apache.org:443/repos/asf/poi/trunk

........
  r678539 | nick | 2008-07-21 20:35:47 +0100 (Mon, 21 Jul 2008) | 1 line
  
  Fix bug #45437 - Detect encrypted word documents, and throw an EncryptedDocumentException instead of a OOM
........
  r680394 | nick | 2008-07-28 17:41:47 +0100 (Mon, 28 Jul 2008) | 1 line
  
  Fix for DataFormatter on some JVMs
........
  r680470 | josh | 2008-07-28 21:08:15 +0100 (Mon, 28 Jul 2008) | 1 line
  
  More tweaks for bug 45404.  Fixes for JDK 1.4, improved member scoping and formatting.
........


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@680509 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-07-28 21:27:16 +00:00
parent a6701efc5f
commit a748887d6b
10 changed files with 564 additions and 543 deletions

View File

@ -51,6 +51,7 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release> </release>
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45437 - Detect encrypted word documents, and throw an EncryptedDocumentException instead of a OOM</action>
<action dev="POI-DEVELOPERS" type="add">45404 - New class, hssf.usermodel.HSSFDataFormatter, for formatting numbers and dates in the same way that Excel does</action> <action dev="POI-DEVELOPERS" type="add">45404 - New class, hssf.usermodel.HSSFDataFormatter, for formatting numbers and dates in the same way that Excel does</action>
<action dev="POI-DEVELOPERS" type="fix">45414 - Don't add too many UncalcedRecords to sheets with charts in them</action> <action dev="POI-DEVELOPERS" type="fix">45414 - Don't add too many UncalcedRecords to sheets with charts in them</action>
<action dev="POI-DEVELOPERS" type="fix">45398 - Support detecting date formats containing "am/pm" as date times</action> <action dev="POI-DEVELOPERS" type="fix">45398 - Support detecting date formats containing "am/pm" as date times</action>

View File

@ -48,6 +48,7 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release> </release>
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45437 - Detect encrypted word documents, and throw an EncryptedDocumentException instead of a OOM</action>
<action dev="POI-DEVELOPERS" type="add">45404 - New class, hssf.usermodel.HSSFDataFormatter, for formatting numbers and dates in the same way that Excel does</action> <action dev="POI-DEVELOPERS" type="add">45404 - New class, hssf.usermodel.HSSFDataFormatter, for formatting numbers and dates in the same way that Excel does</action>
<action dev="POI-DEVELOPERS" type="fix">45414 - Don't add too many UncalcedRecords to sheets with charts in them</action> <action dev="POI-DEVELOPERS" type="fix">45414 - Don't add too many UncalcedRecords to sheets with charts in them</action>
<action dev="POI-DEVELOPERS" type="fix">45398 - Support detecting date formats containing "am/pm" as date times</action> <action dev="POI-DEVELOPERS" type="fix">45398 - Support detecting date formats containing "am/pm" as date times</action>

View File

@ -0,0 +1,24 @@
/* ====================================================================
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;
public class EncryptedDocumentException extends IllegalStateException
{
public EncryptedDocumentException(String s) {
super(s);
}
}

View File

@ -73,69 +73,55 @@ import java.util.regex.Pattern;
* @author James May (james dot may at fmr dot com) * @author James May (james dot may at fmr dot com)
* *
*/ */
public class HSSFDataFormatter { public final class HSSFDataFormatter {
/** Pattern to find a number format: "0" or "#" */ /** Pattern to find a number format: "0" or "#" */
protected Pattern numPattern; private static final Pattern numPattern = Pattern.compile("[0#]+");
/** Pattern to find days of week as text "ddd...." */ /** Pattern to find days of week as text "ddd...." */
protected Pattern daysAsText; private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
/** Pattern to find "AM/PM" marker */ /** Pattern to find "AM/PM" marker */
protected Pattern amPmPattern; private static final Pattern amPmPattern = Pattern.compile("((A|P)[M/P]*)", Pattern.CASE_INSENSITIVE);
/** A regex to find patterns like [$$-1009] and [$<24>-452]. */ /** A regex to find patterns like [$$-1009] and [$<24>-452]. */
protected Pattern specialPatternGroup; private static final Pattern specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
/** <em>General</em> format for whole numbers. */ /** <em>General</em> format for whole numbers. */
protected Format generalWholeNumFormat; private static final Format generalWholeNumFormat = new DecimalFormat("#");
/** <em>General</em> format for decimal numbers. */ /** <em>General</em> format for decimal numbers. */
protected Format generalDecimalNumFormat; private static final Format generalDecimalNumFormat = new DecimalFormat("#.##########");
/** A default format to use when a number pattern cannot be parsed. */ /** A default format to use when a number pattern cannot be parsed. */
protected Format defaultNumFormat; private Format defaultNumFormat;
/** /**
* A map to cache formats. * A map to cache formats.
* Map<String,Format> formats * Map<String,Format> formats
*/ */
protected Map formats; private final Map formats;
/** /**
* Constructor * Constructor
*/ */
public HSSFDataFormatter() { public HSSFDataFormatter() {
numPattern = Pattern.compile("[0#]+");
daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
amPmPattern = Pattern.compile("((A|P)[M/P]*)", Pattern.CASE_INSENSITIVE);
specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
generalWholeNumFormat = new DecimalFormat("#");
generalDecimalNumFormat = new DecimalFormat("#.##########");
formats = new HashMap(); formats = new HashMap();
// init built-in formats // init built-in formats
init();
}
/** Format zipFormat = ZipPlusFourFormat.instance;
* Initialize the formatter. Called after construction.
*/
protected void init() {
ZipPlusFourFormat zipFormat = new ZipPlusFourFormat();
addFormat("00000\\-0000", zipFormat); addFormat("00000\\-0000", zipFormat);
addFormat("00000-0000", zipFormat); addFormat("00000-0000", zipFormat);
PhoneFormat phoneFormat = new PhoneFormat(); Format phoneFormat = PhoneFormat.instance;
// allow for format string variations // allow for format string variations
addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat); addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat); addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat);
addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat); addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
addFormat("###-####;(###) ###-####", phoneFormat); addFormat("###-####;(###) ###-####", phoneFormat);
SSNFormat ssnFormat = new SSNFormat(); Format ssnFormat = SSNFormat.instance;
addFormat("000\\-00\\-0000", ssnFormat); addFormat("000\\-00\\-0000", ssnFormat);
addFormat("000-00-0000", ssnFormat); addFormat("000-00-0000", ssnFormat);
} }
@ -153,7 +139,7 @@ public class HSSFDataFormatter {
* @param cell The cell to retrieve a Format for * @param cell The cell to retrieve a Format for
* @return A Format for the format String * @return A Format for the format String
*/ */
protected Format getFormat(HSSFCell cell) { private Format getFormat(HSSFCell cell) {
if ( cell.getCellStyle() == null) { if ( cell.getCellStyle() == null) {
return null; return null;
} }
@ -170,18 +156,17 @@ public class HSSFDataFormatter {
Format format = (Format)formats.get(formatStr); Format format = (Format)formats.get(formatStr);
if (format != null) { if (format != null) {
return format; return format;
} else if (formatStr.equals("General")) { }
if (formatStr.equals("General")) {
if (HSSFDataFormatter.isWholeNumber(cellValue)) { if (HSSFDataFormatter.isWholeNumber(cellValue)) {
return generalWholeNumFormat; return generalWholeNumFormat;
} else { }
return generalDecimalNumFormat; return generalDecimalNumFormat;
} }
} else {
format = createFormat(cellValue, formatIndex, formatStr); format = createFormat(cellValue, formatIndex, formatStr);
formats.put(formatStr, format); formats.put(formatStr, format);
return format; return format;
} }
}
/** /**
* Create and return a Format based on the format string from a cell's * Create and return a Format based on the format string from a cell's
@ -190,8 +175,7 @@ public class HSSFDataFormatter {
* @param cell The Excel cell * @param cell The Excel cell
* @return A Format representing the excel format. May return null. * @return A Format representing the excel format. May return null.
*/ */
protected Format createFormat(HSSFCell cell) { public Format createFormat(HSSFCell cell) {
String sFormat = cell.getCellStyle().getDataFormatString();
int formatIndex = cell.getCellStyle().getDataFormat(); int formatIndex = cell.getCellStyle().getDataFormat();
String formatStr = cell.getCellStyle().getDataFormatString(); String formatStr = cell.getCellStyle().getDataFormatString();
@ -204,7 +188,6 @@ public class HSSFDataFormatter {
// try to extract special characters like currency // try to extract special characters like currency
Matcher m = specialPatternGroup.matcher(formatStr); Matcher m = specialPatternGroup.matcher(formatStr);
try {
while(m.find()) { while(m.find()) {
String match = m.group(); String match = m.group();
String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-')); String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
@ -216,20 +199,27 @@ public class HSSFDataFormatter {
symbol = sb.toString(); symbol = sb.toString();
} }
formatStr = m.replaceAll(symbol); formatStr = m.replaceAll(symbol);
} m = specialPatternGroup.matcher(formatStr);
} catch (Exception e) {
return getDefaultFormat(cellValue);
} }
if(formatStr == null || formatStr.trim().length() == 0) { if(formatStr == null || formatStr.trim().length() == 0) {
return getDefaultFormat(cellValue); return getDefaultFormat(cellValue);
} }
Format returnVal = null;
StringBuffer sb = null;
if(HSSFDateUtil.isADateFormat(formatIndex,formatStr) && if(HSSFDateUtil.isADateFormat(formatIndex,formatStr) &&
HSSFDateUtil.isValidExcelDate(cellValue)) { HSSFDateUtil.isValidExcelDate(cellValue)) {
return createDateFormat(formatStr, cellValue);
}
if (numPattern.matcher(formatStr).find()) {
return createNumberFormat(formatStr, cellValue);
}
// TODO - when does this occur?
return null;
}
private Format createDateFormat(String pFormatStr, double cellValue) {
String formatStr = pFormatStr;
formatStr = formatStr.replaceAll("\\\\-","-"); formatStr = formatStr.replaceAll("\\\\-","-");
formatStr = formatStr.replaceAll("\\\\,",","); formatStr = formatStr.replaceAll("\\\\,",",");
formatStr = formatStr.replaceAll("\\\\ "," "); formatStr = formatStr.replaceAll("\\\\ "," ");
@ -237,9 +227,12 @@ public class HSSFDataFormatter {
boolean hasAmPm = false; boolean hasAmPm = false;
Matcher amPmMatcher = amPmPattern.matcher(formatStr); Matcher amPmMatcher = amPmPattern.matcher(formatStr);
while (amPmMatcher.find()) { while (amPmMatcher.find()) {
formatStr = amPmMatcher.replaceAll("a"); formatStr = amPmMatcher.replaceAll("@");
hasAmPm = true; hasAmPm = true;
amPmMatcher = amPmPattern.matcher(formatStr);
} }
formatStr = formatStr.replaceAll("@", "a");
Matcher dateMatcher = daysAsText.matcher(formatStr); Matcher dateMatcher = daysAsText.matcher(formatStr);
if (dateMatcher.find()) { if (dateMatcher.find()) {
@ -256,7 +249,7 @@ public class HSSFDataFormatter {
Excel displays the month instead of minutes." Excel displays the month instead of minutes."
*/ */
sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
char[] chars = formatStr.toCharArray(); char[] chars = formatStr.toCharArray();
boolean mIsMonth = true; boolean mIsMonth = true;
List ms = new ArrayList(); List ms = new ArrayList();
@ -312,7 +305,7 @@ public class HSSFDataFormatter {
formatStr = sb.toString(); formatStr = sb.toString();
try { try {
returnVal = new SimpleDateFormat(formatStr); return new SimpleDateFormat(formatStr);
} catch(IllegalArgumentException iae) { } catch(IllegalArgumentException iae) {
// the pattern could not be parsed correctly, // the pattern could not be parsed correctly,
@ -320,8 +313,10 @@ public class HSSFDataFormatter {
return getDefaultFormat(cellValue); return getDefaultFormat(cellValue);
} }
} else if (numPattern.matcher(formatStr).find()) { }
sb = new StringBuffer(formatStr);
private Format createNumberFormat(String formatStr, double cellValue) {
StringBuffer sb = new StringBuffer(formatStr);
for (int i = 0; i < sb.length(); i++) { for (int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i); char c = sb.charAt(i);
//handle (#,##0_); //handle (#,##0_);
@ -348,9 +343,9 @@ public class HSSFDataFormatter {
i--; i--;
} }
} }
formatStr = sb.toString();
try { try {
returnVal = new DecimalFormat(formatStr); return new DecimalFormat(sb.toString());
} catch(IllegalArgumentException iae) { } catch(IllegalArgumentException iae) {
// the pattern could not be parsed correctly, // the pattern could not be parsed correctly,
@ -358,13 +353,11 @@ public class HSSFDataFormatter {
return getDefaultFormat(cellValue); return getDefaultFormat(cellValue);
} }
} }
return returnVal;
}
/** /**
* Return true if the double value represents a whole number * Return true if the double value represents a whole number
* @param d the double value to check * @param d the double value to check
* @return true if d is a whole number * @return <code>true</code> if d is a whole number
*/ */
private static boolean isWholeNumber(double d) { private static boolean isWholeNumber(double d) {
return d == Math.floor(d); return d == Math.floor(d);
@ -375,7 +368,7 @@ public class HSSFDataFormatter {
* @param cell The cell * @param cell The cell
* @return a default format * @return a default format
*/ */
protected Format getDefaultFormat(HSSFCell cell) { public Format getDefaultFormat(HSSFCell cell) {
return getDefaultFormat(cell.getNumericCellValue()); return getDefaultFormat(cell.getNumericCellValue());
} }
private Format getDefaultFormat(double cellValue) { private Format getDefaultFormat(double cellValue) {
@ -384,11 +377,11 @@ public class HSSFDataFormatter {
return defaultNumFormat; return defaultNumFormat;
// otherwise use general format // otherwise use general format
} else if (isWholeNumber(cellValue)){
return generalWholeNumFormat;
} else {
return generalDecimalNumFormat;
} }
if (isWholeNumber(cellValue)){
return generalWholeNumFormat;
}
return generalDecimalNumFormat;
} }
/** /**
@ -399,14 +392,13 @@ public class HSSFDataFormatter {
* @param cell The cell * @param cell The cell
* @return a formatted date string * @return a formatted date string
*/ */
protected String getFormattedDateString(HSSFCell cell) { private String getFormattedDateString(HSSFCell cell) {
Format dateFormat = getFormat(cell); Format dateFormat = getFormat(cell);
Date d = cell.getDateCellValue(); Date d = cell.getDateCellValue();
if (dateFormat != null) { if (dateFormat != null) {
return dateFormat.format(d); return dateFormat.format(d);
} else {
return d.toString();
} }
return d.toString();
} }
/** /**
@ -418,15 +410,14 @@ public class HSSFDataFormatter {
* @param cell The cell * @param cell The cell
* @return a formatted number string * @return a formatted number string
*/ */
protected String getFormattedNumberString(HSSFCell cell) { private String getFormattedNumberString(HSSFCell cell) {
Format numberFormat = getFormat(cell); Format numberFormat = getFormat(cell);
double d = cell.getNumericCellValue(); double d = cell.getNumericCellValue();
if (numberFormat != null) { if (numberFormat == null) {
return numberFormat.format(new Double(d));
} else {
return String.valueOf(d); return String.valueOf(d);
} }
return numberFormat.format(new Double(d));
} }
/** /**
@ -441,20 +432,17 @@ public class HSSFDataFormatter {
Format dateFormat = getFormat(value, formatIndex, formatString); Format dateFormat = getFormat(value, formatIndex, formatString);
Date d = HSSFDateUtil.getJavaDate(value); Date d = HSSFDateUtil.getJavaDate(value);
if (dateFormat != null) { if (dateFormat == null) {
return dateFormat.format(d);
} else {
return d.toString(); return d.toString();
} }
} else { return dateFormat.format(d);
// Number }
// else Number
Format numberFormat = getFormat(value, formatIndex, formatString); Format numberFormat = getFormat(value, formatIndex, formatString);
if (numberFormat != null) { if (numberFormat == null) {
return numberFormat.format(new Double(value));
} else {
return String.valueOf(value); return String.valueOf(value);
} }
} return numberFormat.format(new Double(value));
} }
/** /**
@ -484,58 +472,51 @@ public class HSSFDataFormatter {
* String (""). Formula cells will be evaluated using the given * String (""). Formula cells will be evaluated using the given
* {@link HSSFFormulaEvaluator} if the evaluator is non-null. If the * {@link HSSFFormulaEvaluator} if the evaluator is non-null. If the
* evaluator is null, then the formula String will be returned. The caller * evaluator is null, then the formula String will be returned. The caller
* is responsible for setting the currentRow on the evaluator, otherwise an * is responsible for setting the currentRow on the evaluator
* IllegalArgumentException may be thrown.
*</p> *</p>
* *
* @param cell The cell * @param cell The cell (can be null)
* @param evaluator The HSSFFormulaEvaluator (can be null) * @param evaluator The HSSFFormulaEvaluator (can be null)
* @return a string value of the cell * @return a string value of the cell
* @throws IllegalArgumentException if cell type is <code>
* HSSFCell.CELL_TYPE_FORMULA</code> <b>and</b> evaluator is not null
* <b>and</b> the evlaluator's currentRow has not been set.
*/ */
public String formatCellValue(HSSFCell cell, public String formatCellValue(HSSFCell cell,
HSSFFormulaEvaluator evaluator) throws IllegalArgumentException { HSSFFormulaEvaluator evaluator) throws IllegalArgumentException {
String value = "";
if (cell == null) { if (cell == null) {
return value; return "";
} }
int cellType = cell.getCellType(); int cellType = cell.getCellType();
if (evaluator != null && cellType == HSSFCell.CELL_TYPE_FORMULA) { if (evaluator != null && cellType == HSSFCell.CELL_TYPE_FORMULA) {
try { try {
cellType = evaluator.evaluateFormulaCell(cell); cellType = evaluator.evaluateFormulaCell(cell);
} catch (Throwable t) { } catch (RuntimeException e) {
throw new IllegalArgumentException("Did you forget to set the current" + throw new RuntimeException("Did you forget to set the current" +
" row on the HSSFFormulaEvaluator?", t); " row on the HSSFFormulaEvaluator?", e);
} }
} }
switch (cellType) switch (cellType)
{ {
case HSSFCell.CELL_TYPE_FORMULA : case HSSFCell.CELL_TYPE_FORMULA :
// should only occur if evaluator is null // should only occur if evaluator is null
value = cell.getCellFormula(); return cell.getCellFormula();
break;
case HSSFCell.CELL_TYPE_NUMERIC : case HSSFCell.CELL_TYPE_NUMERIC :
if (HSSFDateUtil.isCellDateFormatted(cell)) { if (HSSFDateUtil.isCellDateFormatted(cell)) {
value = getFormattedDateString(cell); return getFormattedDateString(cell);
} else {
value = getFormattedNumberString(cell);
} }
break; return getFormattedNumberString(cell);
case HSSFCell.CELL_TYPE_STRING : case HSSFCell.CELL_TYPE_STRING :
value = cell.getRichStringCellValue().getString(); return cell.getRichStringCellValue().getString();
break;
case HSSFCell.CELL_TYPE_BOOLEAN : case HSSFCell.CELL_TYPE_BOOLEAN :
value = String.valueOf(cell.getBooleanCellValue()); return String.valueOf(cell.getBooleanCellValue());
case HSSFCell.CELL_TYPE_BLANK :
return "";
} }
return value; throw new RuntimeException("Unexpected celltype (" + cellType + ")");
} }
@ -585,23 +566,29 @@ public class HSSFDataFormatter {
// Some custom formats // Some custom formats
/**
* @return a <tt>DecimalFormat</tt> with parseIntegerOnly set <code>true</code>
*/
/* package */ static DecimalFormat createIntegerOnlyFormat(String fmt) {
DecimalFormat result = new DecimalFormat(fmt);
result.setParseIntegerOnly(true);
return result;
}
/** /**
* Format class for Excel's SSN format. This class mimics Excel's built-in * Format class for Excel's SSN format. This class mimics Excel's built-in
* SSN formatting. * SSN formatting.
* *
* @author James May * @author James May
*/ */
static class SSNFormat extends Format { private static final class SSNFormat extends Format {
private DecimalFormat df; public static final Format instance = new SSNFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
/** Constructor */ private SSNFormat() {
public SSNFormat() { // enforce singleton
df = new DecimalFormat("000000000");
df.setParseIntegerOnly(true);
} }
/** Format a number as an SSN */ /** Format a number as an SSN */
public String format(Number num) { public static String format(Number num) {
String result = df.format(num); String result = df.format(num);
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append(result.substring(0, 3)).append('-'); sb.append(result.substring(0, 3)).append('-');
@ -610,8 +597,7 @@ public class HSSFDataFormatter {
return sb.toString(); return sb.toString();
} }
public StringBuffer format(Object obj, StringBuffer toAppendTo, public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
FieldPosition pos) {
return toAppendTo.append(format((Number)obj)); return toAppendTo.append(format((Number)obj));
} }
@ -625,17 +611,15 @@ public class HSSFDataFormatter {
* built-in formatting for Zip + 4. * built-in formatting for Zip + 4.
* @author James May * @author James May
*/ */
static class ZipPlusFourFormat extends Format { private static final class ZipPlusFourFormat extends Format {
private DecimalFormat df; public static final Format instance = new ZipPlusFourFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
/** Constructor */ private ZipPlusFourFormat() {
public ZipPlusFourFormat() { // enforce singleton
df = new DecimalFormat("000000000");
df.setParseIntegerOnly(true);
} }
/** Format a number as Zip + 4 */ /** Format a number as Zip + 4 */
public String format(Number num) { public static String format(Number num) {
String result = df.format(num); String result = df.format(num);
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append(result.substring(0, 5)).append('-'); sb.append(result.substring(0, 5)).append('-');
@ -643,8 +627,7 @@ public class HSSFDataFormatter {
return sb.toString(); return sb.toString();
} }
public StringBuffer format(Object obj, StringBuffer toAppendTo, public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
FieldPosition pos) {
return toAppendTo.append(format((Number)obj)); return toAppendTo.append(format((Number)obj));
} }
@ -658,17 +641,15 @@ public class HSSFDataFormatter {
* built-in phone number formatting. * built-in phone number formatting.
* @author James May * @author James May
*/ */
static class PhoneFormat extends Format { private static final class PhoneFormat extends Format {
private DecimalFormat df; public static final Format instance = new PhoneFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("##########");
/** Constructor */ private PhoneFormat() {
public PhoneFormat() { // enforce singleton
df = new DecimalFormat("##########");
df.setParseIntegerOnly(true);
} }
/** Format a number as a phone number */ /** Format a number as a phone number */
public String format(Number num) { public static String format(Number num) {
String result = df.format(num); String result = df.format(num);
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
String seg1, seg2, seg3; String seg1, seg2, seg3;
@ -691,8 +672,7 @@ public class HSSFDataFormatter {
return sb.toString(); return sb.toString();
} }
public StringBuffer format(Object obj, StringBuffer toAppendTo, public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
FieldPosition pos) {
return toAppendTo.append(format((Number)obj)); return toAppendTo.append(format((Number)obj));
} }

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -15,19 +14,15 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hslf.exceptions; package org.apache.poi.hslf.exceptions;
import org.apache.poi.EncryptedDocumentException;
/** /**
* This exception is thrown when we try to open a PowerPoint file, and * This exception is thrown when we try to open a PowerPoint file, and
* discover that it is encrypted * discover that it is encrypted
*
* @author Nick Burch
*/ */
public class EncryptedPowerPointFileException extends EncryptedDocumentException
public class EncryptedPowerPointFileException extends IllegalStateException
{ {
public EncryptedPowerPointFileException(String s) { public EncryptedPowerPointFileException(String s) {
super(s); super(s);

View File

@ -28,6 +28,7 @@ import java.io.ByteArrayInputStream;
import java.util.Iterator; import java.util.Iterator;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.POIDocument; import org.apache.poi.POIDocument;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@ -174,9 +175,13 @@ public class HWPFDocument extends POIDocument
directory.createDocumentInputStream("WordDocument").read(_mainStream); directory.createDocumentInputStream("WordDocument").read(_mainStream);
// use the fib to determine the name of the table stream. // Create our FIB, and check for the doc being encrypted
_fib = new FileInformationBlock(_mainStream); _fib = new FileInformationBlock(_mainStream);
if(_fib.isFEncrypted()) {
throw new EncryptedDocumentException("Cannot process encrypted word files!");
}
// use the fib to determine the name of the table stream.
String name = "0Table"; String name = "0Table";
if (_fib.isFWhichTblStm()) if (_fib.isFWhichTblStm())
{ {

View File

@ -22,6 +22,7 @@ import java.io.FileOutputStream;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.StyleSheet; import org.apache.poi.hwpf.model.StyleSheet;
@ -138,4 +139,18 @@ public class TestProblems extends TestCase {
assertEquals(newLength, totalLength - deletedLength); assertEquals(newLength, totalLength - deletedLength);
} }
/**
* With an encrypted file, we should give a suitable
* exception, and not OOM
*/
public void testEncryptedFile() throws Exception {
try {
new HWPFDocument(new FileInputStream(
new File(dirname, "PasswordProtected.doc")));
fail();
} catch(EncryptedDocumentException e) {
// Good
}
}
} }

View File

@ -44,6 +44,7 @@ public class AllUserModelTests {
result.addTestSuite(TestHSSFClientAnchor.class); result.addTestSuite(TestHSSFClientAnchor.class);
result.addTestSuite(TestHSSFComment.class); result.addTestSuite(TestHSSFComment.class);
result.addTestSuite(TestHSSFConditionalFormatting.class); result.addTestSuite(TestHSSFConditionalFormatting.class);
result.addTestSuite(TestHSSFDataFormatter.class);
result.addTestSuite(TestHSSFDateUtil.class); result.addTestSuite(TestHSSFDateUtil.class);
result.addTestSuite(TestHSSFHeaderFooter.class); result.addTestSuite(TestHSSFHeaderFooter.class);
result.addTestSuite(TestHSSFHyperlink.class); result.addTestSuite(TestHSSFHyperlink.class);

View File

@ -29,10 +29,10 @@ import junit.framework.TestCase;
* @author James May (james dot may at fmr dot com) * @author James May (james dot may at fmr dot com)
* *
*/ */
public class TestHSSFDataFormatter extends TestCase { public final class TestHSSFDataFormatter extends TestCase {
HSSFDataFormatter formatter; private final HSSFDataFormatter formatter;
HSSFWorkbook wb; private final HSSFWorkbook wb;
public TestHSSFDataFormatter() { public TestHSSFDataFormatter() {
// create the formatter to test // create the formatter to test
@ -44,13 +44,13 @@ public class TestHSSFDataFormatter extends TestCase {
HSSFDataFormat format = wb.createDataFormat(); HSSFDataFormat format = wb.createDataFormat();
// create a row and put some cells in it // create a row and put some cells in it
HSSFRow row = sheet.createRow((short)0); HSSFRow row = sheet.createRow(0);
// date value for July 8 1901 1:19 PM // date value for July 8 1901 1:19 PM
double dateNum = 555.555; double dateNum = 555.555;
//valid date formats -- all should have "Jul" in output //valid date formats -- all should have "Jul" in output
String[] goodDatePatterns = new String[] { String[] goodDatePatterns ={
"[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy", "[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy",
"mmm/d/yy\\ h:mm PM;@", "mmm/d/yy\\ h:mm PM;@",
"mmmm/d/yy\\ h:mm;@", "mmmm/d/yy\\ h:mm;@",
@ -72,7 +72,7 @@ public class TestHSSFDataFormatter extends TestCase {
}; };
// valid number formats // valid number formats
String[] goodNumPatterns = new String[] { String[] goodNumPatterns = {
"#,##0.0000", "#,##0.0000",
"#,##0;[Red]#,##0", "#,##0;[Red]#,##0",
"(#,##0.00_);(#,##0.00)", "(#,##0.00_);(#,##0.00)",
@ -86,7 +86,7 @@ public class TestHSSFDataFormatter extends TestCase {
}; };
// invalid date formats -- will throw exception in DecimalFormat ctor // invalid date formats -- will throw exception in DecimalFormat ctor
String[] badNumPatterns = new String[] { String[] badNumPatterns = {
"#,#$'#0.0000", "#,#$'#0.0000",
"'#','#ABC#0;##,##0", "'#','#ABC#0;##,##0",
"000 '123 4'5'6 000", "000 '123 4'5'6 000",
@ -169,10 +169,10 @@ public class TestHSSFDataFormatter extends TestCase {
// Valid date formats -- cell values should be date formatted & not "555.555" // Valid date formats -- cell values should be date formatted & not "555.555"
HSSFRow row = wb.getSheetAt(0).getRow(0); HSSFRow row = wb.getSheetAt(0).getRow(0);
Iterator it = row.cellIterator(); Iterator it = row.cellIterator();
System.out.println("==== VALID DATE FORMATS ===="); log("==== VALID DATE FORMATS ====");
while (it.hasNext()) { while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next(); HSSFCell cell = (HSSFCell) it.next();
System.out.println(formatter.formatCellValue(cell)); log(formatter.formatCellValue(cell));
// should not be equal to "555.555" // should not be equal to "555.555"
assertTrue( ! "555.555".equals(formatter.formatCellValue(cell))); assertTrue( ! "555.555".equals(formatter.formatCellValue(cell)));
@ -184,10 +184,10 @@ public class TestHSSFDataFormatter extends TestCase {
// test number formats // test number formats
row = wb.getSheetAt(0).getRow(1); row = wb.getSheetAt(0).getRow(1);
it = row.cellIterator(); it = row.cellIterator();
System.out.println("\n==== VALID NUMBER FORMATS ===="); log("\n==== VALID NUMBER FORMATS ====");
while (it.hasNext()) { while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next(); HSSFCell cell = (HSSFCell) it.next();
System.out.println(formatter.formatCellValue(cell)); log(formatter.formatCellValue(cell));
// should not be equal to "1234567890.12345" // should not be equal to "1234567890.12345"
assertTrue( ! "1234567890.12345".equals(formatter.formatCellValue(cell))); assertTrue( ! "1234567890.12345".equals(formatter.formatCellValue(cell)));
@ -196,10 +196,10 @@ public class TestHSSFDataFormatter extends TestCase {
// test bad number formats // test bad number formats
row = wb.getSheetAt(0).getRow(2); row = wb.getSheetAt(0).getRow(2);
it = row.cellIterator(); it = row.cellIterator();
System.out.println("\n==== INVALID NUMBER FORMATS ===="); log("\n==== INVALID NUMBER FORMATS ====");
while (it.hasNext()) { while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next(); HSSFCell cell = (HSSFCell) it.next();
System.out.println(formatter.formatCellValue(cell)); log(formatter.formatCellValue(cell));
// should be equal to "1234567890.12345" // should be equal to "1234567890.12345"
assertEquals("1234567890.12345", formatter.formatCellValue(cell)); assertEquals("1234567890.12345", formatter.formatCellValue(cell));
} }
@ -207,22 +207,22 @@ public class TestHSSFDataFormatter extends TestCase {
// test Zip+4 format // test Zip+4 format
row = wb.getSheetAt(0).getRow(3); row = wb.getSheetAt(0).getRow(3);
HSSFCell cell = row.getCell(0); HSSFCell cell = row.getCell(0);
System.out.println("\n==== ZIP FORMAT ===="); log("\n==== ZIP FORMAT ====");
System.out.println(formatter.formatCellValue(cell)); log(formatter.formatCellValue(cell));
assertEquals("12345-6789", formatter.formatCellValue(cell)); assertEquals("12345-6789", formatter.formatCellValue(cell));
// test phone number format // test phone number format
row = wb.getSheetAt(0).getRow(4); row = wb.getSheetAt(0).getRow(4);
cell = row.getCell(0); cell = row.getCell(0);
System.out.println("\n==== PHONE FORMAT ===="); log("\n==== PHONE FORMAT ====");
System.out.println(formatter.formatCellValue(cell)); log(formatter.formatCellValue(cell));
assertEquals("(555) 123-4567", formatter.formatCellValue(cell)); assertEquals("(555) 123-4567", formatter.formatCellValue(cell));
// test SSN format // test SSN format
row = wb.getSheetAt(0).getRow(5); row = wb.getSheetAt(0).getRow(5);
cell = row.getCell(0); cell = row.getCell(0);
System.out.println("\n==== SSN FORMAT ===="); log("\n==== SSN FORMAT ====");
System.out.println(formatter.formatCellValue(cell)); log(formatter.formatCellValue(cell));
assertEquals("444-55-1234", formatter.formatCellValue(cell)); assertEquals("444-55-1234", formatter.formatCellValue(cell));
// null test-- null cell should result in empty String // null test-- null cell should result in empty String
@ -237,17 +237,17 @@ public class TestHSSFDataFormatter extends TestCase {
// test formula format // test formula format
HSSFRow row = wb.getSheetAt(0).getRow(6); HSSFRow row = wb.getSheetAt(0).getRow(6);
HSSFCell cell = row.getCell(0); HSSFCell cell = row.getCell(0);
System.out.println("\n==== FORMULA CELL ===="); log("\n==== FORMULA CELL ====");
// first without a formula evaluator // first without a formula evaluator
System.out.println(formatter.formatCellValue(cell) + "\t (without evaluator)"); log(formatter.formatCellValue(cell) + "\t (without evaluator)");
assertEquals("SUM(12.25,12.25)/100", formatter.formatCellValue(cell)); assertEquals("SUM(12.25,12.25)/100", formatter.formatCellValue(cell));
// now with a formula evaluator // now with a formula evaluator
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb.getSheetAt(0), wb); HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb.getSheetAt(0), wb);
//! must set current row ! //! must set current row !
evaluator.setCurrentRow(row); evaluator.setCurrentRow(row);
System.out.println(formatter.formatCellValue(cell, evaluator) + "\t\t\t (with evaluator)"); log(formatter.formatCellValue(cell, evaluator) + "\t\t\t (with evaluator)");
assertEquals("24.50%", formatter.formatCellValue(cell,evaluator)); assertEquals("24.50%", formatter.formatCellValue(cell,evaluator));
} }
@ -263,20 +263,19 @@ public class TestHSSFDataFormatter extends TestCase {
Format defaultFormat = new DecimalFormat("Balance $#,#00.00 USD;Balance -$#,#00.00 USD"); Format defaultFormat = new DecimalFormat("Balance $#,#00.00 USD;Balance -$#,#00.00 USD");
formatter.setDefaultNumberFormat(defaultFormat); formatter.setDefaultNumberFormat(defaultFormat);
double value = 10d; double value = 10d;
System.out.println("\n==== DEFAULT NUMBER FORMAT ===="); log("\n==== DEFAULT NUMBER FORMAT ====");
while (it.hasNext()) { while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next(); HSSFCell cell = (HSSFCell) it.next();
cell.setCellValue(cell.getNumericCellValue() * Math.random() / 1000000 - 1000); cell.setCellValue(cell.getNumericCellValue() * Math.random() / 1000000 - 1000);
System.out.println(formatter.formatCellValue(cell)); log(formatter.formatCellValue(cell));
assertTrue(formatter.formatCellValue(cell).startsWith("Balance ")); assertTrue(formatter.formatCellValue(cell).startsWith("Balance "));
assertTrue(formatter.formatCellValue(cell).endsWith(" USD")); assertTrue(formatter.formatCellValue(cell).endsWith(" USD"));
} }
} }
public static void main(String [] args) { private static void log(String msg) {
System.out if (false) { // successful tests should be silent
.println("Testing org.apache.poi.hssf.usermodel.TestHSSFDataFormatter"); System.out.println(msg);
junit.textui.TestRunner.run(TestHSSFDataFormatter.class); }
} }
} }