diff --git a/src/documentation/xdocs/hssf/quick-guide.xml b/src/documentation/xdocs/hssf/quick-guide.xml index 5b7cafb15..3e16013fc 100644 --- a/src/documentation/xdocs/hssf/quick-guide.xml +++ b/src/documentation/xdocs/hssf/quick-guide.xml @@ -29,6 +29,7 @@
  • Working with fonts
  • Reading and writing
  • Use newlines in cells.
  • +
  • Create user defined data formats.
  • @@ -324,6 +325,38 @@ wb.write( fileOut ); fileOut.close();
    + +
    + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet("format sheet"); + HSSFCellStyle style; + HSSFDataFormat format = wb.createDataFormat(); + HSSFRow row; + HSSFCell cell; + short rowNum = 0; + short colNum = 0; + + row = sheet.createRow(rowNum++); + cell = row.createCell(colNum); + cell.setCellValue(11111.25); + style = wb.createCellStyle(); + style.setDataFormat(format.getFormat("0.0")); + cell.setCellStyle(style); + + row = sheet.createRow(rowNum++); + cell = row.createCell(colNum); + cell.setCellValue(11111.25); + style = wb.createCellStyle(); + style.setDataFormat(format.getFormat("#,##0.0000")); + cell.setCellStyle(style); + + FileOutputStream fileOut = new FileOutputStream("workbook.xls"); + wb.write(fileOut); + fileOut.close(); + +
    + diff --git a/src/documentation/xdocs/who.xml b/src/documentation/xdocs/who.xml index 524e124ee..f4f182608 100644 --- a/src/documentation/xdocs/who.xml +++ b/src/documentation/xdocs/who.xml @@ -50,7 +50,9 @@
    - +
    diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/BigExample.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/BigExample.java index d6bd14c6b..2513631c2 100644 --- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/BigExample.java +++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/BigExample.java @@ -109,7 +109,7 @@ public class BigExample //set cell stlye cs.setFont(f); //set the cell format see HSSFDataFromat for a full list - cs.setDataFormat(HSSFDataFormat.getFormat("($#,##0_);[Red]($#,##0)")); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("($#,##0_);[Red]($#,##0)")); //set a thin border cs2.setBorderBottom(cs2.BORDER_THIN); diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/CreateDateCells.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/CreateDateCells.java index c7b230a2b..073e8fcf6 100644 --- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/CreateDateCells.java +++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/CreateDateCells.java @@ -85,7 +85,7 @@ public class CreateDateCells // we style the second cell as a date (and time). It is important to create a new cell style from the workbook // otherwise you can end up modifying the built in style and effecting not only this cell but other cells. HSSFCellStyle cellStyle = wb.createCellStyle(); - cellStyle.setDataFormat(HSSFDataFormat.getFormat("m/d/yy h:mm")); + cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm")); cell = row.createCell((short)1); cell.setCellValue(new Date()); cell.setCellStyle(cellStyle); diff --git a/src/java/org/apache/poi/hssf/dev/HSSF.java b/src/java/org/apache/poi/hssf/dev/HSSF.java index 2cae1c9b8..1b3e6367c 100644 --- a/src/java/org/apache/poi/hssf/dev/HSSF.java +++ b/src/java/org/apache/poi/hssf/dev/HSSF.java @@ -145,7 +145,7 @@ public class HSSF f2.setColor(( short ) 0xf); f2.setBoldweight(f2.BOLDWEIGHT_BOLD); cs.setFont(f); - cs.setDataFormat(HSSFDataFormat.getFormat("($#,##0_);[Red]($#,##0)")); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("($#,##0_);[Red]($#,##0)")); cs2.setBorderBottom(cs2.BORDER_THIN); cs2.setFillPattern(( short ) 1); // fill w fg cs2.setFillForegroundColor(( short ) 0xA); diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index ad3a99cc7..2aa2f1c4b 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -84,6 +84,7 @@ import org.apache.poi.hssf.util.HSSFColor; * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf * before even attempting to use this. * + * @author Shawn Laubach (shawnlaubach at cox.net) (Data Formats) * @author Andrew C. Oliver (acoliver at apache dot org) * @author Glen Stampoultzis (glens at apache.org) * @author Sergei Kozello (sergeikozello at mail.ru) @@ -130,6 +131,8 @@ public class Workbook { protected ArrayList boundsheets = new ArrayList(); + protected ArrayList formats = new ArrayList(); + protected ArrayList names = new ArrayList(); protected int bspos = @@ -150,6 +153,8 @@ public class Workbook { 0; // holds the position of last name record private int supbookpos = 0; // holds the position of sup book + private short maxformatid = + -1; // holds the max format id private static POILogger log = POILogFactory.getLogger(Workbook.class); @@ -238,6 +243,11 @@ public class Workbook { log.log(DEBUG, "found SupBook record at " + k); retval.supbookpos = k; break; + case FormatRecord.sid : + log.log(DEBUG, "found format record at " + k); + retval.formats.add(rec); + retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode(); + break; default : } @@ -263,6 +273,7 @@ public class Workbook { log.log(DEBUG, "creating new workbook from scratch"); Workbook retval = new Workbook(); ArrayList records = new ArrayList(30); + ArrayList formats = new ArrayList(8); records.add(retval.createBOF()); records.add(retval.createInterfaceHdr()); @@ -293,14 +304,17 @@ public class Workbook { records.add(retval.createFont()); retval.fontpos = records.size() - 1; // last font record postion retval.numfonts = 4; - records.add(retval.createFormat(0)); - records.add(retval.createFormat(1)); - records.add(retval.createFormat(2)); - records.add(retval.createFormat(3)); - records.add(retval.createFormat(4)); - records.add(retval.createFormat(5)); - records.add(retval.createFormat(6)); - records.add(retval.createFormat(7)); + + // set up format records + for (int i = 0; i <= 7; i++) { + Record rec; + rec = retval.createFormat(i); + retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode(); + formats.add(rec); + records.add(rec); + } + retval.formats = formats; + for (int k = 0; k < 21; k++) { records.add(retval.createExtendedFormat(k)); retval.numxfs++; @@ -1796,6 +1810,51 @@ public class Workbook { return rec; } + /** + * Returns a format index that matches the passed in format. It does not tie into HSSFDataFormat. + * @param format the format string + * @param createIfNotFound creates a new format if format not found + * @return the format id of a format that matches or -1 if none found and createIfNotFound + */ + public short getFormat(String format, boolean createIfNotFound) { + Iterator iterator; + for (iterator = formats.iterator(); iterator.hasNext();) { + FormatRecord r = (FormatRecord)iterator.next(); + if (r.getFormatString().equals(format)) { + return r.getIndexCode(); + } + } + + if (createIfNotFound) { + return createFormat(format); + } + + return -1; + } + + /** + * Creates a FormatRecord, inserts it, and returns the index code. + * @param format the format string + * @return the index code of the format record. + * @see org.apache.poi.hssf.record.FormatRecord + * @see org.apache.poi.hssf.record.Record + */ + public short createFormat(String format) { + FormatRecord rec = new FormatRecord(); + maxformatid = maxformatid >= (short)0xa4 ? (short)(maxformatid + 1) : (short)0xa4; //Starting value from M$ empiracle study. + rec.setIndexCode(maxformatid); + rec.setFormatStringLength((byte)format.length()); + rec.setFormatString(format); + + int pos = 0; + while (pos < records.size() && ((Record)records.get(pos)).getSid() != FormatRecord.sid) + pos++; + pos += formats.size(); + formats.add(rec); + records.add(pos, rec); + return maxformatid; + } + /** * Returns the first occurance of a record matching a particular sid. @@ -1804,6 +1863,22 @@ public class Workbook { public Record findFirstRecordBySid(short sid) { for (Iterator iterator = records.iterator(); iterator.hasNext(); ) { Record record = ( Record ) iterator.next(); + + if (record.getSid() == sid) { + return record; + } + } + return null; + } + + /** + * Returns the next occurance of a record matching a particular sid. + */ + public Record findNextRecordBySid(short sid, int pos) { + Iterator iterator = records.iterator(); + for (;pos > 0 && iterator.hasNext(); iterator.next(),pos--); + while (iterator.hasNext()) { + Record record = ( Record ) iterator.next(); if (record.getSid() == sid) { return record; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormat.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormat.java index f1d75fe99..6cdb2cfa8 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormat.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormat.java @@ -1,4 +1,3 @@ - /* ==================================================================== * The Apache Software License, Version 1.1 * @@ -60,11 +59,16 @@ */ package org.apache.poi.hssf.usermodel; -import java.util.ArrayList; +import java.util.Vector; import java.util.List; +import java.util.ListIterator; + +import org.apache.poi.hssf.model.Workbook; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.FormatRecord; /** - * Utility to identify builin formats. The following is a list of the formats as + * Utility to identify builin formats. Now can handle user defined data formats also. The following is a list of the formats as * returned by this class.

    *

    * 0, "General"

    @@ -112,75 +116,92 @@ import java.util.List; public class HSSFDataFormat { - private static ArrayList formats; + private static Vector builtinFormats; - private static synchronized void populateFormats() - { - formats = new ArrayList(); - formats.add(0, "General"); - formats.add(1, "0"); - formats.add(2, "0.00"); - formats.add(3, "#,##0"); - formats.add(4, "#,##0.00"); - formats.add(5, "($#,##0_);($#,##0)"); - formats.add(6, "($#,##0_);[Red]($#,##0)"); - formats.add(7, "($#,##0.00);($#,##0.00)"); - formats.add(8, "($#,##0.00_);[Red]($#,##0.00)"); - formats.add(9, "0%"); - formats.add(0xa, "0.00%"); - formats.add(0xb, "0.00E+00"); - formats.add(0xc, "# ?/?"); - formats.add(0xd, "# ??/??"); - formats.add(0xe, "m/d/yy"); - formats.add(0xf, "d-mmm-yy"); - formats.add(0x10, "d-mmm"); - formats.add(0x11, "mmm-yy"); - formats.add(0x12, "h:mm AM/PM"); - formats.add(0x13, "h:mm:ss AM/PM"); - formats.add(0x14, "h:mm"); - formats.add(0x15, "h:mm:ss"); - formats.add(0x16, "m/d/yy h:mm"); + private Vector formats = new Vector(); + private Workbook workbook; + private boolean movedBuiltins = false; // Flag to see if need to + // check the built in list + // or if the regular list + // has all entries. - // 0x17 - 0x24 reserved for international and undocumented - formats.add(0x17, "0x17"); - formats.add(0x18, "0x18"); - formats.add(0x19, "0x19"); - formats.add(0x1a, "0x1a"); - formats.add(0x1b, "0x1b"); - formats.add(0x1c, "0x1c"); - formats.add(0x1d, "0x1d"); - formats.add(0x1e, "0x1e"); - formats.add(0x1f, "0x1f"); - formats.add(0x20, "0x20"); - formats.add(0x21, "0x21"); - formats.add(0x22, "0x22"); - formats.add(0x23, "0x23"); - formats.add(0x24, "0x24"); - - // 0x17 - 0x24 reserved for international and undocumented - formats.add(0x25, "(#,##0_);(#,##0)"); - formats.add(0x26, "(#,##0_);[Red](#,##0)"); - formats.add(0x27, "(#,##0.00_);(#,##0.00)"); - formats.add(0x28, "(#,##0.00_);[Red](#,##0.00)"); - formats.add(0x29, "_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)"); - formats.add(0x2a, "_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)"); - formats.add(0x2b, "_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)"); - formats.add(0x2c, - "_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)"); - formats.add(0x2d, "mm:ss"); - formats.add(0x2e, "[h]:mm:ss"); - formats.add(0x2f, "mm:ss.0"); - formats.add(0x30, "##0.0E+0"); - formats.add(0x31, "@"); + /** + * Construncts a new data formatter. It takes a workbook to have + * access to the workbooks format records. + * @param workbook the workbook the formats are tied to. + */ + public HSSFDataFormat(Workbook workbook) { + this.workbook = workbook; + if (builtinFormats == null) populateBuiltinFormats(); } - public static List getFormats() + private static synchronized void populateBuiltinFormats() { - if (formats == null) + builtinFormats = new Vector(); + builtinFormats.add(0, "General"); + builtinFormats.add(1, "0"); + builtinFormats.add(2, "0.00"); + builtinFormats.add(3, "#,##0"); + builtinFormats.add(4, "#,##0.00"); + builtinFormats.add(5, "($#,##0_);($#,##0)"); + builtinFormats.add(6, "($#,##0_);[Red]($#,##0)"); + builtinFormats.add(7, "($#,##0.00);($#,##0.00)"); + builtinFormats.add(8, "($#,##0.00_);[Red]($#,##0.00)"); + builtinFormats.add(9, "0%"); + builtinFormats.add(0xa, "0.00%"); + builtinFormats.add(0xb, "0.00E+00"); + builtinFormats.add(0xc, "# ?/?"); + builtinFormats.add(0xd, "# ??/??"); + builtinFormats.add(0xe, "m/d/yy"); + builtinFormats.add(0xf, "d-mmm-yy"); + builtinFormats.add(0x10, "d-mmm"); + builtinFormats.add(0x11, "mmm-yy"); + builtinFormats.add(0x12, "h:mm AM/PM"); + builtinFormats.add(0x13, "h:mm:ss AM/PM"); + builtinFormats.add(0x14, "h:mm"); + builtinFormats.add(0x15, "h:mm:ss"); + builtinFormats.add(0x16, "m/d/yy h:mm"); + + // 0x17 - 0x24 reserved for international and undocumented + builtinFormats.add(0x17, "0x17"); + builtinFormats.add(0x18, "0x18"); + builtinFormats.add(0x19, "0x19"); + builtinFormats.add(0x1a, "0x1a"); + builtinFormats.add(0x1b, "0x1b"); + builtinFormats.add(0x1c, "0x1c"); + builtinFormats.add(0x1d, "0x1d"); + builtinFormats.add(0x1e, "0x1e"); + builtinFormats.add(0x1f, "0x1f"); + builtinFormats.add(0x20, "0x20"); + builtinFormats.add(0x21, "0x21"); + builtinFormats.add(0x22, "0x22"); + builtinFormats.add(0x23, "0x23"); + builtinFormats.add(0x24, "0x24"); + + // 0x17 - 0x24 reserved for international and undocumented + builtinFormats.add(0x25, "(#,##0_);(#,##0)"); + builtinFormats.add(0x26, "(#,##0_);[Red](#,##0)"); + builtinFormats.add(0x27, "(#,##0.00_);(#,##0.00)"); + builtinFormats.add(0x28, "(#,##0.00_);[Red](#,##0.00)"); + builtinFormats.add(0x29, "_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)"); + builtinFormats.add(0x2a, "_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)"); + builtinFormats.add(0x2b, "_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)"); + builtinFormats.add(0x2c, + "_($*#,##0.00_);_($*(#,##0.00);_($*\"-\"??_);_(@_)"); + builtinFormats.add(0x2d, "mm:ss"); + builtinFormats.add(0x2e, "[h]:mm:ss"); + builtinFormats.add(0x2f, "mm:ss.0"); + builtinFormats.add(0x30, "##0.0E+0"); + builtinFormats.add(0x31, "@"); + } + + public static List getBuiltinFormats() + { + if (builtinFormats == null) { - populateFormats(); + populateBuiltinFormats(); } - return formats; + return builtinFormats; } /** @@ -189,17 +210,17 @@ public class HSSFDataFormat * @return index of format or -1 if undefined. */ - public static short getFormat(String format) + public static short getBuiltinFormat(String format) { - if (formats == null) + if (builtinFormats == null) { - populateFormats(); + populateBuiltinFormats(); } short retval = -1; for (short k = 0; k < 0x31; k++) { - String nformat = ( String ) formats.get(k); + String nformat = ( String ) builtinFormats.get(k); if ((nformat != null) && nformat.equals(format)) { @@ -210,32 +231,80 @@ public class HSSFDataFormat return retval; } + /** + * get the format index that matches the given format string. + * Creates a new format if one is not found. + * @param format string matching a built in format + * @return index of format. + */ + + public short getFormat(String format) + { + ListIterator i; + int ind; + if (!movedBuiltins) { + i = builtinFormats.listIterator(); + while (i.hasNext()) { + ind = i.nextIndex(); + formats.add(ind, i.next()); + } + movedBuiltins = true; + } + i = formats.listIterator(); + while (i.hasNext()) { + ind = i.nextIndex(); + if (format.equals(i.next())) + return (short)ind; + } + + ind = workbook.getFormat(format, true); + if (formats.size() <= ind) + formats.setSize(ind + 1); + formats.add(ind, format); + + return (short)ind; + } + + /** + * get the format string that matches the given format index + * @param index of a format + * @return string represented at index of format or null if there is not a format at that index + */ + + public String getFormat(short index) + { + if (movedBuiltins) + return ( String ) formats.get(index); + else + return (String) (builtinFormats.get(index) != null ? builtinFormats.get(index) : formats.get(index)); + } + /** * get the format string that matches the given format index * @param index of a built in format * @return string represented at index of format or null if there is not a builtin format at that index */ - public static String getFormat(short index) + public static String getBuiltinFormat(short index) { - if (formats == null) + if (builtinFormats == null) { - populateFormats(); + populateBuiltinFormats(); } - return ( String ) formats.get(index); + return ( String ) builtinFormats.get(index); } /** - * get the number of builtin and reserved formats - * @return number of builtin and reserved formats + * get the number of builtin and reserved builtinFormats + * @return number of builtin and reserved builtinFormats */ - public static int getNumberOfBuiltinFormats() + public static int getNumberOfBuiltinBuiltinFormats() { - if (formats == null) + if (builtinFormats == null) { - populateFormats(); + populateBuiltinFormats(); } - return formats.size(); + return builtinFormats.size(); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 46fad9ae1..3c9df3c27 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -82,6 +82,7 @@ import java.util.List; * @see org.apache.poi.hssf.usermodel.HSSFSheet * @author Andrew C. Oliver (acoliver at apache dot org) * @author Glen Stampoultzis (glens at apache.org) + * @author Shawn Laubach (shawnlaubach at cox.net) * @version 2.0-pre */ @@ -615,7 +616,17 @@ public class HSSFWorkbook names.remove(index); workbook.removeName(index); } - + + /** + * Creates an instance of HSSFDataFormat. + * @return the HSSFDataFormat object + * @see org.apache.poi.hssf.record.FormatRecord + * @see org.apache.poi.hssf.record.Record + */ + public HSSFDataFormat createDataFormat() { + return new HSSFDataFormat(workbook); + } + /** remove the named range by his name * @param name named range name */ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java b/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java index 4b55b820c..af05f57b9 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestCellStyle.java @@ -152,13 +152,13 @@ public class TestCellStyle // with Date: HSSFCell cell = row.createCell((short)1); - cs.setDataFormat(HSSFDataFormat.getFormat("m/d/yy")); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy")); cell.setCellStyle(cs); cell.setCellValue(new Date()); // with Calendar: cell = row.createCell((short)2); - cs.setDataFormat(HSSFDataFormat.getFormat("m/d/yy")); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy")); cell.setCellStyle(cs); Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java index 1fdfb75e8..0080d58e9 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java @@ -233,6 +233,35 @@ public class TestWorkbook stream.close(); } + /** + * TEST NAME: Test Read Simple w/ Data Format

    + * OBJECTIVE: Test that HSSF can read a simple spreadsheet (SimpleWithDataFormat.xls).

    + * SUCCESS: HSSF reads the sheet. Matches values in their particular positions and format is correct

    + * FAILURE: HSSF does not read a sheet or excepts. HSSF cannot identify values + * in the sheet in their known positions.

    + * + */ + + public void testReadSimpleWithDataFormat() + throws IOException + { + String filename = System.getProperty("HSSF.testdata.path"); + + filename = filename + "/SimpleWithDataFormat.xls"; + FileInputStream stream = new FileInputStream(filename); + POIFSFileSystem fs = new POIFSFileSystem(stream); + HSSFWorkbook workbook = new HSSFWorkbook(fs); + HSSFSheet sheet = workbook.getSheetAt(0); + HSSFDataFormat format = workbook.createDataFormat(); + + assertEquals(1.25, + sheet.getRow(( short ) 0).getCell(( short ) 0) + .getNumericCellValue(), 1e-10); + + assertEquals(format.getFormat("0.0"), 0xa4); + stream.close(); + } + /** * TEST NAME: Test Read Employee Simple

    * OBJECTIVE: Test that HSSF can read a simple spreadsheet (Employee.xls).