diff --git a/build.xml b/build.xml index 802ae68b9c..18984f8cab 100644 --- a/build.xml +++ b/build.xml @@ -66,6 +66,9 @@ under the License. + + + + srcdir="${main.output.dir}" + encoding="${java.source.encoding}"> + diff --git a/src/documentation/content/xdocs/faq.xml b/src/documentation/content/xdocs/faq.xml index 829caa5f27..8afe510b78 100644 --- a/src/documentation/content/xdocs/faq.xml +++ b/src/documentation/content/xdocs/faq.xml @@ -63,7 +63,7 @@ System.out.println("Core POI came from " + path); org.openxmlformats.schemas namespace.

There are two jar files available, as described in the components overview section. - The full jar of all of the schemas is ooxml-schemas-1.0.jar, + The full jar of all of the schemas is ooxml-schemas-1.1.jar, and it is currently around 15mb. The smaller poi-ooxml-schemas jar is only about 4mb. This latter jar file only contains the typically used parts though.

@@ -72,11 +72,11 @@ System.out.println("Core POI came from " + path); classes that are typically used, as identified by the unit tests. Every so often, you may try to use part of the file format which isn't included in the minimal poi-ooxml-schemas jar. In this case, - you should switch to the full ooxml-schemas-1.0.jar. Longer term, + you should switch to the full ooxml-schemas-1.1.jar. Longer term, you may also wish to submit a new unit test which uses the extra parts of the XSDs, so that a future poi-ooxml-schemas jar will include them.

-

There are a number of ways to get the full ooxml-schemas-1.0.jar. +

There are a number of ways to get the full ooxml-schemas-1.1.jar. If you are a maven user, see the the components overview section for the artifact details to have maven download it for you. @@ -88,6 +88,12 @@ System.out.println("Core POI came from " + path); look at this). Finally, you can download the jar by hand from the POI Maven Repository.

+

Note that for POI 3.5 and 3.6, the full ooxml schemas jar was + named ooxml-schemas-1.0.jar. For POI 3.7, the filename was bumped + to ooxml-schemas-1.1.jar when generics support was added. You can + use ooxml-schemas-1.1.jar with POI 3.5 and 3.6 if you wish, but + POI 3.7 won't wokr with ooxml-schemas-1.0.jar (it needs thew newer + one).

diff --git a/src/documentation/content/xdocs/overview.xml b/src/documentation/content/xdocs/overview.xml index bb8e0d2aee..35ae35f380 100644 --- a/src/documentation/content/xdocs/overview.xml +++ b/src/documentation/content/xdocs/overview.xml @@ -247,15 +247,17 @@ ooxml-schemas xmlbeans - ooxml-schemas-1.0.jar + ooxml-schemas-1.1.jar

(*) starting with 3.6-beta1-20091124.

- poi-ooxml requires poi-ooxml-schemas. This is a substantially smaller version of the ooxml-schemas-1.0.jar. - The larger ooxml-schemas jar is only required for development. + poi-ooxml requires poi-ooxml-schemas. This is a substantially smaller + version of the ooxml-schemas jar (ooxml-schemas-1.1.jar for POI 3.7 or later, ooxml-scheams-1.0.jar for POI 3.5 and 3.6). + The larger ooxml-schemas jar is normally + only required for development

