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).
- 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 colours48574 - further XWPF support for tables, paragraphs, including enhanced support for adding new ones48245 - tweak HWPF table cell detection to work across more files48996 - 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 files48343 - added implementation of SUBTOTAL function
+ Switch to compiling the OOXML Schemas for Java 1.548332 - 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