diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml
index df0e5e51a..34345df5d 100644
--- a/src/documentation/content/xdocs/changes.xml
+++ b/src/documentation/content/xdocs/changes.xml
@@ -36,6 +36,11 @@
+ 44375 - Cope with a broken dictionary in Document Summary Information stream. RuntimeExceptions that occured when trying to read bogus data are now caught. Dictionary entries up to but not including the bogus one are preserved, the rest is ignored.
+ 38641 - Handle timezones better with cell.setCellValue(Calendar), so now 20:00-03:00, 20:00+00:00 and 20:00+03:00 will all be recorded as 20:00, and not 17:00 / 20:00 / 23:00 (pass a Date not a Calendar for old behaviour)
+ 44373 - Have HSSFDateUtil.isADateFormat recognize more formats as being dates
+ 37923 - Support for Excel hyperlinks
+ Implement hashCode() and equals(obj) on HSSFFont and HSSFCellStyle44345 - Implement CountA, CountIf, Index, Rows and Columns functions44336 - Properly escape sheet names as required when figuring out the text of formulas44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions
diff --git a/src/documentation/content/xdocs/getinvolved/index.xml b/src/documentation/content/xdocs/getinvolved/index.xml
index 1c182a508..71ba94134 100644
--- a/src/documentation/content/xdocs/getinvolved/index.xml
+++ b/src/documentation/content/xdocs/getinvolved/index.xml
@@ -96,7 +96,8 @@
Create patches by getting the latest sources from Subversion.
Alter or add files as appropriate. Then, from the poi directiory,
type svn diff > mypatch.patch. This will capture all of your changes
- in a patch file of the appropriate format. Next, if you've added any
+ in a patch file of the appropriate format. However, svn diff won't
+ capture any new files you may have added. So, if you've added any
files, create an archive (tar.bz2 preferred as its the smallest) in a
path-preserving archive format, relative to your poi directory.
You'll attach both files in the next step.
@@ -117,6 +118,33 @@
Standards). Patches that are of low quality may be rejected or
the contributer may be asked to bring them up to spec.
+
If you use a unix shell, you may find the following following
+ sequence of commands useful for building the files to attach.
+
- POI 3.0.2 BETA2 Release
-
The latest release of Apache POI is 3.0.2 BETA2 which was promoted to "Beta" on 12 January 2008. It contains a mixture of
- new features and bug fixes, compared to 3.0.1. A full list of changes
- is available in
- the changelog, and
- download
+ February 06 2008 - POI 3.0.2 Released
+
The POI team is pleased to announce POI 3.0.2, the latest release of Apache POI.
+ There have been many important bug fixes since the 3.0.1 release and a lot of new features. A full list of changes is available in
+ the changelog, and
+ download
the source and binaries from your
- local mirror.
- The release is also available from the central Maven repository under Group ID "org.apache.poi".
+ local mirror.
+
+ The release is also available from the central Maven repository under Group ID "org.apache.poi" and Version "3.0.2-FINAL".
+
POI 3.0.1 Release
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 9a7a3f43c..91641b13b 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -33,6 +33,11 @@
+ 44375 - Cope with a broken dictionary in Document Summary Information stream. RuntimeExceptions that occured when trying to read bogus data are now caught. Dictionary entries up to but not including the bogus one are preserved, the rest is ignored.
+ 38641 - Handle timezones better with cell.setCellValue(Calendar), so now 20:00-03:00, 20:00+00:00 and 20:00+03:00 will all be recorded as 20:00, and not 17:00 / 20:00 / 23:00 (pass a Date not a Calendar for old behaviour)
+ 44373 - Have HSSFDateUtil.isADateFormat recognize more formats as being dates
+ 37923 - Support for Excel hyperlinks
+ Implement hashCode() and equals(obj) on HSSFFont and HSSFCellStyle44345 - Implement CountA, CountIf, Index, Rows and Columns functions44336 - Properly escape sheet names as required when figuring out the text of formulas44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java
new file mode 100755
index 000000000..24b3f186f
--- /dev/null
+++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java
@@ -0,0 +1,91 @@
+
+/* ====================================================================
+ 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.hssf.usermodel.examples;
+
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.hssf.util.HSSFColor;
+
+import java.io.IOException;
+import java.io.FileOutputStream;
+
+/**
+ * Demonstrates how to create hyperlinks.
+ *
+ * @author Yegor Kozlov (yegor at apach.org)
+ */
+public class Hyperlinks {
+
+ public static void main(String[] args) throws IOException {
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ //cell style for hyperlinks
+ //by default hypelrinks are blue and underlined
+ HSSFCellStyle hlink_style = wb.createCellStyle();
+ HSSFFont hlink_font = wb.createFont();
+ hlink_font.setUnderline(HSSFFont.U_SINGLE);
+ hlink_font.setColor(HSSFColor.BLUE.index);
+ hlink_style.setFont(hlink_font);
+
+ HSSFCell cell;
+ HSSFSheet sheet = wb.createSheet("Hyperlinks");
+
+ //URL
+ cell = sheet.createRow(0).createCell((short)0);
+ cell.setCellValue("URL Link");
+ HSSFHyperlink link = new HSSFHyperlink(HSSFHyperlink.LINK_URL);
+ link.setAddress("http://poi.apache.org/");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlink_style);
+
+ //link to a file in the current directory
+ cell = sheet.createRow(1).createCell((short)0);
+ cell.setCellValue("File Link");
+ link = new HSSFHyperlink(HSSFHyperlink.LINK_FILE);
+ link.setAddress("link1.xls");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlink_style);
+
+ //e-mail link
+ cell = sheet.createRow(2).createCell((short)0);
+ cell.setCellValue("Email Link");
+ link = new HSSFHyperlink(HSSFHyperlink.LINK_EMAIL);
+ //note, if subject contains white spaces, make sure they are url-encoded
+ link.setAddress("mailto:poi@apache.org?subject=Hyperlinks");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlink_style);
+
+ //link to a place in this workbook
+
+ //create a target sheet and cell
+ HSSFSheet sheet2 = wb.createSheet("Target Sheet");
+ sheet2.createRow(0).createCell((short)0).setCellValue("Target Cell");
+
+ cell = sheet.createRow(3).createCell((short)0);
+ cell.setCellValue("Worksheet Link");
+ link = new HSSFHyperlink(HSSFHyperlink.LINK_DOCUMENT);
+ link.setAddress("'Target Sheet'!A1");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlink_style);
+
+ FileOutputStream out = new FileOutputStream("hssf-links.xls");
+ wb.write(out);
+ out.close();
+
+ }
+}
diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java
index ece7a3f13..8d91c06e7 100644
--- a/src/java/org/apache/poi/POIDocument.java
+++ b/src/java/org/apache/poi/POIDocument.java
@@ -67,14 +67,28 @@ public abstract class POIDocument {
/**
* Find, and create objects for, the standard
- * Documment Information Properties (HPSF)
+ * Documment Information Properties (HPSF).
+ * If a given property set is missing or corrupt,
+ * it will remain null;
*/
protected void readProperties() {
+ PropertySet ps;
+
// DocumentSummaryInformation
- dsInf = (DocumentSummaryInformation)getPropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+ ps = getPropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+ if(ps != null && ps instanceof DocumentSummaryInformation) {
+ dsInf = (DocumentSummaryInformation)ps;
+ } else if(ps != null) {
+ logger.log(POILogger.WARN, "DocumentSummaryInformation property set came back with wrong class - ", ps.getClass());
+ }
// SummaryInformation
- sInf = (SummaryInformation)getPropertySet(SummaryInformation.DEFAULT_STREAM_NAME);
+ ps = getPropertySet(SummaryInformation.DEFAULT_STREAM_NAME);
+ if(ps instanceof SummaryInformation) {
+ sInf = (SummaryInformation)ps;
+ } else if(ps != null) {
+ logger.log(POILogger.WARN, "SummaryInformation property set came back with wrong class - ", ps.getClass());
+ }
}
/**
diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java
index 3cfb58e6d..16b4f7e41 100644
--- a/src/java/org/apache/poi/hpsf/Property.java
+++ b/src/java/org/apache/poi/hpsf/Property.java
@@ -23,6 +23,8 @@ import java.util.Map;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
/**
*
A property in a {@link Section} of a {@link PropertySet}.
@@ -113,7 +115,8 @@ public class Property
*
* @param id the property's ID.
* @param type the property's type, see {@link Variant}.
- * @param value the property's value. Only certain types are allowed, see {@link Variant}.
+ * @param value the property's value. Only certain types are allowed, see
+ * {@link Variant}.
*/
public Property(final long id, final long type, final Object value)
{
@@ -210,68 +213,80 @@ public class Property
o += LittleEndian.INT_SIZE;
final Map m = new HashMap((int) nrEntries, (float) 1.0);
- for (int i = 0; i < nrEntries; i++)
+
+ try
{
- /* The key. */
- final Long id = new Long(LittleEndian.getUInt(src, o));
- o += LittleEndian.INT_SIZE;
-
- /* The value (a string). The length is the either the
- * number of (two-byte) characters if the character set is Unicode
- * or the number of bytes if the character set is not Unicode.
- * The length includes terminating 0x00 bytes which we have to strip
- * off to create a Java string. */
- long sLength = LittleEndian.getUInt(src, o);
- o += LittleEndian.INT_SIZE;
-
- /* Read the string. */
- final StringBuffer b = new StringBuffer();
- switch (codepage)
+ for (int i = 0; i < nrEntries; i++)
{
- case -1:
+ /* The key. */
+ final Long id = new Long(LittleEndian.getUInt(src, o));
+ o += LittleEndian.INT_SIZE;
+
+ /* The value (a string). The length is the either the
+ * number of (two-byte) characters if the character set is Unicode
+ * or the number of bytes if the character set is not Unicode.
+ * The length includes terminating 0x00 bytes which we have to strip
+ * off to create a Java string. */
+ long sLength = LittleEndian.getUInt(src, o);
+ o += LittleEndian.INT_SIZE;
+
+ /* Read the string. */
+ final StringBuffer b = new StringBuffer();
+ switch (codepage)
{
- /* Without a codepage the length is equal to the number of
- * bytes. */
- b.append(new String(src, o, (int) sLength));
- break;
- }
- case Constants.CP_UNICODE:
- {
- /* The length is the number of characters, i.e. the number
- * of bytes is twice the number of the characters. */
- final int nrBytes = (int) (sLength * 2);
- final byte[] h = new byte[nrBytes];
- for (int i2 = 0; i2 < nrBytes; i2 += 2)
+ case -1:
{
- h[i2] = src[o + i2 + 1];
- h[i2 + 1] = src[o + i2];
+ /* Without a codepage the length is equal to the number of
+ * bytes. */
+ b.append(new String(src, o, (int) sLength));
+ break;
+ }
+ case Constants.CP_UNICODE:
+ {
+ /* The length is the number of characters, i.e. the number
+ * of bytes is twice the number of the characters. */
+ final int nrBytes = (int) (sLength * 2);
+ final byte[] h = new byte[nrBytes];
+ for (int i2 = 0; i2 < nrBytes; i2 += 2)
+ {
+ h[i2] = src[o + i2 + 1];
+ h[i2 + 1] = src[o + i2];
+ }
+ b.append(new String(h, 0, nrBytes,
+ VariantSupport.codepageToEncoding(codepage)));
+ break;
+ }
+ default:
+ {
+ /* For encodings other than Unicode the length is the number
+ * of bytes. */
+ b.append(new String(src, o, (int) sLength,
+ VariantSupport.codepageToEncoding(codepage)));
+ break;
}
- b.append(new String(h, 0, nrBytes,
- VariantSupport.codepageToEncoding(codepage)));
- break;
}
- default:
- {
- /* For encodings other than Unicode the length is the number
- * of bytes. */
- b.append(new String(src, o, (int) sLength,
- VariantSupport.codepageToEncoding(codepage)));
- break;
- }
- }
- /* Strip 0x00 characters from the end of the string: */
- while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
- b.setLength(b.length() - 1);
- if (codepage == Constants.CP_UNICODE)
- {
- if (sLength % 2 == 1)
- sLength++;
- o += (sLength + sLength);
+ /* Strip 0x00 characters from the end of the string: */
+ while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
+ b.setLength(b.length() - 1);
+ if (codepage == Constants.CP_UNICODE)
+ {
+ if (sLength % 2 == 1)
+ sLength++;
+ o += (sLength + sLength);
+ }
+ else
+ o += sLength;
+ m.put(id, b.toString());
}
- else
- o += sLength;
- m.put(id, b.toString());
+ }
+ catch (RuntimeException ex)
+ {
+ final POILogger l = POILogFactory.getLogger(getClass());
+ l.log(POILogger.WARN,
+ "The property set's dictionary contains bogus data. "
+ + "All dictionary entries starting with the one with ID "
+ + id + " will be ignored.", ex);
}
return m;
}
@@ -320,11 +335,10 @@ public class Property
/**
- *
Compares two properties.
- *
- *
Please beware that a property with ID == 0 is a special case: It does not have a type, and its value is the section's
- * dictionary. Another special case are strings: Two properties may have
- * the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;
+ *
Compares two properties.
Please beware that a property with
+ * ID == 0 is a special case: It does not have a type, and its value is the
+ * section's dictionary. Another special case are strings: Two properties
+ * may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;
*
* @see Object#equals(java.lang.Object)
*/
diff --git a/src/java/org/apache/poi/hpsf/Section.java b/src/java/org/apache/poi/hpsf/Section.java
index 3b041ed9c..76824e7b7 100644
--- a/src/java/org/apache/poi/hpsf/Section.java
+++ b/src/java/org/apache/poi/hpsf/Section.java
@@ -210,7 +210,7 @@ public class Section
/* Pass 1: Read the property list. */
int pass1Offset = o1;
- List propertyList = new ArrayList(propertyCount);
+ final List propertyList = new ArrayList(propertyCount);
PropertyListEntry ple;
for (int i = 0; i < properties.length; i++)
{
diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java
index 1986cac30..703e8abe8 100644
--- a/src/java/org/apache/poi/hpsf/VariantSupport.java
+++ b/src/java/org/apache/poi/hpsf/VariantSupport.java
@@ -109,25 +109,51 @@ public class VariantSupport extends Variant
}
+ /**
+ *
Checks whether HPSF supports the specified variant type. Unsupported
+ * types should be implemented included in the {@link #SUPPORTED_TYPES}
+ * array.
+ *
+ * @see Variant
+ * @param variantType the variant type to check
+ * @return true if HPFS supports this type, else
+ * false
+ */
+ public boolean isSupportedType(final int variantType)
+ {
+ for (int i = 0; i < SUPPORTED_TYPES.length; i++)
+ if (variantType == SUPPORTED_TYPES[i])
+ return true;
+ return false;
+ }
+
+
/**
*