Examples diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 16ca0807fc..d7096d8cdc 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -35,7 +35,10 @@ - + + 49432 - Lazy caching of XSSFComment CTComment objects by reference, to make repeated comment searching faster + Better handling of Outlook messages in HSMF when there's no recipient email address + When formatting numbers with DataFormatter, handle brackets following colours 48574 - further XWPF support for tables, paragraphs, including enhanced support for adding new ones 48245 - tweak HWPF table cell detection to work across more files 48996 - initial support for External Name References in HSSF formula evaluation @@ -116,6 +119,7 @@ 48339 - fixed ExternalNameRecord to properly distinguish DDE data from OLE data items 47920 - allow editing workbooks embedded into PowerPoint files 48343 - added implementation of SUBTOTAL function + Switch to compiling the OOXML Schemas for Java 1.5 48332 - fixed XSSFSheet autoSizeColumn() to tolerate empty RichTextString diff --git a/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java b/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java index 14bb16887d..1ba9b6441c 100644 --- a/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java +++ b/src/java/org/apache/poi/hpsf/extractor/HPSFPropertiesExtractor.java @@ -17,6 +17,8 @@ package org.apache.poi.hpsf.extractor; +import java.io.FileInputStream; +import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; @@ -150,4 +152,13 @@ public class HPSFPropertiesExtractor extends POITextExtractor { throw new IllegalStateException("Unable to write, only for properties!"); } } + + public static void main(String[] args) throws IOException { + for(String file : args) { + HPSFPropertiesExtractor ext = new HPSFPropertiesExtractor( + new POIFSFileSystem(new FileInputStream(file)) + ); + System.out.println(ext.getText()); + } + } } diff --git a/src/java/org/apache/poi/ss/usermodel/DataFormatter.java b/src/java/org/apache/poi/ss/usermodel/DataFormatter.java index 36ba5a1b6b..de586db66a 100644 --- a/src/java/org/apache/poi/ss/usermodel/DataFormatter.java +++ b/src/java/org/apache/poi/ss/usermodel/DataFormatter.java @@ -233,14 +233,14 @@ public class DataFormatter { int at = formatStr.indexOf(colour); if(at == -1) break; String nFormatStr = formatStr.substring(0,at) + - formatStr.substring(at+colour.length()+1); + formatStr.substring(at+colour.length()); if(nFormatStr.equals(formatStr)) break; // Try again in case there's multiple formatStr = nFormatStr; colourM = colorPattern.matcher(formatStr); } - + // try to extract special characters like currency Matcher m = specialPatternGroup.matcher(formatStr); while(m.find()) { diff --git a/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java b/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java index da47d90a2c..3e35db871e 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java @@ -19,20 +19,27 @@ package org.apache.poi.xssf.model; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; -import org.apache.poi.ss.util.CellReference; -import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CommentsDocument; -import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.openxml4j.opc.PackageRelationship; public class CommentsTable extends POIXMLDocumentPart { private CTComments comments; + /** + * XML Beans uses a list, which is very slow + * to search, so we wrap things with our own + * map for fast lookup. + */ + private Map commentRefs; public CommentsTable() { super(); @@ -67,6 +74,17 @@ public class CommentsTable extends POIXMLDocumentPart { writeTo(out); out.close(); } + + /** + * Called after the reference is updated, so that + * we can reflect that in our cache + */ + public void referenceUpdated(String oldReference, CTComment comment) { + if(commentRefs != null) { + commentRefs.remove(oldReference); + commentRefs.put(comment.getRef(), comment); + } + } public int getNumberOfComments() { return comments.getCommentList().sizeOfCommentArray(); @@ -95,18 +113,26 @@ public class CommentsTable extends POIXMLDocumentPart { } public CTComment getCTComment(String cellRef) { - for (CTComment comment : comments.getCommentList().getCommentArray()) { - if (cellRef.equals(comment.getRef())) { - return comment; - } + // Create the cache if needed + if(commentRefs == null) { + commentRefs = new HashMap(); + for (CTComment comment : comments.getCommentList().getCommentArray()) { + commentRefs.put(comment.getRef(), comment); + } } - return null; + + // Return the comment, or null if not known + return commentRefs.get(cellRef); } public CTComment newComment() { CTComment ct = comments.getCommentList().addNewComment(); ct.setRef("A1"); ct.setAuthorId(0); + + if(commentRefs != null) { + commentRefs.put(ct.getRef(), ct); + } return ct; } @@ -116,6 +142,10 @@ public class CommentsTable extends POIXMLDocumentPart { CTComment comment = lst.getCommentArray(i); if (cellRef.equals(comment.getRef())) { lst.removeComment(i); + + if(commentRefs != null) { + commentRefs.remove(cellRef); + } return true; } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java index a7e577d190..9d6c34cd47 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFComment.java @@ -110,8 +110,12 @@ public class XSSFComment implements Comment { * @param col the 0-based column of the cell that contains the comment */ public void setColumn(int col) { + String oldRef = _comment.getRef(); + CellReference ref = new CellReference(getRow(), col); - _comment.setRef(ref.formatAsString()); + _comment.setRef(ref.formatAsString()); + _comments.referenceUpdated(oldRef, _comment); + if(_vmlShape != null) _vmlShape.getClientDataArray(0).setColumnArray(0, new BigInteger(String.valueOf(col))); } @@ -121,9 +125,13 @@ public class XSSFComment implements Comment { * @param row the 0-based row of the cell that contains the comment */ public void setRow(int row) { + String oldRef = _comment.getRef(); + String newRef = (new CellReference(row, getColumn())).formatAsString(); _comment.setRef(newRef); + _comments.referenceUpdated(oldRef, _comment); + if(_vmlShape != null) _vmlShape.getClientDataArray(0).setRowArray(0, new BigInteger(String.valueOf(row))); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 34db7c403e..6c13857f9c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -175,9 +175,14 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { initRows(worksheet); columnHelper = new ColumnHelper(worksheet); + // Look for bits we're interested in for(POIXMLDocumentPart p : getRelations()){ - if(p instanceof CommentsTable) sheetComments = (CommentsTable)p; + if(p instanceof CommentsTable) { + sheetComments = (CommentsTable)p; + break; + } } + // Process external hyperlinks for the sheet, if there are any initHyperlinks(); } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java index 6aabadef95..e771424170 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java @@ -93,8 +93,8 @@ public class XWPFParagraph implements IBodyElement{ } runs = new ArrayList(); - if (prgrph.getRList().size() > 0) { - for(CTR ctRun : prgrph.getRList()) { + if (prgrph.getRArray().length > 0) { + for(CTR ctRun : prgrph.getRArray()) { runs.add(new XWPFRun(ctRun, this)); } } @@ -111,17 +111,17 @@ public class XWPFParagraph implements IBodyElement{ // TODO - replace this with some sort of XPath expression // to directly find all the CTRs, in the right order ArrayList rs = new ArrayList(); - rs.addAll( paragraph.getRList() ); + rs.addAll( Arrays.asList(paragraph.getRArray()) ); - for (CTSdtRun sdt : paragraph.getSdtList()) { + for (CTSdtRun sdt : paragraph.getSdtArray()) { CTSdtContentRun run = sdt.getSdtContent(); - rs.addAll( run.getRList() ); + rs.addAll( Arrays.asList(run.getRArray()) ); } - for (CTRunTrackChange c : paragraph.getDelList()) { - rs.addAll( c.getRList() ); + for (CTRunTrackChange c : paragraph.getDelArray()) { + rs.addAll( Arrays.asList(c.getRArray()) ); } - for (CTRunTrackChange c : paragraph.getInsList()) { - rs.addAll( c.getRList() ); + for (CTRunTrackChange c : paragraph.getInsArray()) { + rs.addAll( Arrays.asList(c.getRArray()) ); } // Get text of the paragraph @@ -179,7 +179,7 @@ public class XWPFParagraph implements IBodyElement{ // Loop over pictures inside our // paragraph, looking for text in them - for(CTPicture pict : rs.get(j).getPictList()) { + for(CTPicture pict : rs.get(j).getPictArray()) { XmlObject[] t = pict .selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//w:t"); for (int m = 0; m < t.length; m++) { diff --git a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java index 8d84b1627a..e04d299b13 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java @@ -239,7 +239,11 @@ public class MAPIMessage extends POIDocument { if(email != null) { emails[i] = email; } else { - throw new ChunkNotFoundException("No email address holding chunks found for the " + (i+1) + "th recipient"); + if(returnNullOnMissingChunk) { + emails[i] = null; + } else { + throw new ChunkNotFoundException("No email address holding chunks found for the " + (i+1) + "th recipient"); + } } } @@ -393,6 +397,7 @@ public class MAPIMessage extends POIDocument { boolean first = true; for(String s : l) { + if(s == null) continue; if(first) { first = false; } else { diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/TestBasics.java b/src/scratchpad/testcases/org/apache/poi/hsmf/TestBasics.java index 6e958c193f..18a316491f 100644 --- a/src/scratchpad/testcases/org/apache/poi/hsmf/TestBasics.java +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/TestBasics.java @@ -33,6 +33,7 @@ public final class TestBasics extends TestCase { private MAPIMessage quick; private MAPIMessage outlook30; private MAPIMessage attachments; + private MAPIMessage noRecipientAddress; /** * Initialize this test, load up the blank.msg mapi message. @@ -44,6 +45,7 @@ public final class TestBasics extends TestCase { quick = new MAPIMessage(samples.openResourceAsStream("quick.msg")); outlook30 = new MAPIMessage(samples.openResourceAsStream("outlook_30_msg.msg")); attachments = new MAPIMessage(samples.openResourceAsStream("attachment_test_msg.msg")); + noRecipientAddress = new MAPIMessage(samples.openResourceAsStream("no_recipient_address.msg")); } /** @@ -140,4 +142,39 @@ public final class TestBasics extends TestCase { // Good } } + + /** + * More missing chunk testing, this time for + * missing recipient email address + */ + public void testMissingAddressChunk() throws Exception { + assertEquals(false, noRecipientAddress.isReturnNullOnMissingChunk()); + + try { + noRecipientAddress.getRecipientEmailAddress(); + fail(); + } catch(ChunkNotFoundException e) { + // Good + } + try { + noRecipientAddress.getRecipientEmailAddressList(); + fail(); + } catch(ChunkNotFoundException e) { + // Good + } + + noRecipientAddress.setReturnNullOnMissingChunk(true); + + noRecipientAddress.getRecipientEmailAddress(); + noRecipientAddress.getRecipientEmailAddressList(); + assertEquals("", noRecipientAddress.getRecipientEmailAddress()); + assertEquals(1, noRecipientAddress.getRecipientEmailAddressList().length); + assertEquals(null, noRecipientAddress.getRecipientEmailAddressList()[0]); + + // Check a few other bits too + assertEquals("Microsoft Outlook 2003 Team", noRecipientAddress.getDisplayFrom()); + assertEquals("New Outlook User", noRecipientAddress.getDisplayTo()); + + noRecipientAddress.setReturnNullOnMissingChunk(false); + } } diff --git a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java index ed2da48bd4..00b3afad8f 100644 --- a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java +++ b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java @@ -251,7 +251,7 @@ public final class TestExcelExtractor extends TestCase { ); assertTrue( text.indexOf( - "£nn.nn\t£10.52\n" + "\u00a3nn.nn\t\u00a310.52\n" ) > -1 ); } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java index e5f1f8129c..4759a6c04b 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java @@ -405,9 +405,9 @@ public final class TestHSSFDataFormatter extends TestCase { assertEquals("10.5", f.formatCellValue(sheet.getRow(11).getCell(1))); // text isn't quite the format rule... - assertEquals("£nn.nn", sheet.getRow(12).getCell(0).getStringCellValue()); - assertEquals("\"£\"#,##0.00", sheet.getRow(12).getCell(1).getCellStyle().getDataFormatString()); - assertEquals("£10.52", f.formatCellValue(sheet.getRow(12).getCell(1))); + assertEquals("\u00a3nn.nn", sheet.getRow(12).getCell(0).getStringCellValue()); + assertEquals("\"\u00a3\"#,##0.00", sheet.getRow(12).getCell(1).getCellStyle().getDataFormatString()); + assertEquals("\u00a310.52", f.formatCellValue(sheet.getRow(12).getCell(1))); } private static void log(String msg) { diff --git a/src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java b/src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java index 35be8c4ff1..06abd6c502 100644 --- a/src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java +++ b/src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java @@ -77,6 +77,44 @@ public class TestDataFormatter extends TestCase { assertEquals("[ab]12.34[x]", dfUS.formatRawCellContents(12.343, -1, "[ab]##.##[x]")); } + public void testColoursAndBrackets() { + DataFormatter dfUS = new DataFormatter(Locale.US); + + // Without currency symbols + String[] formats = new String[] { + "#,##0.00;[Blue](#,##0.00)", + }; + for(String format : formats) { + assertEquals( + "Wrong format for: " + format, + "12.34", + dfUS.formatRawCellContents(12.343, -1, format) + ); + assertEquals( + "Wrong format for: " + format, + "(12.34)", + dfUS.formatRawCellContents(-12.343, -1, format) + ); + } + + // With + formats = new String[] { + "$#,##0.00;[Red]($#,##0.00)" + }; + for(String format : formats) { + assertEquals( + "Wrong format for: " + format, + "$12.34", + dfUS.formatRawCellContents(12.343, -1, format) + ); + assertEquals( + "Wrong format for: " + format, + "($12.34)", + dfUS.formatRawCellContents(-12.343, -1, format) + ); + } + } + /** * Test how we handle negative and zeros. * Note - some tests are disabled as DecimalFormat diff --git a/test-data/hsmf/no_recipient_address.msg b/test-data/hsmf/no_recipient_address.msg new file mode 100644 index 0000000000..e88d503fe8 Binary files /dev/null and b/test-data/hsmf/no_recipient_address.msg differ