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 ea48f23d55
commit 5d95d3f082
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>
</release>
<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="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>

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>
</release>
<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="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>

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)
*
*/
public class HSSFDataFormatter {
public final class HSSFDataFormatter {
/** 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...." */
protected Pattern daysAsText;
private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
/** 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]. */
protected Pattern specialPatternGroup;
private static final Pattern specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
/** <em>General</em> format for whole numbers. */
protected Format generalWholeNumFormat;
private static final Format generalWholeNumFormat = new DecimalFormat("#");
/** <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. */
protected Format defaultNumFormat;
private Format defaultNumFormat;
/**
* A map to cache formats.
* Map<String,Format> formats
*/
protected Map formats;
private final Map formats;
/**
* Constructor
*/
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();
// init built-in formats
init();
}
/**
* Initialize the formatter. Called after construction.
*/
protected void init() {
ZipPlusFourFormat zipFormat = new ZipPlusFourFormat();
Format zipFormat = ZipPlusFourFormat.instance;
addFormat("00000\\-0000", zipFormat);
addFormat("00000-0000", zipFormat);
PhoneFormat phoneFormat = new PhoneFormat();
Format phoneFormat = PhoneFormat.instance;
// allow for format string variations
addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat);
addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
addFormat("###-####;(###) ###-####", phoneFormat);
SSNFormat ssnFormat = new SSNFormat();
Format ssnFormat = SSNFormat.instance;
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
* @return A Format for the format String
*/
protected Format getFormat(HSSFCell cell) {
private Format getFormat(HSSFCell cell) {
if ( cell.getCellStyle() == null) {
return null;
}
@ -170,17 +156,16 @@ public class HSSFDataFormatter {
Format format = (Format)formats.get(formatStr);
if (format != null) {
return format;
} else if (formatStr.equals("General")) {
}
if (formatStr.equals("General")) {
if (HSSFDataFormatter.isWholeNumber(cellValue)) {
return generalWholeNumFormat;
} else {
return generalDecimalNumFormat;
}
} else {
format = createFormat(cellValue, formatIndex, formatStr);
formats.put(formatStr, format);
return format;
return generalDecimalNumFormat;
}
format = createFormat(cellValue, formatIndex, formatStr);
formats.put(formatStr, format);
return format;
}
/**
@ -190,8 +175,7 @@ public class HSSFDataFormatter {
* @param cell The Excel cell
* @return A Format representing the excel format. May return null.
*/
protected Format createFormat(HSSFCell cell) {
String sFormat = cell.getCellStyle().getDataFormatString();
public Format createFormat(HSSFCell cell) {
int formatIndex = cell.getCellStyle().getDataFormat();
String formatStr = cell.getCellStyle().getDataFormatString();
@ -204,167 +188,176 @@ public class HSSFDataFormatter {
// try to extract special characters like currency
Matcher m = specialPatternGroup.matcher(formatStr);
try {
while(m.find()) {
String match = m.group();
String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
if (symbol.indexOf('$') > -1) {
StringBuffer sb = new StringBuffer();
sb.append(symbol.substring(0, symbol.indexOf('$')));
sb.append('\\');
sb.append(symbol.substring(symbol.indexOf('$'), symbol.length()));
symbol = sb.toString();
}
formatStr = m.replaceAll(symbol);
while(m.find()) {
String match = m.group();
String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
if (symbol.indexOf('$') > -1) {
StringBuffer sb = new StringBuffer();
sb.append(symbol.substring(0, symbol.indexOf('$')));
sb.append('\\');
sb.append(symbol.substring(symbol.indexOf('$'), symbol.length()));
symbol = sb.toString();
}
} catch (Exception e) {
return getDefaultFormat(cellValue);
formatStr = m.replaceAll(symbol);
m = specialPatternGroup.matcher(formatStr);
}
if(formatStr == null || formatStr.trim().length() == 0) {
return getDefaultFormat(cellValue);
}
Format returnVal = null;
StringBuffer sb = null;
if(HSSFDateUtil.isADateFormat(formatIndex,formatStr) &&
HSSFDateUtil.isValidExcelDate(cellValue)) {
formatStr = formatStr.replaceAll("\\\\-","-");
formatStr = formatStr.replaceAll("\\\\,",",");
formatStr = formatStr.replaceAll("\\\\ "," ");
formatStr = formatStr.replaceAll(";@", "");
boolean hasAmPm = false;
Matcher amPmMatcher = amPmPattern.matcher(formatStr);
while (amPmMatcher.find()) {
formatStr = amPmMatcher.replaceAll("a");
hasAmPm = true;
}
if(HSSFDateUtil.isADateFormat(formatIndex,formatStr) &&
HSSFDateUtil.isValidExcelDate(cellValue)) {
return createDateFormat(formatStr, cellValue);
}
if (numPattern.matcher(formatStr).find()) {
return createNumberFormat(formatStr, cellValue);
}
// TODO - when does this occur?
return null;
}
Matcher dateMatcher = daysAsText.matcher(formatStr);
if (dateMatcher.find()) {
String match = dateMatcher.group(0);
formatStr = dateMatcher.replaceAll(match.toUpperCase().replaceAll("D", "E"));
}
private Format createDateFormat(String pFormatStr, double cellValue) {
String formatStr = pFormatStr;
formatStr = formatStr.replaceAll("\\\\-","-");
formatStr = formatStr.replaceAll("\\\\,",",");
formatStr = formatStr.replaceAll("\\\\ "," ");
formatStr = formatStr.replaceAll(";@", "");
boolean hasAmPm = false;
Matcher amPmMatcher = amPmPattern.matcher(formatStr);
while (amPmMatcher.find()) {
formatStr = amPmMatcher.replaceAll("@");
hasAmPm = true;
amPmMatcher = amPmPattern.matcher(formatStr);
}
formatStr = formatStr.replaceAll("@", "a");
// Convert excel date format to SimpleDateFormat.
// Excel uses lower case 'm' for both minutes and months.
// From Excel help:
/*
The "m" or "mm" code must appear immediately after the "h" or"hh"
code or immediately before the "ss" code; otherwise, Microsoft
Excel displays the month instead of minutes."
*/
sb = new StringBuffer();
char[] chars = formatStr.toCharArray();
boolean mIsMonth = true;
List ms = new ArrayList();
for(int j=0; j<chars.length; j++) {
char c = chars[j];
if (c == 'h' || c == 'H') {
mIsMonth = false;
if (hasAmPm) {
sb.append('h');
} else {
sb.append('H');
}
}
else if (c == 'm') {
if(mIsMonth) {
sb.append('M');
ms.add(
new Integer(sb.length() -1)
);
} else {
sb.append('m');
}
}
else if (c == 's' || c == 'S') {
sb.append('s');
// if 'M' precedes 's' it should be minutes ('m')
for (int i = 0; i < ms.size(); i++) {
int index = ((Integer)ms.get(i)).intValue();
if (sb.charAt(index) == 'M') {
sb.replace(index, index+1, "m");
}
}
mIsMonth = true;
ms.clear();
}
else if (Character.isLetter(c)) {
mIsMonth = true;
ms.clear();
if (c == 'y' || c == 'Y') {
sb.append('y');
}
else if (c == 'd' || c == 'D') {
sb.append('d');
}
else {
sb.append(c);
}
}
else {
sb.append(c);
}
}
formatStr = sb.toString();
Matcher dateMatcher = daysAsText.matcher(formatStr);
if (dateMatcher.find()) {
String match = dateMatcher.group(0);
formatStr = dateMatcher.replaceAll(match.toUpperCase().replaceAll("D", "E"));
}
try {
returnVal = new SimpleDateFormat(formatStr);
} catch(IllegalArgumentException iae) {
// Convert excel date format to SimpleDateFormat.
// Excel uses lower case 'm' for both minutes and months.
// From Excel help:
/*
The "m" or "mm" code must appear immediately after the "h" or"hh"
code or immediately before the "ss" code; otherwise, Microsoft
Excel displays the month instead of minutes."
*/
// the pattern could not be parsed correctly,
// so fall back to the default number format
return getDefaultFormat(cellValue);
StringBuffer sb = new StringBuffer();
char[] chars = formatStr.toCharArray();
boolean mIsMonth = true;
List ms = new ArrayList();
for(int j=0; j<chars.length; j++) {
char c = chars[j];
if (c == 'h' || c == 'H') {
mIsMonth = false;
if (hasAmPm) {
sb.append('h');
} else {
sb.append('H');
}
}
} else if (numPattern.matcher(formatStr).find()) {
sb = new StringBuffer(formatStr);
for (int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i);
//handle (#,##0_);
if (c == '(') {
int idx = sb.indexOf(")", i);
if (idx > -1 && sb.charAt(idx -1) == '_') {
sb.deleteCharAt(idx);
sb.deleteCharAt(idx - 1);
sb.deleteCharAt(i);
i--;
else if (c == 'm') {
if(mIsMonth) {
sb.append('M');
ms.add(
new Integer(sb.length() -1)
);
} else {
sb.append('m');
}
}
else if (c == 's' || c == 'S') {
sb.append('s');
// if 'M' precedes 's' it should be minutes ('m')
for (int i = 0; i < ms.size(); i++) {
int index = ((Integer)ms.get(i)).intValue();
if (sb.charAt(index) == 'M') {
sb.replace(index, index+1, "m");
}
} else if (c == ')' && i > 0 && sb.charAt(i - 1) == '_') {
sb.deleteCharAt(i);
sb.deleteCharAt(i - 1);
i--;
// remove quotes and back slashes
} else if (c == '\\' || c == '"') {
sb.deleteCharAt(i);
i--;
}
mIsMonth = true;
ms.clear();
}
else if (Character.isLetter(c)) {
mIsMonth = true;
ms.clear();
if (c == 'y' || c == 'Y') {
sb.append('y');
}
else if (c == 'd' || c == 'D') {
sb.append('d');
}
else {
sb.append(c);
}
}
else {
sb.append(c);
}
}
formatStr = sb.toString();
// for scientific/engineering notation
} else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') {
try {
return new SimpleDateFormat(formatStr);
} catch(IllegalArgumentException iae) {
// the pattern could not be parsed correctly,
// so fall back to the default number format
return getDefaultFormat(cellValue);
}
}
private Format createNumberFormat(String formatStr, double cellValue) {
StringBuffer sb = new StringBuffer(formatStr);
for (int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i);
//handle (#,##0_);
if (c == '(') {
int idx = sb.indexOf(")", i);
if (idx > -1 && sb.charAt(idx -1) == '_') {
sb.deleteCharAt(idx);
sb.deleteCharAt(idx - 1);
sb.deleteCharAt(i);
i--;
}
}
formatStr = sb.toString();
try {
returnVal = new DecimalFormat(formatStr);
} catch(IllegalArgumentException iae) {
} else if (c == ')' && i > 0 && sb.charAt(i - 1) == '_') {
sb.deleteCharAt(i);
sb.deleteCharAt(i - 1);
i--;
// remove quotes and back slashes
} else if (c == '\\' || c == '"') {
sb.deleteCharAt(i);
i--;
// the pattern could not be parsed correctly,
// so fall back to the default number format
return getDefaultFormat(cellValue);
// for scientific/engineering notation
} else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') {
sb.deleteCharAt(i);
i--;
}
}
return returnVal;
try {
return new DecimalFormat(sb.toString());
} catch(IllegalArgumentException iae) {
// the pattern could not be parsed correctly,
// so fall back to the default number format
return getDefaultFormat(cellValue);
}
}
/**
* Return true if the double value represents a whole number
* @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) {
return d == Math.floor(d);
@ -375,7 +368,7 @@ public class HSSFDataFormatter {
* @param cell The cell
* @return a default format
*/
protected Format getDefaultFormat(HSSFCell cell) {
public Format getDefaultFormat(HSSFCell cell) {
return getDefaultFormat(cell.getNumericCellValue());
}
private Format getDefaultFormat(double cellValue) {
@ -384,11 +377,11 @@ public class HSSFDataFormatter {
return defaultNumFormat;
// otherwise use general format
} else if (isWholeNumber(cellValue)){
return generalWholeNumFormat;
} else {
return generalDecimalNumFormat;
}
if (isWholeNumber(cellValue)){
return generalWholeNumFormat;
}
return generalDecimalNumFormat;
}
/**
@ -399,15 +392,14 @@ public class HSSFDataFormatter {
* @param cell The cell
* @return a formatted date string
*/
protected String getFormattedDateString(HSSFCell cell) {
Format dateFormat = getFormat(cell);
Date d = cell.getDateCellValue();
if (dateFormat != null) {
return dateFormat.format(d);
} else {
return d.toString();
}
}
private String getFormattedDateString(HSSFCell cell) {
Format dateFormat = getFormat(cell);
Date d = cell.getDateCellValue();
if (dateFormat != null) {
return dateFormat.format(d);
}
return d.toString();
}
/**
* Returns the formatted value of an Excel number as a <tt>String</tt>
@ -418,44 +410,40 @@ public class HSSFDataFormatter {
* @param cell The cell
* @return a formatted number string
*/
protected String getFormattedNumberString(HSSFCell cell) {
private String getFormattedNumberString(HSSFCell cell) {
Format numberFormat = getFormat(cell);
double d = cell.getNumericCellValue();
if (numberFormat != null) {
return numberFormat.format(new Double(d));
} else {
return String.valueOf(d);
}
}
Format numberFormat = getFormat(cell);
double d = cell.getNumericCellValue();
if (numberFormat == null) {
return String.valueOf(d);
}
return numberFormat.format(new Double(d));
}
/**
* Formats the given raw cell value, based on the supplied
* format index and string, according to excel style rules.
* @see #formatCellValue(HSSFCell)
*/
public String formatRawCellContents(double value, int formatIndex, String formatString) {
// Is it a date?
if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
HSSFDateUtil.isValidExcelDate(value)) {
/**
* Formats the given raw cell value, based on the supplied
* format index and string, according to excel style rules.
* @see #formatCellValue(HSSFCell)
*/
public String formatRawCellContents(double value, int formatIndex, String formatString) {
// Is it a date?
if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
HSSFDateUtil.isValidExcelDate(value)) {
Format dateFormat = getFormat(value, formatIndex, formatString);
Date d = HSSFDateUtil.getJavaDate(value);
if (dateFormat != null) {
return dateFormat.format(d);
} else {
return d.toString();
}
} else {
// Number
Format numberFormat = getFormat(value, formatIndex, formatString);
if (numberFormat != null) {
return numberFormat.format(new Double(value));
} else {
return String.valueOf(value);
}
}
}
Format dateFormat = getFormat(value, formatIndex, formatString);
Date d = HSSFDateUtil.getJavaDate(value);
if (dateFormat == null) {
return d.toString();
}
return dateFormat.format(d);
}
// else Number
Format numberFormat = getFormat(value, formatIndex, formatString);
if (numberFormat == null) {
return String.valueOf(value);
}
return numberFormat.format(new Double(value));
}
/**
* <p>
@ -484,58 +472,51 @@ public class HSSFDataFormatter {
* String (""). Formula cells will be evaluated using the given
* {@link HSSFFormulaEvaluator} if the evaluator is non-null. If the
* evaluator is null, then the formula String will be returned. The caller
* is responsible for setting the currentRow on the evaluator, otherwise an
* IllegalArgumentException may be thrown.
* is responsible for setting the currentRow on the evaluator
*</p>
*
* @param cell The cell
* @param cell The cell (can be null)
* @param evaluator The HSSFFormulaEvaluator (can be null)
* @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,
HSSFFormulaEvaluator evaluator) throws IllegalArgumentException {
String value = "";
if (cell == null) {
return value;
return "";
}
int cellType = cell.getCellType();
if (evaluator != null && cellType == HSSFCell.CELL_TYPE_FORMULA) {
try {
cellType = evaluator.evaluateFormulaCell(cell);
} catch (Throwable t) {
throw new IllegalArgumentException("Did you forget to set the current" +
" row on the HSSFFormulaEvaluator?", t);
} catch (RuntimeException e) {
throw new RuntimeException("Did you forget to set the current" +
" row on the HSSFFormulaEvaluator?", e);
}
}
switch (cellType)
{
case HSSFCell.CELL_TYPE_FORMULA :
// should only occur if evaluator is null
value = cell.getCellFormula();
break;
{
case HSSFCell.CELL_TYPE_FORMULA :
// should only occur if evaluator is null
return cell.getCellFormula();
case HSSFCell.CELL_TYPE_NUMERIC :
case HSSFCell.CELL_TYPE_NUMERIC :
if (HSSFDateUtil.isCellDateFormatted(cell)) {
value = getFormattedDateString(cell);
} else {
value = getFormattedNumberString(cell);
}
break;
if (HSSFDateUtil.isCellDateFormatted(cell)) {
return getFormattedDateString(cell);
}
return getFormattedNumberString(cell);
case HSSFCell.CELL_TYPE_STRING :
value = cell.getRichStringCellValue().getString();
break;
case HSSFCell.CELL_TYPE_STRING :
return cell.getRichStringCellValue().getString();
case HSSFCell.CELL_TYPE_BOOLEAN :
value = String.valueOf(cell.getBooleanCellValue());
}
return value;
case HSSFCell.CELL_TYPE_BOOLEAN :
return String.valueOf(cell.getBooleanCellValue());
case HSSFCell.CELL_TYPE_BLANK :
return "";
}
throw new RuntimeException("Unexpected celltype (" + cellType + ")");
}
@ -585,23 +566,29 @@ public class HSSFDataFormatter {
// 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
* SSN formatting.
*
* @author James May
*/
static class SSNFormat extends Format {
private DecimalFormat df;
/** Constructor */
public SSNFormat() {
df = new DecimalFormat("000000000");
df.setParseIntegerOnly(true);
private static final class SSNFormat extends Format {
public static final Format instance = new SSNFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
private SSNFormat() {
// enforce singleton
}
/** Format a number as an SSN */
public String format(Number num) {
public static String format(Number num) {
String result = df.format(num);
StringBuffer sb = new StringBuffer();
sb.append(result.substring(0, 3)).append('-');
@ -610,8 +597,7 @@ public class HSSFDataFormatter {
return sb.toString();
}
public StringBuffer format(Object obj, StringBuffer toAppendTo,
FieldPosition pos) {
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(format((Number)obj));
}
@ -625,17 +611,15 @@ public class HSSFDataFormatter {
* built-in formatting for Zip + 4.
* @author James May
*/
static class ZipPlusFourFormat extends Format {
private DecimalFormat df;
/** Constructor */
public ZipPlusFourFormat() {
df = new DecimalFormat("000000000");
df.setParseIntegerOnly(true);
private static final class ZipPlusFourFormat extends Format {
public static final Format instance = new ZipPlusFourFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
private ZipPlusFourFormat() {
// enforce singleton
}
/** Format a number as Zip + 4 */
public String format(Number num) {
public static String format(Number num) {
String result = df.format(num);
StringBuffer sb = new StringBuffer();
sb.append(result.substring(0, 5)).append('-');
@ -643,8 +627,7 @@ public class HSSFDataFormatter {
return sb.toString();
}
public StringBuffer format(Object obj, StringBuffer toAppendTo,
FieldPosition pos) {
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(format((Number)obj));
}
@ -658,17 +641,15 @@ public class HSSFDataFormatter {
* built-in phone number formatting.
* @author James May
*/
static class PhoneFormat extends Format {
private DecimalFormat df;
/** Constructor */
public PhoneFormat() {
df = new DecimalFormat("##########");
df.setParseIntegerOnly(true);
private static final class PhoneFormat extends Format {
public static final Format instance = new PhoneFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("##########");
private PhoneFormat() {
// enforce singleton
}
/** Format a number as a phone number */
public String format(Number num) {
public static String format(Number num) {
String result = df.format(num);
StringBuffer sb = new StringBuffer();
String seg1, seg2, seg3;
@ -691,8 +672,7 @@ public class HSSFDataFormatter {
return sb.toString();
}
public StringBuffer format(Object obj, StringBuffer toAppendTo,
FieldPosition pos) {
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(format((Number)obj));
}

View File

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

View File

@ -28,6 +28,7 @@ import java.io.ByteArrayInputStream;
import java.util.Iterator;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.POIDocument;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@ -174,9 +175,13 @@ public class HWPFDocument extends POIDocument
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);
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";
if (_fib.isFWhichTblStm())
{

View File

@ -22,6 +22,7 @@ import java.io.FileOutputStream;
import junit.framework.TestCase;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.StyleSheet;
@ -138,4 +139,18 @@ public class TestProblems extends TestCase {
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(TestHSSFComment.class);
result.addTestSuite(TestHSSFConditionalFormatting.class);
result.addTestSuite(TestHSSFDataFormatter.class);
result.addTestSuite(TestHSSFDateUtil.class);
result.addTestSuite(TestHSSFHeaderFooter.class);
result.addTestSuite(TestHSSFHyperlink.class);

View File

@ -29,137 +29,137 @@ import junit.framework.TestCase;
* @author James May (james dot may at fmr dot com)
*
*/
public class TestHSSFDataFormatter extends TestCase {
public final class TestHSSFDataFormatter extends TestCase {
HSSFDataFormatter formatter;
HSSFWorkbook wb;
private final HSSFDataFormatter formatter;
private final HSSFWorkbook wb;
public TestHSSFDataFormatter() {
// create the formatter to test
formatter = new HSSFDataFormatter();
// create a workbook to test with
wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
HSSFDataFormat format = wb.createDataFormat();
wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
HSSFDataFormat format = wb.createDataFormat();
// create a row and put some cells in it
HSSFRow row = sheet.createRow((short)0);
// create a row and put some cells in it
HSSFRow row = sheet.createRow(0);
// date value for July 8 1901 1:19 PM
double dateNum = 555.555;
// date value for July 8 1901 1:19 PM
double dateNum = 555.555;
//valid date formats -- all should have "Jul" in output
String[] goodDatePatterns = new String[] {
"[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy",
"mmm/d/yy\\ h:mm PM;@",
"mmmm/d/yy\\ h:mm;@",
"mmmm/d;@",
"mmmm/d/yy;@",
"mmm/dd/yy;@",
"[$-409]d\\-mmm;@",
"[$-409]d\\-mmm\\-yy;@",
"[$-409]dd\\-mmm\\-yy;@",
"[$-409]mmm\\-yy;@",
"[$-409]mmmm\\-yy;@",
"[$-409]mmmm\\ d\\,\\ yyyy;@",
"[$-409]mmm/d/yy\\ h:mm:ss;@",
"[$-409]mmmm/d/yy\\ h:mm:ss am;@",
"[$-409]mmmmm;@",
"[$-409]mmmmm\\-yy;@",
"mmmm/d/yyyy;@",
"[$-409]d\\-mmm\\-yyyy;@"
};
//valid date formats -- all should have "Jul" in output
String[] goodDatePatterns ={
"[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy",
"mmm/d/yy\\ h:mm PM;@",
"mmmm/d/yy\\ h:mm;@",
"mmmm/d;@",
"mmmm/d/yy;@",
"mmm/dd/yy;@",
"[$-409]d\\-mmm;@",
"[$-409]d\\-mmm\\-yy;@",
"[$-409]dd\\-mmm\\-yy;@",
"[$-409]mmm\\-yy;@",
"[$-409]mmmm\\-yy;@",
"[$-409]mmmm\\ d\\,\\ yyyy;@",
"[$-409]mmm/d/yy\\ h:mm:ss;@",
"[$-409]mmmm/d/yy\\ h:mm:ss am;@",
"[$-409]mmmmm;@",
"[$-409]mmmmm\\-yy;@",
"mmmm/d/yyyy;@",
"[$-409]d\\-mmm\\-yyyy;@"
};
// valid number formats
String[] goodNumPatterns = new String[] {
"#,##0.0000",
"#,##0;[Red]#,##0",
"(#,##0.00_);(#,##0.00)",
"($#,##0.00_);[Red]($#,##0.00)",
"$#,##0.00",
"[$<24>-809]#,##0.00",
"[$<24>-2] #,##0.00",
"0000.00000%",
"0.000E+00",
"0.00E+00",
};
// valid number formats
String[] goodNumPatterns = {
"#,##0.0000",
"#,##0;[Red]#,##0",
"(#,##0.00_);(#,##0.00)",
"($#,##0.00_);[Red]($#,##0.00)",
"$#,##0.00",
"[$<24>-809]#,##0.00",
"[$<24>-2] #,##0.00",
"0000.00000%",
"0.000E+00",
"0.00E+00",
};
// invalid date formats -- will throw exception in DecimalFormat ctor
String[] badNumPatterns = new String[] {
"#,#$'#0.0000",
"'#','#ABC#0;##,##0",
"000 '123 4'5'6 000",
"#''0#0'1#10L16EE"
};
// invalid date formats -- will throw exception in DecimalFormat ctor
String[] badNumPatterns = {
"#,#$'#0.0000",
"'#','#ABC#0;##,##0",
"000 '123 4'5'6 000",
"#''0#0'1#10L16EE"
};
// create cells with good date patterns
for (int i = 0; i < goodDatePatterns.length; i++) {
HSSFCell cell = row.createCell((short) i);
cell.setCellValue(dateNum);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat(goodDatePatterns[i]));
cell.setCellStyle(cellStyle);
}
row = sheet.createRow(1);
// create cells with good date patterns
for (int i = 0; i < goodDatePatterns.length; i++) {
HSSFCell cell = row.createCell((short) i);
cell.setCellValue(dateNum);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat(goodDatePatterns[i]));
cell.setCellStyle(cellStyle);
}
row = sheet.createRow(1);
// create cells with num patterns
for (int i = 0; i < goodNumPatterns.length; i++) {
HSSFCell cell = row.createCell((short) i);
cell.setCellValue(-1234567890.12345);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat(goodNumPatterns[i]));
cell.setCellStyle(cellStyle);
}
row = sheet.createRow(2);
// create cells with num patterns
for (int i = 0; i < goodNumPatterns.length; i++) {
HSSFCell cell = row.createCell((short) i);
cell.setCellValue(-1234567890.12345);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat(goodNumPatterns[i]));
cell.setCellStyle(cellStyle);
}
row = sheet.createRow(2);
// create cells with bad num patterns
for (int i = 0; i < badNumPatterns.length; i++) {
HSSFCell cell = row.createCell((short) i);
cell.setCellValue(1234567890.12345);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat(badNumPatterns[i]));
cell.setCellStyle(cellStyle);
}
// create cells with bad num patterns
for (int i = 0; i < badNumPatterns.length; i++) {
HSSFCell cell = row.createCell((short) i);
cell.setCellValue(1234567890.12345);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat(badNumPatterns[i]));
cell.setCellStyle(cellStyle);
}
// Built in formats
// Built in formats
{ // Zip + 4 format
row = sheet.createRow(3);
HSSFCell cell = row.createCell((short) 0);
cell.setCellValue(123456789);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("00000-0000"));
cell.setCellStyle(cellStyle);
}
{ // Zip + 4 format
row = sheet.createRow(3);
HSSFCell cell = row.createCell((short) 0);
cell.setCellValue(123456789);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("00000-0000"));
cell.setCellStyle(cellStyle);
}
{ // Phone number format
row = sheet.createRow(4);
HSSFCell cell = row.createCell((short) 0);
cell.setCellValue(5551234567D);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("[<=9999999]###-####;(###) ###-####"));
cell.setCellStyle(cellStyle);
}
{ // Phone number format
row = sheet.createRow(4);
HSSFCell cell = row.createCell((short) 0);
cell.setCellValue(5551234567D);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("[<=9999999]###-####;(###) ###-####"));
cell.setCellStyle(cellStyle);
}
{ // SSN format
row = sheet.createRow(5);
HSSFCell cell = row.createCell((short) 0);
cell.setCellValue(444551234);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("000-00-0000"));
cell.setCellStyle(cellStyle);
}
{ // SSN format
row = sheet.createRow(5);
HSSFCell cell = row.createCell((short) 0);
cell.setCellValue(444551234);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("000-00-0000"));
cell.setCellStyle(cellStyle);
}
{ // formula cell
row = sheet.createRow(6);
HSSFCell cell = row.createCell((short) 0);
cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
cell.setCellFormula("SUM(12.25,12.25)/100");
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("##.00%;"));
cell.setCellStyle(cellStyle);
}
{ // formula cell
row = sheet.createRow(6);
HSSFCell cell = row.createCell((short) 0);
cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
cell.setCellFormula("SUM(12.25,12.25)/100");
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(format.getFormat("##.00%;"));
cell.setCellStyle(cellStyle);
}
}
/**
@ -169,10 +169,10 @@ public class TestHSSFDataFormatter extends TestCase {
// Valid date formats -- cell values should be date formatted & not "555.555"
HSSFRow row = wb.getSheetAt(0).getRow(0);
Iterator it = row.cellIterator();
System.out.println("==== VALID DATE FORMATS ====");
log("==== VALID DATE FORMATS ====");
while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next();
System.out.println(formatter.formatCellValue(cell));
log(formatter.formatCellValue(cell));
// should not be equal to "555.555"
assertTrue( ! "555.555".equals(formatter.formatCellValue(cell)));
@ -184,10 +184,10 @@ public class TestHSSFDataFormatter extends TestCase {
// test number formats
row = wb.getSheetAt(0).getRow(1);
it = row.cellIterator();
System.out.println("\n==== VALID NUMBER FORMATS ====");
log("\n==== VALID NUMBER FORMATS ====");
while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next();
System.out.println(formatter.formatCellValue(cell));
log(formatter.formatCellValue(cell));
// should not be equal to "1234567890.12345"
assertTrue( ! "1234567890.12345".equals(formatter.formatCellValue(cell)));
@ -196,10 +196,10 @@ public class TestHSSFDataFormatter extends TestCase {
// test bad number formats
row = wb.getSheetAt(0).getRow(2);
it = row.cellIterator();
System.out.println("\n==== INVALID NUMBER FORMATS ====");
log("\n==== INVALID NUMBER FORMATS ====");
while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next();
System.out.println(formatter.formatCellValue(cell));
log(formatter.formatCellValue(cell));
// should be equal to "1234567890.12345"
assertEquals("1234567890.12345", formatter.formatCellValue(cell));
}
@ -207,22 +207,22 @@ public class TestHSSFDataFormatter extends TestCase {
// test Zip+4 format
row = wb.getSheetAt(0).getRow(3);
HSSFCell cell = row.getCell(0);
System.out.println("\n==== ZIP FORMAT ====");
System.out.println(formatter.formatCellValue(cell));
log("\n==== ZIP FORMAT ====");
log(formatter.formatCellValue(cell));
assertEquals("12345-6789", formatter.formatCellValue(cell));
// test phone number format
row = wb.getSheetAt(0).getRow(4);
cell = row.getCell(0);
System.out.println("\n==== PHONE FORMAT ====");
System.out.println(formatter.formatCellValue(cell));
log("\n==== PHONE FORMAT ====");
log(formatter.formatCellValue(cell));
assertEquals("(555) 123-4567", formatter.formatCellValue(cell));
// test SSN format
row = wb.getSheetAt(0).getRow(5);
cell = row.getCell(0);
System.out.println("\n==== SSN FORMAT ====");
System.out.println(formatter.formatCellValue(cell));
log("\n==== SSN FORMAT ====");
log(formatter.formatCellValue(cell));
assertEquals("444-55-1234", formatter.formatCellValue(cell));
// null test-- null cell should result in empty String
@ -237,17 +237,17 @@ public class TestHSSFDataFormatter extends TestCase {
// test formula format
HSSFRow row = wb.getSheetAt(0).getRow(6);
HSSFCell cell = row.getCell(0);
System.out.println("\n==== FORMULA CELL ====");
log("\n==== FORMULA CELL ====");
// 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));
// now with a formula evaluator
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb.getSheetAt(0), wb);
//! must set current 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));
}
@ -263,20 +263,19 @@ public class TestHSSFDataFormatter extends TestCase {
Format defaultFormat = new DecimalFormat("Balance $#,#00.00 USD;Balance -$#,#00.00 USD");
formatter.setDefaultNumberFormat(defaultFormat);
double value = 10d;
System.out.println("\n==== DEFAULT NUMBER FORMAT ====");
log("\n==== DEFAULT NUMBER FORMAT ====");
while (it.hasNext()) {
HSSFCell cell = (HSSFCell) it.next();
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).endsWith(" USD"));
}
}
public static void main(String [] args) {
System.out
.println("Testing org.apache.poi.hssf.usermodel.TestHSSFDataFormatter");
junit.textui.TestRunner.run(TestHSSFDataFormatter.class);
}
private static void log(String msg) {
if (false) { // successful tests should be silent
System.out.println(msg);
}
}
}