Merged revisions 618680-620582 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk ........ r618680 | nick | 2008-02-05 16:51:29 +0100 (Tue, 05 Feb 2008) | 1 line Add java.lang.Iterable style methods for iterating over rows and cells, but don't actually implement that, as it wasn't in jdk 1.3 or jdk 1.4, with is rather tedious ........ r618690 | nick | 2008-02-05 17:21:21 +0100 (Tue, 05 Feb 2008) | 1 line Tweaks to the iterator use guides for hssf ........ r618692 | nick | 2008-02-05 17:32:45 +0100 (Tue, 05 Feb 2008) | 1 line Few tweaks to the patching guide ........ r618940 | yegor | 2008-02-06 10:47:13 +0100 (Wed, 06 Feb 2008) | 1 line 3.0.2-FINAL released ........ r619001 | nick | 2008-02-06 15:35:05 +0100 (Wed, 06 Feb 2008) | 1 line Implement hashCode and equals for HSSFFont and HSSFCellStyle ........ r619310 | yegor | 2008-02-07 09:56:59 +0100 (Thu, 07 Feb 2008) | 1 line support for excel hypelrinks ........ r619382 | nick | 2008-02-07 13:39:12 +0100 (Thu, 07 Feb 2008) | 1 line Patch from bug #44373 - Have HSSFDateUtil.isADateFormat support more date formats ........ r619502 | nick | 2008-02-07 17:53:23 +0100 (Thu, 07 Feb 2008) | 1 line 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) - patch from bug #38641 ........ r619509 | nick | 2008-02-07 18:12:09 +0100 (Thu, 07 Feb 2008) | 1 line Add (disabled) failing testcase for bug #44375 ........ r619848 | klute | 2008-02-08 12:55:43 +0100 (Fri, 08 Feb 2008) | 1 line - Fixed bug 44375 - HPSF now copes 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. ........ r619849 | klute | 2008-02-08 12:56:11 +0100 (Fri, 08 Feb 2008) | 1 line - Fixed bug 44375 - HPSF now copes 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. ........ r619851 | nick | 2008-02-08 13:02:10 +0100 (Fri, 08 Feb 2008) | 1 line Copy Rainer's change note from changes.xml to status.xml, as it's needed in both ........ r619968 | nick | 2008-02-08 19:17:29 +0100 (Fri, 08 Feb 2008) | 1 line Tweak layout ........ r620341 | nick | 2008-02-10 22:54:13 +0100 (Sun, 10 Feb 2008) | 1 line Test for bug #42564 - appears to already be fixed ........ r620557 | nick | 2008-02-11 18:55:09 +0100 (Mon, 11 Feb 2008) | 1 line Another test relating to bug #42564 - this one is still failing ........ r620582 | nick | 2008-02-11 20:14:04 +0100 (Mon, 11 Feb 2008) | 1 line If we have a document with a hpsf stream that exists, but is of the wrong type, then log a warning but continue ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@627544 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7d48f4d18e
commit
8ec2aaafd6
@ -36,6 +36,11 @@
|
|||||||
|
|
||||||
<!-- Don't forget to update status.xml too! -->
|
<!-- Don't forget to update status.xml too! -->
|
||||||
<release version="3.1-beta1" date="2008-??-??">
|
<release version="3.1-beta1" date="2008-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="fix">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.</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="fix">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)</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="fix">44373 - Have HSSFDateUtil.isADateFormat recognize more formats as being dates</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">37923 - Support for Excel hyperlinks</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">Implement hashCode() and equals(obj) on HSSFFont and HSSFCellStyle</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44345 - Implement CountA, CountIf, Index, Rows and Columns functions</action>
|
<action dev="POI-DEVELOPERS" type="fix">44345 - Implement CountA, CountIf, Index, Rows and Columns functions</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44336 - Properly escape sheet names as required when figuring out the text of formulas</action>
|
<action dev="POI-DEVELOPERS" type="fix">44336 - Properly escape sheet names as required when figuring out the text of formulas</action>
|
||||||
<action dev="POI-DEVELOPERS" type="add">44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions</action>
|
<action dev="POI-DEVELOPERS" type="add">44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions</action>
|
||||||
|
@ -96,7 +96,8 @@
|
|||||||
Create patches by getting the latest sources from Subversion.
|
Create patches by getting the latest sources from Subversion.
|
||||||
Alter or add files as appropriate. Then, from the poi directiory,
|
Alter or add files as appropriate. Then, from the poi directiory,
|
||||||
type svn diff > mypatch.patch. This will capture all of your changes
|
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
|
files, create an archive (tar.bz2 preferred as its the smallest) in a
|
||||||
path-preserving archive format, relative to your poi directory.
|
path-preserving archive format, relative to your poi directory.
|
||||||
You'll attach both files in the next step.
|
You'll attach both files in the next step.
|
||||||
@ -117,6 +118,33 @@
|
|||||||
Standards</link>). Patches that are of low quality may be rejected or
|
Standards</link>). Patches that are of low quality may be rejected or
|
||||||
the contributer may be asked to bring them up to spec.
|
the contributer may be asked to bring them up to spec.
|
||||||
</p>
|
</p>
|
||||||
|
<p>If you use a unix shell, you may find the following following
|
||||||
|
sequence of commands useful for building the files to attach.</p>
|
||||||
|
<source>
|
||||||
|
# Run this in the root of the checkout, i.e. the directory holding
|
||||||
|
# build.xml and poi.pom
|
||||||
|
|
||||||
|
# Build the directory to hold new files
|
||||||
|
mkdir /tmp/poi-patch/
|
||||||
|
mkdir /tmp/poi-patch/new-files/
|
||||||
|
|
||||||
|
# Get changes to existing files
|
||||||
|
svn diff > /tmp/poi-patch/diff.txt
|
||||||
|
|
||||||
|
# Capture any new files, as svn diff won't include them
|
||||||
|
# Preserve the path
|
||||||
|
svn status | grep "^\?" | awk '{printf "cp --parents %s /tmp/poi-patch/new-files/\n", $2 }' | sh -s
|
||||||
|
|
||||||
|
# tar up the new files
|
||||||
|
cd /tmp/poi-patch/new-files/
|
||||||
|
tar jcvf ../new-files.tar.bz2
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Upload these to bugzilla
|
||||||
|
echo "Please upload to bugzilla:"
|
||||||
|
echo " /tmp/poi-patch/diff.txt"
|
||||||
|
echo " /tmp/poi-patch/new-files.tar.bz2"
|
||||||
|
</source>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
<li><link href="#NamedRanges">Named Ranges and Named Cells</link></li>
|
<li><link href="#NamedRanges">Named Ranges and Named Cells</link></li>
|
||||||
<li><link href="#CellComments">How to set cell comments</link></li>
|
<li><link href="#CellComments">How to set cell comments</link></li>
|
||||||
<li><link href="#Autofit">How to adjust column width to fit the contents</link></li>
|
<li><link href="#Autofit">How to adjust column width to fit the contents</link></li>
|
||||||
|
<li><link href="#Hyperlinks">Hyperlinks</link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section><title>Features</title>
|
<section><title>Features</title>
|
||||||
@ -236,16 +237,51 @@
|
|||||||
</source>
|
</source>
|
||||||
</section>
|
</section>
|
||||||
<anchor id="Iterator"/>
|
<anchor id="Iterator"/>
|
||||||
<section><title>Iterate over rows and cells (including Java 5 foreach loops)</title>
|
<section><title>Iterate over rows and cells</title>
|
||||||
<p>Sometimes, you'd like to just iterate over all the rows in
|
<p>Sometimes, you'd like to just iterate over all the rows in
|
||||||
a sheet, or all the cells in a row. If you are using Java
|
a sheet, or all the cells in a row. This is possible with
|
||||||
5 or later, then this is especially handy, as it'll allow the
|
a simple for loop.</p>
|
||||||
new foreach loop support to work.</p>
|
|
||||||
<p>Luckily, this is very easy. HSSFRow defines a
|
<p>Luckily, this is very easy. HSSFRow defines a
|
||||||
<em>CellIterator</em> inner class to handle iterating over
|
<em>CellIterator</em> inner class to handle iterating over
|
||||||
the cells (get one with a call to <em>row.cellIterator()</em>),
|
the cells (get one with a call to <em>row.cellIterator()</em>),
|
||||||
and HSSFSheet provides a <em>rowIterator()</em> method to
|
and HSSFSheet provides a <em>rowIterator()</em> method to
|
||||||
give an iterator over all the rows.</p>
|
give an iterator over all the rows.</p>
|
||||||
|
<p>(Unfortunately, due to the broken and
|
||||||
|
backwards-incompatible way that Java 5 foreach loops were
|
||||||
|
implemented, it isn't possible to use them on a codebase
|
||||||
|
that supports Java 1.4, as POI does)</p>
|
||||||
|
<source>
|
||||||
|
HSSFSheet sheet = wb.getSheetAt(0);
|
||||||
|
for (Iterator rit = sheet.rowIterator(); rit.hasNext(); ) {
|
||||||
|
HSSFRow row = (HSSFRow)rit.next();
|
||||||
|
for (Iterator cit = row.cellIterator(); cit.hasNext(); ) {
|
||||||
|
HSSFCell cell = (HSSFCell)cit.next();
|
||||||
|
// Do something here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</source>
|
||||||
|
<source>
|
||||||
|
HSSFSheet sheet = wb.getSheetAt(0);
|
||||||
|
for (Iterator<HSSFRow> rit = (Iterator<HSSFRow>)sheet.rowIterator(); rit.hasNext(); ) {
|
||||||
|
HSSFRow row = rit.next();
|
||||||
|
for (Iterator<HSSFCell> cit = (Iterator<HSSFCell>)row.cellIterator(); cit.hasNext(); ) {
|
||||||
|
HSSFCell cell = cit.next();
|
||||||
|
// Do something here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</source>
|
||||||
|
</section>
|
||||||
|
<section><title>Iterate over rows and cells using Java 1.5 foreach loops - OOXML Branch Only</title>
|
||||||
|
<p>Sometimes, you'd like to just iterate over all the rows in
|
||||||
|
a sheet, or all the cells in a row. If you are using Java
|
||||||
|
5 or later, then this is especially handy, as it'll allow the
|
||||||
|
new foreach loop support to work.</p>
|
||||||
|
<p>Luckily, this is very easy. Both HSSFSheet and HSSFRow
|
||||||
|
implement <em>java.lang.Iterable</em> to allow foreach
|
||||||
|
loops. For HSSFRow this allows access to the
|
||||||
|
<em>CellIterator</em> inner class to handle iterating over
|
||||||
|
the cells, and for HSSFSheet gives the
|
||||||
|
<em>rowIterator()</em> to iterator over all the rows.</p>
|
||||||
<source>
|
<source>
|
||||||
HSSFSheet sheet = wb.getSheetAt(0);
|
HSSFSheet sheet = wb.getSheetAt(0);
|
||||||
for (HSSFRow row : sheet.rowIterator()) {
|
for (HSSFRow row : sheet.rowIterator()) {
|
||||||
@ -254,6 +290,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</source>
|
</source>
|
||||||
|
<note>This only works on the OOXML branch of POI</note>
|
||||||
</section>
|
</section>
|
||||||
<anchor id="TextExtraction"/>
|
<anchor id="TextExtraction"/>
|
||||||
<section><title>Text Extraction</title>
|
<section><title>Text Extraction</title>
|
||||||
@ -1286,6 +1323,76 @@ Examples:
|
|||||||
(either via <code>-Djava.awt.headless=true</code> startup parameter or via <code>System.setProperty("java.awt.headless", "true")</code>).
|
(either via <code>-Djava.awt.headless=true</code> startup parameter or via <code>System.setProperty("java.awt.headless", "true")</code>).
|
||||||
</warning>
|
</warning>
|
||||||
</section>
|
</section>
|
||||||
|
<anchor id="Hyperlinks"/>
|
||||||
|
<section><title>How to read hyperlinks</title>
|
||||||
|
<source>
|
||||||
|
HSSFSheet sheet = workbook.getSheetAt(0);
|
||||||
|
|
||||||
|
HSSFCell cell = sheet.getRow(0).getCell((short)0);
|
||||||
|
HSSFHyperlink link = cell.getHyperlink();
|
||||||
|
if(link != null){
|
||||||
|
System.out.println(link.getAddress());
|
||||||
|
}
|
||||||
|
</source>
|
||||||
|
</section>
|
||||||
|
<section><title>How to create hyperlinks</title>
|
||||||
|
<source>
|
||||||
|
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();
|
||||||
|
</source>
|
||||||
|
</section>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</document>
|
</document>
|
||||||
|
@ -31,16 +31,17 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<section><title>POI 3.0.2 BETA2 Release</title>
|
<section><title>February 06 2008 - POI 3.0.2 Released</title>
|
||||||
<p>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
|
<p>The POI team is pleased to announce POI 3.0.2, the latest release of Apache POI.
|
||||||
new features and bug fixes, compared to 3.0.1. A full list of changes
|
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
|
||||||
is available in
|
<link href="./changes.html">the changelog</link>, and
|
||||||
<link href="./changes.html">the changelog</link>, and
|
<link href="http://www.apache.org/dyn/closer.cgi/poi/release/">download</link>
|
||||||
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">download</link>
|
|
||||||
the source and binaries from your
|
the source and binaries from your
|
||||||
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">local mirror</link>.
|
<link href="http://www.apache.org/dyn/closer.cgi/poi/release/">local mirror</link>.
|
||||||
The release is also available from the central Maven repository under Group ID "org.apache.poi".
|
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
The release is also available from the central Maven repository under Group ID "org.apache.poi" and Version "3.0.2-FINAL".
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section><title>POI 3.0.1 Release</title>
|
<section><title>POI 3.0.1 Release</title>
|
||||||
|
@ -33,6 +33,11 @@
|
|||||||
<!-- Don't forget to update changes.xml too! -->
|
<!-- Don't forget to update changes.xml too! -->
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.1-beta1" date="2008-??-??">
|
<release version="3.1-beta1" date="2008-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="fix">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.</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="fix">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)</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="fix">44373 - Have HSSFDateUtil.isADateFormat recognize more formats as being dates</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">37923 - Support for Excel hyperlinks</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">Implement hashCode() and equals(obj) on HSSFFont and HSSFCellStyle</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44345 - Implement CountA, CountIf, Index, Rows and Columns functions</action>
|
<action dev="POI-DEVELOPERS" type="fix">44345 - Implement CountA, CountIf, Index, Rows and Columns functions</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44336 - Properly escape sheet names as required when figuring out the text of formulas</action>
|
<action dev="POI-DEVELOPERS" type="fix">44336 - Properly escape sheet names as required when figuring out the text of formulas</action>
|
||||||
<action dev="POI-DEVELOPERS" type="add">44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions</action>
|
<action dev="POI-DEVELOPERS" type="add">44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions</action>
|
||||||
|
91
src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java
Executable file
91
src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java
Executable file
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -67,14 +67,28 @@ public abstract class POIDocument {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Find, and create objects for, the standard
|
* 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() {
|
protected void readProperties() {
|
||||||
|
PropertySet ps;
|
||||||
|
|
||||||
// DocumentSummaryInformation
|
// 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
|
// 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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,6 +23,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.apache.poi.util.HexDump;
|
import org.apache.poi.util.HexDump;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A property in a {@link Section} of a {@link PropertySet}.</p>
|
* <p>A property in a {@link Section} of a {@link PropertySet}.</p>
|
||||||
@ -113,7 +115,8 @@ public class Property
|
|||||||
*
|
*
|
||||||
* @param id the property's ID.
|
* @param id the property's ID.
|
||||||
* @param type the property's type, see {@link Variant}.
|
* @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)
|
public Property(final long id, final long type, final Object value)
|
||||||
{
|
{
|
||||||
@ -210,68 +213,80 @@ public class Property
|
|||||||
o += LittleEndian.INT_SIZE;
|
o += LittleEndian.INT_SIZE;
|
||||||
|
|
||||||
final Map m = new HashMap((int) nrEntries, (float) 1.0);
|
final Map m = new HashMap((int) nrEntries, (float) 1.0);
|
||||||
for (int i = 0; i < nrEntries; i++)
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
/* The key. */
|
for (int i = 0; i < nrEntries; i++)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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
|
case -1:
|
||||||
* 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];
|
/* Without a codepage the length is equal to the number of
|
||||||
h[i2 + 1] = src[o + i2];
|
* 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: */
|
/* Strip 0x00 characters from the end of the string: */
|
||||||
while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
|
while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
|
||||||
b.setLength(b.length() - 1);
|
b.setLength(b.length() - 1);
|
||||||
if (codepage == Constants.CP_UNICODE)
|
if (codepage == Constants.CP_UNICODE)
|
||||||
{
|
{
|
||||||
if (sLength % 2 == 1)
|
if (sLength % 2 == 1)
|
||||||
sLength++;
|
sLength++;
|
||||||
o += (sLength + sLength);
|
o += (sLength + sLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
o += sLength;
|
||||||
|
m.put(id, b.toString());
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
o += sLength;
|
catch (RuntimeException ex)
|
||||||
m.put(id, b.toString());
|
{
|
||||||
|
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;
|
return m;
|
||||||
}
|
}
|
||||||
@ -320,11 +335,10 @@ public class Property
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Compares two properties.</p>
|
* <p>Compares two properties.</p> <p>Please beware that a property with
|
||||||
*
|
* ID == 0 is a special case: It does not have a type, and its value is the
|
||||||
* <p>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
|
* section's dictionary. Another special case are strings: Two properties
|
||||||
* dictionary. Another special case are strings: Two properties may have
|
* may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
|
||||||
* the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
|
|
||||||
*
|
*
|
||||||
* @see Object#equals(java.lang.Object)
|
* @see Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
|
@ -210,7 +210,7 @@ public class Section
|
|||||||
|
|
||||||
/* Pass 1: Read the property list. */
|
/* Pass 1: Read the property list. */
|
||||||
int pass1Offset = o1;
|
int pass1Offset = o1;
|
||||||
List propertyList = new ArrayList(propertyCount);
|
final List propertyList = new ArrayList(propertyCount);
|
||||||
PropertyListEntry ple;
|
PropertyListEntry ple;
|
||||||
for (int i = 0; i < properties.length; i++)
|
for (int i = 0; i < properties.length; i++)
|
||||||
{
|
{
|
||||||
|
@ -109,25 +109,51 @@ public class VariantSupport extends Variant
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>HPSF is able to read these {@link Variant} types.</p>
|
||||||
|
*/
|
||||||
|
final static public int[] SUPPORTED_TYPES = { Variant.VT_EMPTY,
|
||||||
|
Variant.VT_I2, Variant.VT_I4, Variant.VT_I8, Variant.VT_R8,
|
||||||
|
Variant.VT_FILETIME, Variant.VT_LPSTR, Variant.VT_LPWSTR,
|
||||||
|
Variant.VT_CF, Variant.VT_BOOL };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks whether HPSF supports the specified variant type. Unsupported
|
||||||
|
* types should be implemented included in the {@link #SUPPORTED_TYPES}
|
||||||
|
* array.</p>
|
||||||
|
*
|
||||||
|
* @see Variant
|
||||||
|
* @param variantType the variant type to check
|
||||||
|
* @return <code>true</code> if HPFS supports this type, else
|
||||||
|
* <code>false</code>
|
||||||
|
*/
|
||||||
|
public boolean isSupportedType(final int variantType)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < SUPPORTED_TYPES.length; i++)
|
||||||
|
if (variantType == SUPPORTED_TYPES[i])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Reads a variant type from a byte array.</p>
|
* <p>Reads a variant type from a byte array.</p>
|
||||||
*
|
*
|
||||||
* @param src The byte array
|
* @param src The byte array
|
||||||
* @param offset The offset in the byte array where the variant
|
* @param offset The offset in the byte array where the variant starts
|
||||||
* starts
|
* @param length The length of the variant including the variant type field
|
||||||
* @param length The length of the variant including the variant
|
|
||||||
* type field
|
|
||||||
* @param type The variant type to read
|
* @param type The variant type to read
|
||||||
* @param codepage The codepage to use to write non-wide strings
|
* @param codepage The codepage to use for non-wide strings
|
||||||
* @return A Java object that corresponds best to the variant
|
* @return A Java object that corresponds best to the variant field. For
|
||||||
* field. For example, a VT_I4 is returned as a {@link Long}, a
|
* example, a VT_I4 is returned as a {@link Long}, a VT_LPSTR as a
|
||||||
* VT_LPSTR as a {@link String}.
|
* {@link String}.
|
||||||
* @exception ReadingNotSupportedException if a property is to be written
|
* @exception ReadingNotSupportedException if a property is to be written
|
||||||
* who's variant type HPSF does not yet support
|
* who's variant type HPSF does not yet support
|
||||||
* @exception UnsupportedEncodingException if the specified codepage is not
|
* @exception UnsupportedEncodingException if the specified codepage is not
|
||||||
* supported.
|
* supported.
|
||||||
*
|
|
||||||
* @see Variant
|
* @see Variant
|
||||||
*/
|
*/
|
||||||
public static Object read(final byte[] src, final int offset,
|
public static Object read(final byte[] src, final int offset,
|
||||||
|
@ -1814,4 +1814,56 @@ public class ExtendedFormatRecord
|
|||||||
{
|
{
|
||||||
return sid;
|
return sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + field_1_font_index;
|
||||||
|
result = prime * result + field_2_format_index;
|
||||||
|
result = prime * result + field_3_cell_options;
|
||||||
|
result = prime * result + field_4_alignment_options;
|
||||||
|
result = prime * result + field_5_indention_options;
|
||||||
|
result = prime * result + field_6_border_options;
|
||||||
|
result = prime * result + field_7_palette_options;
|
||||||
|
result = prime * result + field_8_adtl_palette_options;
|
||||||
|
result = prime * result + field_9_fill_palette_options;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will consider two different records with the same
|
||||||
|
* contents as equals, as the various indexes
|
||||||
|
* that matter are embedded in the records
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (obj instanceof ExtendedFormatRecord) {
|
||||||
|
final ExtendedFormatRecord other = (ExtendedFormatRecord) obj;
|
||||||
|
if (field_1_font_index != other.field_1_font_index)
|
||||||
|
return false;
|
||||||
|
if (field_2_format_index != other.field_2_format_index)
|
||||||
|
return false;
|
||||||
|
if (field_3_cell_options != other.field_3_cell_options)
|
||||||
|
return false;
|
||||||
|
if (field_4_alignment_options != other.field_4_alignment_options)
|
||||||
|
return false;
|
||||||
|
if (field_5_indention_options != other.field_5_indention_options)
|
||||||
|
return false;
|
||||||
|
if (field_6_border_options != other.field_6_border_options)
|
||||||
|
return false;
|
||||||
|
if (field_7_palette_options != other.field_7_palette_options)
|
||||||
|
return false;
|
||||||
|
if (field_8_adtl_palette_options != other.field_8_adtl_palette_options)
|
||||||
|
return false;
|
||||||
|
if (field_9_fill_palette_options != other.field_9_fill_palette_options)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -538,4 +538,37 @@ public class FontRecord
|
|||||||
{
|
{
|
||||||
return sid;
|
return sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime
|
||||||
|
* result
|
||||||
|
+ ((field_11_font_name == null) ? 0 : field_11_font_name
|
||||||
|
.hashCode());
|
||||||
|
result = prime * result + field_1_font_height;
|
||||||
|
result = prime * result + field_2_attributes;
|
||||||
|
result = prime * result + field_3_color_palette_index;
|
||||||
|
result = prime * result + field_4_bold_weight;
|
||||||
|
result = prime * result + field_5_super_sub_script;
|
||||||
|
result = prime * result + field_6_underline;
|
||||||
|
result = prime * result + field_7_family;
|
||||||
|
result = prime * result + field_8_charset;
|
||||||
|
result = prime * result + field_9_zero;
|
||||||
|
result = prime * result + field_10_font_name_len;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only returns two for the same exact object -
|
||||||
|
* creating a second FontRecord with the same
|
||||||
|
* properties won't be considered equal, as
|
||||||
|
* the record's position in the record stream
|
||||||
|
* matters.
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,27 @@
|
|||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Copyright 2002-2004 Apache Software Foundation
|
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
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
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,
|
||||||
Unless required by applicable law or agreed to in writing, software
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
See the License for the specific language governing permissions and
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
limitations under the License.
|
||||||
See the License for the specific language governing permissions and
|
==================================================================== */
|
||||||
limitations under the License.
|
|
||||||
==================================================================== */
|
|
||||||
|
|
||||||
package org.apache.poi.hssf.record;
|
package org.apache.poi.hssf.record;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.util.Arrays;
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.StringUtil;
|
import org.apache.poi.util.StringUtil;
|
||||||
|
import org.apache.poi.util.HexDump;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <code>HyperlinkRecord</code> wraps an HLINK-record
|
* The <code>HyperlinkRecord</code> wraps an HLINK-record
|
||||||
@ -31,146 +29,283 @@ import org.apache.poi.util.StringUtil;
|
|||||||
* Supports only external links for now (eg http://)
|
* Supports only external links for now (eg http://)
|
||||||
*
|
*
|
||||||
* @author Mark Hissink Muller <a href="mailto:mark@hissinkmuller.nl >mark&064;hissinkmuller.nl</a>
|
* @author Mark Hissink Muller <a href="mailto:mark@hissinkmuller.nl >mark&064;hissinkmuller.nl</a>
|
||||||
|
* @author Yegor Kozlov (yegor at apache dot org)
|
||||||
*/
|
*/
|
||||||
public class HyperlinkRecord extends Record implements CellValueRecordInterface
|
public class HyperlinkRecord extends Record {
|
||||||
{
|
/**
|
||||||
/** Indicates the URL in the Record */
|
* Link flags
|
||||||
private static byte[] GUID_OF_URL_MONIKER =
|
*/
|
||||||
{ -32, -55, -22, 121, -7, -70, -50, 17, -116, -126, 0, -86, 0, 75, -87, 11 };
|
protected static final int HLINK_URL = 0x01; // File link or URL.
|
||||||
|
protected static final int HLINK_ABS = 0x02; // Absolute path.
|
||||||
|
protected static final int HLINK_LABEL = 0x14; // Has label.
|
||||||
|
protected static final int HLINK_PLACE = 0x08; // Place in worksheet.
|
||||||
|
|
||||||
/** Indicates the STD_LINK in the Record */
|
|
||||||
// MHM: to be added when necessary
|
|
||||||
private static byte[] GUID_OF_STD_LINK = {};
|
|
||||||
|
|
||||||
/** Logger */
|
protected final static byte[] STD_MONIKER = {(byte)0xD0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
public static final Log log = LogFactory.getLog(HyperlinkRecord.class);
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B };
|
||||||
|
protected final static byte[] URL_MONIKER = {(byte)0xE0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B };
|
||||||
|
protected final static byte[] FILE_MONIKER = {0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46};
|
||||||
|
|
||||||
// quick and dirty
|
/**
|
||||||
private static final boolean _DEBUG_ = true;
|
* Tail of a URL link
|
||||||
|
*/
|
||||||
|
protected final static byte[] URL_TAIL = {0x79, 0x58, (byte)0x81, (byte)0xF4, 0x3B, 0x1D, 0x7F, 0x48, (byte)0xAF, 0x2C,
|
||||||
|
(byte)0x82, 0x5D, (byte)0xC4, (byte)0x85, 0x27, 0x63, 0x00, 0x00, 0x00,
|
||||||
|
0x00, (byte)0xA5, (byte)0xAB, 0x00, 0x00};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tail of a file link
|
||||||
|
*/
|
||||||
|
protected final static byte[] FILE_TAIL = {(byte)0xFF, (byte)0xFF, (byte)0xAD, (byte)0xDE, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
public final static short sid = 0x1b8;
|
public final static short sid = 0x1b8;
|
||||||
|
|
||||||
private short field_1_unknown;
|
/**
|
||||||
private int field_2_row;
|
* First row of the hyperlink
|
||||||
private short field_3_column;
|
*/
|
||||||
private short field_4_xf_index;
|
private int rwFirst;
|
||||||
private byte[] field_5_unknown;
|
|
||||||
private int field_6_label_opts;
|
|
||||||
private int field_7_url_len;
|
|
||||||
private int field_8_label_len;
|
|
||||||
private String field_9_label;
|
|
||||||
private byte[] field_10_unknown;
|
|
||||||
private int field_11_url_opts;
|
|
||||||
private String field_12_url;
|
|
||||||
|
|
||||||
/** Blank Constructor */
|
/**
|
||||||
|
* Last row of the hyperlink
|
||||||
|
*/
|
||||||
|
private int rwLast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First column of the hyperlink
|
||||||
|
*/
|
||||||
|
private short colFirst;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last column of the hyperlink
|
||||||
|
*/
|
||||||
|
private short colLast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 16-byte GUID
|
||||||
|
*/
|
||||||
|
private byte[] guid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some sort of options. Seems to always equal 2
|
||||||
|
*/
|
||||||
|
private int label_opts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some sort of options for file links.
|
||||||
|
*/
|
||||||
|
private short file_opts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link options. Can include any of HLINK_* flags.
|
||||||
|
*/
|
||||||
|
private int link_opts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test label
|
||||||
|
*/
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moniker. Makes sense only for URL and file links
|
||||||
|
*/
|
||||||
|
private byte[] moniker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link
|
||||||
|
*/
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remaining bytes
|
||||||
|
*/
|
||||||
|
private byte[] tail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new hyperlink
|
||||||
|
*/
|
||||||
public HyperlinkRecord()
|
public HyperlinkRecord()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Real Constructor */
|
/**
|
||||||
|
* Read hyperlink from input stream
|
||||||
|
*
|
||||||
|
* @param in the stream to read from
|
||||||
|
*/
|
||||||
public HyperlinkRecord(RecordInputStream in)
|
public HyperlinkRecord(RecordInputStream in)
|
||||||
{
|
{
|
||||||
super(in);
|
super(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#getColumn()
|
* Return the column of the first cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @return the 0-based column of the first cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public short getColumn()
|
public short getFirstColumn()
|
||||||
{
|
{
|
||||||
return field_3_column;
|
return colFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#getRow()
|
* Set the column of the first cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @param col the 0-based column of the first cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public int getRow()
|
public void setFirstColumn(short col)
|
||||||
{
|
{
|
||||||
return field_2_row;
|
this.colFirst = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#getXFIndex()
|
* Set the column of the last cell that contains the hyperlink
|
||||||
*/
|
*
|
||||||
public short getXFIndex()
|
* @return the 0-based column of the last cell that contains the hyperlink
|
||||||
|
*/
|
||||||
|
public short getLastColumn()
|
||||||
{
|
{
|
||||||
return field_4_xf_index;
|
return colLast;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#isAfter(org.apache.poi.hssf.record.CellValueRecordInterface)
|
* Set the column of the last cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @param col the 0-based column of the last cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public boolean isAfter(CellValueRecordInterface i)
|
public void setLastColumn(short col)
|
||||||
{
|
{
|
||||||
if (this.getRow() < i.getRow())
|
this.colLast = col;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((this.getRow() == i.getRow()) && (this.getColumn() < i.getColumn()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((this.getRow() == i.getRow()) && (this.getColumn() == i.getColumn()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#isBefore(org.apache.poi.hssf.record.CellValueRecordInterface)
|
* Return the row of the first cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @return the 0-based row of the first cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public boolean isBefore(CellValueRecordInterface i)
|
public int getFirstRow()
|
||||||
{
|
{
|
||||||
if (this.getRow() > i.getRow())
|
return rwFirst;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((this.getRow() == i.getRow()) && (this.getColumn() > i.getColumn()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((this.getRow() == i.getRow()) && (this.getColumn() == i.getColumn()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#isEqual(org.apache.poi.hssf.record.CellValueRecordInterface)
|
* Set the row of the first cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @param row the 0-based row of the first cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public boolean isEqual(CellValueRecordInterface i)
|
public void setFirstRow(int row)
|
||||||
{
|
{
|
||||||
return ((this.getRow() == i.getRow()) && (this.getColumn() == i.getColumn()));
|
this.rwFirst = row;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#setColumn(short)
|
* Return the row of the last cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @return the 0-based row of the last cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public void setColumn(short col)
|
public int getLastRow()
|
||||||
{
|
{
|
||||||
this.field_3_column = col;
|
return rwLast;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#setRow(int)
|
* Set the row of the last cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @param row the 0-based row of the last cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public void setRow(int row)
|
public void setLastRow(int row)
|
||||||
{
|
{
|
||||||
this.field_2_row = row;
|
this.rwLast = row;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#setXFIndex(short)
|
* Returns a 16-byte guid identifier. Seems to always equal {@link STD_MONIKER}
|
||||||
|
*
|
||||||
|
* @return 16-byte guid identifier
|
||||||
*/
|
*/
|
||||||
public void setXFIndex(short xf)
|
public byte[] getGuid()
|
||||||
{
|
{
|
||||||
this.field_4_xf_index = xf;
|
return guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a 16-byte moniker.
|
||||||
|
*
|
||||||
|
* @return 16-byte moniker
|
||||||
|
*/
|
||||||
|
public byte[] getMoniker()
|
||||||
|
{
|
||||||
|
return moniker;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return text label for this hyperlink
|
||||||
|
*
|
||||||
|
* @return text to display
|
||||||
|
*/
|
||||||
|
public String getLabel()
|
||||||
|
{
|
||||||
|
int idx = label.indexOf('\u0000');
|
||||||
|
return idx == -1 ? label : label.substring(0, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets text label for this hyperlink
|
||||||
|
*
|
||||||
|
* @param label text label for this hyperlink
|
||||||
|
*/
|
||||||
|
public void setLabel(String label)
|
||||||
|
{
|
||||||
|
this.label = label + '\u0000';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hypelink address. Depending on the hyperlink type it can be URL, e-mail, patrh to a file, etc.
|
||||||
|
*
|
||||||
|
* @return the address of this hyperlink
|
||||||
|
*/
|
||||||
|
public String getAddress()
|
||||||
|
{
|
||||||
|
int idx = address.indexOf('\u0000');
|
||||||
|
return idx == -1 ? address : address.substring(0, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hypelink address. Depending on the hyperlink type it can be URL, e-mail, patrh to a file, etc.
|
||||||
|
*
|
||||||
|
* @param address the address of this hyperlink
|
||||||
|
*/
|
||||||
|
public void setAddress(String address)
|
||||||
|
{
|
||||||
|
this.address = address + '\u0000';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link options. Must be a combination of HLINK_* constants.
|
||||||
|
*/
|
||||||
|
public int getLinkOptions(){
|
||||||
|
return link_opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label options
|
||||||
|
*/
|
||||||
|
public int getLabelOptions(){
|
||||||
|
return label_opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for a file link
|
||||||
|
*/
|
||||||
|
public int getFileOptions(){
|
||||||
|
return file_opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getTail(){
|
||||||
|
return tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,57 +313,56 @@ public class HyperlinkRecord extends Record implements CellValueRecordInterface
|
|||||||
*/
|
*/
|
||||||
protected void fillFields(RecordInputStream in)
|
protected void fillFields(RecordInputStream in)
|
||||||
{
|
{
|
||||||
// System.err.println(in.currentSid);
|
|
||||||
// System.err.println(in.currentLength);
|
|
||||||
// for(int i=0; i<300; i++) {
|
|
||||||
// System.err.println(in.readByte());
|
|
||||||
// }
|
|
||||||
// if(1==1)
|
|
||||||
// throw new IllegalArgumentException("");
|
|
||||||
|
|
||||||
field_1_unknown = in.readShort();
|
|
||||||
field_2_row = in.readUShort();
|
|
||||||
field_3_column = in.readShort();
|
|
||||||
field_4_xf_index = in.readShort();
|
|
||||||
|
|
||||||
// Next up is 16 bytes we don't get
|
|
||||||
field_5_unknown = new byte[16];
|
|
||||||
try {
|
try {
|
||||||
in.read(field_5_unknown);
|
rwFirst = in.readShort();
|
||||||
} catch(IOException e) { throw new IllegalStateException(e.getMessage()); }
|
rwLast = in.readUShort();
|
||||||
|
colFirst = in.readShort();
|
||||||
// Some sort of opts
|
colLast = in.readShort();
|
||||||
field_6_label_opts = in.readInt();
|
|
||||||
|
// 16-byte GUID
|
||||||
// Now for lengths, in characters
|
guid = new byte[16];
|
||||||
field_7_url_len = in.readInt();
|
in.read(guid);
|
||||||
field_8_label_len = in.readInt();
|
|
||||||
|
label_opts = in.readInt();
|
||||||
// Now we have the label, as little endian unicode,
|
link_opts = in.readInt();
|
||||||
// with a trailing \0
|
|
||||||
field_9_label = in.readUnicodeLEString(field_8_label_len);
|
if ((link_opts & HLINK_LABEL) != 0){
|
||||||
|
int label_len = in.readInt();
|
||||||
// Next up is some more data we can't make sense of
|
label = in.readUnicodeLEString(label_len);
|
||||||
field_10_unknown = new byte[16];
|
}
|
||||||
try {
|
|
||||||
in.read(field_10_unknown);
|
if ((link_opts & HLINK_URL) != 0){
|
||||||
} catch(IOException e) { throw new IllegalStateException(e.getMessage()); }
|
moniker = new byte[16];
|
||||||
|
in.read(moniker);
|
||||||
// Might need to nudge the length by one byte
|
|
||||||
// This is an empirical hack!
|
if(Arrays.equals(URL_MONIKER, moniker)){
|
||||||
field_11_url_opts = in.readInt();
|
int len = in.readInt();
|
||||||
if(field_11_url_opts == 44) {
|
|
||||||
field_7_url_len--;
|
address = in.readUnicodeLEString(len/2);
|
||||||
|
|
||||||
|
tail = in.readRemainder();
|
||||||
|
} else if (Arrays.equals(FILE_MONIKER, moniker)){
|
||||||
|
file_opts = in.readShort();
|
||||||
|
|
||||||
|
int len = in.readInt();
|
||||||
|
|
||||||
|
byte[] path_bytes = new byte[len];
|
||||||
|
in.read(path_bytes);
|
||||||
|
|
||||||
|
address = new String(path_bytes);
|
||||||
|
|
||||||
|
tail = in.readRemainder();
|
||||||
|
}
|
||||||
|
} else if((link_opts & HLINK_PLACE) != 0){
|
||||||
|
int len = in.readInt();
|
||||||
|
address = in.readUnicodeLEString(len);
|
||||||
|
}
|
||||||
|
} catch (IOException e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally it's the URL
|
|
||||||
int strlen = field_7_url_len > (in.remaining()/2) ? (in.remaining()/2) : field_7_url_len;
|
|
||||||
field_12_url = in.readUnicodeLEString(strlen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.apache.poi.hssf.record.Record#getSid()
|
|
||||||
*/
|
|
||||||
public short getSid()
|
public short getSid()
|
||||||
{
|
{
|
||||||
return HyperlinkRecord.sid;
|
return HyperlinkRecord.sid;
|
||||||
@ -244,55 +378,75 @@ public class HyperlinkRecord extends Record implements CellValueRecordInterface
|
|||||||
|
|
||||||
public int serialize(int offset, byte[] data)
|
public int serialize(int offset, byte[] data)
|
||||||
{
|
{
|
||||||
LittleEndian.putShort(data, 0 + offset, sid);
|
int pos = offset;
|
||||||
LittleEndian.putShort(data, 2 + offset,
|
LittleEndian.putShort(data, pos, sid); pos += 2;
|
||||||
( short )(getRecordSize()-4));
|
LittleEndian.putShort(data, pos, ( short )(getRecordSize()-4)); pos += 2;
|
||||||
LittleEndian.putShort(data, 4 + offset, field_1_unknown);
|
LittleEndian.putUShort(data, pos, rwFirst); pos += 2;
|
||||||
LittleEndian.putUShort(data, 6 + offset, field_2_row);
|
LittleEndian.putUShort(data, pos, rwLast); pos += 2;
|
||||||
LittleEndian.putShort(data, 8 + offset, field_3_column);
|
LittleEndian.putShort(data, pos, colFirst); pos += 2;
|
||||||
LittleEndian.putShort(data, 10 + offset, field_4_xf_index);
|
LittleEndian.putShort(data, pos, colLast); pos += 2;
|
||||||
|
|
||||||
offset += 12;
|
|
||||||
for(int i=0; i<field_5_unknown.length; i++) {
|
|
||||||
data[offset] = field_5_unknown[i];
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian.putInt(data, offset, field_6_label_opts);
|
|
||||||
offset += 4;
|
|
||||||
LittleEndian.putInt(data, offset, field_7_url_len);
|
|
||||||
offset += 4;
|
|
||||||
LittleEndian.putInt(data, offset, field_8_label_len);
|
|
||||||
offset += 4;
|
|
||||||
StringUtil.putUnicodeLE(field_9_label, data, offset);
|
|
||||||
offset += field_9_label.length()*2;
|
|
||||||
|
|
||||||
for(int i=0; i<field_10_unknown.length; i++) {
|
System.arraycopy(guid, 0, data, pos, guid.length); pos += guid.length;
|
||||||
data[offset] = field_10_unknown[i];
|
|
||||||
offset++;
|
LittleEndian.putInt(data, pos, label_opts); pos += 4;
|
||||||
|
LittleEndian.putInt(data, pos, link_opts); pos += 4;
|
||||||
|
|
||||||
|
if ((link_opts & HLINK_LABEL) != 0){
|
||||||
|
LittleEndian.putInt(data, pos, label.length()); pos += 4;
|
||||||
|
StringUtil.putUnicodeLE(label, data, pos); pos += label.length()*2;
|
||||||
|
}
|
||||||
|
if ((link_opts & HLINK_URL) != 0){
|
||||||
|
System.arraycopy(moniker, 0, data, pos, moniker.length); pos += moniker.length;
|
||||||
|
if(Arrays.equals(URL_MONIKER, moniker)){
|
||||||
|
LittleEndian.putInt(data, pos, address.length()*2 + tail.length); pos += 4;
|
||||||
|
StringUtil.putUnicodeLE(address, data, pos); pos += address.length()*2;
|
||||||
|
if(tail.length > 0){
|
||||||
|
System.arraycopy(tail, 0, data, pos, tail.length); pos += tail.length;
|
||||||
|
}
|
||||||
|
} else if (Arrays.equals(FILE_MONIKER, moniker)){
|
||||||
|
LittleEndian.putShort(data, pos, file_opts); pos += 2;
|
||||||
|
LittleEndian.putInt(data, pos, address.length()); pos += 4;
|
||||||
|
byte[] bytes = address.getBytes();
|
||||||
|
System.arraycopy(bytes, 0, data, pos, bytes.length); pos += bytes.length;
|
||||||
|
if(tail.length > 0){
|
||||||
|
System.arraycopy(tail, 0, data, pos, tail.length); pos += tail.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if((link_opts & HLINK_PLACE) != 0){
|
||||||
|
LittleEndian.putInt(data, pos, address.length()); pos += 4;
|
||||||
|
StringUtil.putUnicodeLE(address, data, pos); pos += address.length()*2;
|
||||||
}
|
}
|
||||||
|
|
||||||
LittleEndian.putInt(data, offset, field_11_url_opts);
|
|
||||||
offset += 4;
|
|
||||||
StringUtil.putUnicodeLE(field_12_url, data, offset);
|
|
||||||
|
|
||||||
return getRecordSize();
|
return getRecordSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRecordSize()
|
public int getRecordSize()
|
||||||
{
|
{
|
||||||
// We have:
|
int size = 4;
|
||||||
// 4 shorts
|
size += 2 + 2 + 2 + 2; //rwFirst, rwLast, colFirst, colLast
|
||||||
// junk
|
size += guid.length;
|
||||||
// 3 ints
|
size += 4; //label_opts
|
||||||
// label
|
size += 4; //link_opts
|
||||||
// junk
|
if ((link_opts & HLINK_LABEL) != 0){
|
||||||
// int
|
size += 4; //link length
|
||||||
// url
|
size += label.length()*2;
|
||||||
return 4 + 4*2 + field_5_unknown.length +
|
}
|
||||||
3*4 + field_9_label.length()*2 +
|
if ((link_opts & HLINK_URL) != 0){
|
||||||
field_10_unknown.length + 4 +
|
size += moniker.length; //moniker length
|
||||||
field_12_url.length()*2;
|
if(Arrays.equals(URL_MONIKER, moniker)){
|
||||||
|
size += 4; //address length
|
||||||
|
size += address.length()*2;
|
||||||
|
size += tail.length;
|
||||||
|
} else if (Arrays.equals(FILE_MONIKER, moniker)){
|
||||||
|
size += 2; //file_opts
|
||||||
|
size += 4; //address length
|
||||||
|
size += address.length();
|
||||||
|
size += tail.length;
|
||||||
|
}
|
||||||
|
} else if((link_opts & HLINK_PLACE) != 0){
|
||||||
|
size += 4; //address length
|
||||||
|
size += address.length()*2;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
@ -300,71 +454,89 @@ public class HyperlinkRecord extends Record implements CellValueRecordInterface
|
|||||||
StringBuffer buffer = new StringBuffer();
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
|
||||||
buffer.append("[HYPERLINK RECORD]\n");
|
buffer.append("[HYPERLINK RECORD]\n");
|
||||||
buffer.append(" .row = ").append(Integer.toHexString(getRow())).append("\n");
|
buffer.append(" .rwFirst = ").append(Integer.toHexString(getFirstRow())).append("\n");
|
||||||
buffer.append(" .column = ").append(Integer.toHexString(getColumn())).append("\n");
|
buffer.append(" .rwLast = ").append(Integer.toHexString(getLastRow())).append("\n");
|
||||||
buffer.append(" .xfindex = ").append(Integer.toHexString(getXFIndex())).append("\n");
|
buffer.append(" .colFirst = ").append(Integer.toHexString(getFirstColumn())).append("\n");
|
||||||
buffer.append(" .label = ").append(field_9_label).append("\n");
|
buffer.append(" .colLast = ").append(Integer.toHexString(getLastColumn())).append("\n");
|
||||||
buffer.append(" .url = ").append(field_12_url).append("\n");
|
buffer.append(" .guid = ").append(HexDump.toHex(guid)).append("\n");
|
||||||
|
buffer.append(" .label_opts = ").append(label_opts).append("\n");
|
||||||
|
buffer.append(" .label = ").append(getLabel()).append("\n");
|
||||||
|
if((link_opts & HLINK_URL) != 0){
|
||||||
|
buffer.append(" .moniker = ").append(HexDump.toHex(moniker)).append("\n");
|
||||||
|
}
|
||||||
|
buffer.append(" .address = ").append(getAddress()).append("\n");
|
||||||
buffer.append("[/HYPERLINK RECORD]\n");
|
buffer.append("[/HYPERLINK RECORD]\n");
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the label.
|
* Initialize a new url link
|
||||||
*/
|
*/
|
||||||
public String getLabel()
|
public void newUrlLink(){
|
||||||
{
|
rwFirst = 0;
|
||||||
if(field_9_label.length() == 0) {
|
rwLast = 0;
|
||||||
return "";
|
colFirst = 0;
|
||||||
} else {
|
colLast = 0;
|
||||||
// Trim off \0
|
guid = STD_MONIKER;
|
||||||
return field_9_label.substring(0, field_9_label.length() - 1);
|
label_opts = 0x2;
|
||||||
}
|
link_opts = HLINK_URL | HLINK_ABS | HLINK_LABEL;
|
||||||
|
label = "" + '\u0000';
|
||||||
|
moniker = URL_MONIKER;
|
||||||
|
address = "" + '\u0000';
|
||||||
|
tail = URL_TAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param label The label to set.
|
* Initialize a new file link
|
||||||
*/
|
*/
|
||||||
public void setLabel(String label)
|
public void newFileLink(){
|
||||||
{
|
rwFirst = 0;
|
||||||
this.field_9_label = label + '\u0000';
|
rwLast = 0;
|
||||||
this.field_8_label_len = field_9_label.length();
|
colFirst = 0;
|
||||||
|
colLast = 0;
|
||||||
|
guid = STD_MONIKER;
|
||||||
|
label_opts = 0x2;
|
||||||
|
link_opts = HLINK_URL | HLINK_LABEL;
|
||||||
|
file_opts = 0;
|
||||||
|
label = "" + '\u0000';
|
||||||
|
moniker = FILE_MONIKER;
|
||||||
|
address = "" + '\0';
|
||||||
|
tail = FILE_TAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the Url.
|
* Initialize a new document link
|
||||||
*/
|
*/
|
||||||
public URL getUrl() throws MalformedURLException
|
public void newDocumentLink(){
|
||||||
{
|
rwFirst = 0;
|
||||||
return new URL(getUrlString());
|
rwLast = 0;
|
||||||
}
|
colFirst = 0;
|
||||||
public String getUrlString()
|
colLast = 0;
|
||||||
{
|
guid = STD_MONIKER;
|
||||||
if(field_12_url.length() == 0) {
|
label_opts = 0x2;
|
||||||
return "";
|
link_opts = HLINK_LABEL | HLINK_PLACE;
|
||||||
} else {
|
label = "" + '\u0000';
|
||||||
// Trim off \0
|
moniker = FILE_MONIKER;
|
||||||
return field_12_url.substring(0, field_12_url.length() - 1);
|
address = "" + '\0';
|
||||||
}
|
tail = new byte[]{};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Object clone() {
|
||||||
* @param url The url to set.
|
HyperlinkRecord rec = new HyperlinkRecord();
|
||||||
*/
|
rec.rwFirst = rwFirst;
|
||||||
public void setUrl(URL url)
|
rec.rwLast = rwLast;
|
||||||
{
|
rec.colFirst = colFirst;
|
||||||
setUrl(url.toString());
|
rec.colLast = colLast;
|
||||||
}
|
rec.guid = guid;
|
||||||
/**
|
rec.label_opts = label_opts;
|
||||||
* @param url The url to set.
|
rec.link_opts = link_opts;
|
||||||
*/
|
rec.file_opts = file_opts;
|
||||||
public void setUrl(String url)
|
rec.label = label;
|
||||||
{
|
rec.address = address;
|
||||||
this.field_12_url = url + '\u0000';
|
rec.moniker = moniker;
|
||||||
this.field_7_url_len = field_12_url.length();
|
rec.tail = tail;
|
||||||
|
return rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOptions(){
|
|
||||||
return field_11_url_opts;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -550,6 +550,13 @@ public class HSSFCell implements Cell
|
|||||||
/**
|
/**
|
||||||
* set a date value for the cell. Excel treats dates as numeric so you will need to format the cell as
|
* set a date value for the cell. Excel treats dates as numeric so you will need to format the cell as
|
||||||
* a date.
|
* a date.
|
||||||
|
*
|
||||||
|
* This will set the cell value based on the Calendar's timezone. As Excel
|
||||||
|
* does not support timezones this means that both 20:00+03:00 and
|
||||||
|
* 20:00-03:00 will be reported as the same value (20:00) even that there
|
||||||
|
* are 6 hours difference between the two times. This difference can be
|
||||||
|
* preserved by using <code>setCellValue(value.getTime())</code> which will
|
||||||
|
* automatically shift the times to the default timezone.
|
||||||
*
|
*
|
||||||
* @param value the date value to set this cell to. For formulas we'll set the
|
* @param value the date value to set this cell to. For formulas we'll set the
|
||||||
* precalculated value, for numerics we'll set its value. For othertypes we
|
* precalculated value, for numerics we'll set its value. For othertypes we
|
||||||
@ -557,7 +564,7 @@ public class HSSFCell implements Cell
|
|||||||
*/
|
*/
|
||||||
public void setCellValue(Calendar value)
|
public void setCellValue(Calendar value)
|
||||||
{
|
{
|
||||||
setCellValue(value.getTime());
|
setCellValue( HSSFDateUtil.getExcelDate(value, this.book.isUsing1904DateWindowing()) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1071,7 +1078,7 @@ public class HSSFCell implements Cell
|
|||||||
Record rec = ( Record ) it.next();
|
Record rec = ( Record ) it.next();
|
||||||
if (rec instanceof HyperlinkRecord){
|
if (rec instanceof HyperlinkRecord){
|
||||||
HyperlinkRecord link = (HyperlinkRecord)rec;
|
HyperlinkRecord link = (HyperlinkRecord)rec;
|
||||||
if(link.getColumn() == record.getColumn() && link.getRow() == record.getRow()){
|
if(link.getFirstColumn() == record.getColumn() && link.getFirstRow() == record.getRow()){
|
||||||
return new HSSFHyperlink(link);
|
return new HSSFHyperlink(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1085,6 +1092,25 @@ public class HSSFCell implements Cell
|
|||||||
* @param link hypelrink associated with this cell
|
* @param link hypelrink associated with this cell
|
||||||
*/
|
*/
|
||||||
public void setHyperlink(HSSFHyperlink link){
|
public void setHyperlink(HSSFHyperlink link){
|
||||||
|
link.setFirstRow(record.getRow());
|
||||||
|
link.setLastRow(record.getRow());
|
||||||
|
link.setFirstColumn(record.getColumn());
|
||||||
|
link.setLastColumn(record.getColumn());
|
||||||
|
|
||||||
|
switch(link.getType()){
|
||||||
|
case HSSFHyperlink.LINK_EMAIL:
|
||||||
|
case HSSFHyperlink.LINK_URL:
|
||||||
|
link.setLabel("url");
|
||||||
|
break;
|
||||||
|
case HSSFHyperlink.LINK_FILE:
|
||||||
|
link.setLabel("file");
|
||||||
|
break;
|
||||||
|
case HSSFHyperlink.LINK_DOCUMENT:
|
||||||
|
link.setLabel("place");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid );
|
||||||
|
sheet.getRecords().add( eofLoc, link.record );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -914,4 +914,29 @@ public class HSSFCellStyle implements CellStyle
|
|||||||
{
|
{
|
||||||
return format.getFillForeground();
|
return format.getFillForeground();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((format == null) ? 0 : format.hashCode());
|
||||||
|
result = prime * result + index;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (obj == null) return false;
|
||||||
|
if (obj instanceof HSSFCellStyle) {
|
||||||
|
final HSSFCellStyle other = (HSSFCellStyle) obj;
|
||||||
|
if (format == null) {
|
||||||
|
if (other.format != null)
|
||||||
|
return false;
|
||||||
|
} else if (!format.equals(other.format))
|
||||||
|
return false;
|
||||||
|
if (index != other.index)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,25 @@ public class HSSFDateUtil
|
|||||||
public static double getExcelDate(Date date, boolean use1904windowing) {
|
public static double getExcelDate(Date date, boolean use1904windowing) {
|
||||||
Calendar calStart = new GregorianCalendar();
|
Calendar calStart = new GregorianCalendar();
|
||||||
calStart.setTime(date); // If date includes hours, minutes, and seconds, set them to 0
|
calStart.setTime(date); // If date includes hours, minutes, and seconds, set them to 0
|
||||||
|
return internalGetExcelDate(calStart, use1904windowing);
|
||||||
if ((!use1904windowing && calStart.get(Calendar.YEAR) < 1900) ||
|
}
|
||||||
(use1904windowing && calStart.get(Calendar.YEAR) < 1904))
|
/**
|
||||||
|
* Given a Date in the form of a Calendar, converts it into a double
|
||||||
|
* representing its internal Excel representation, which is the
|
||||||
|
* number of days since 1/1/1900. Fractional days represent hours,
|
||||||
|
* minutes, and seconds.
|
||||||
|
*
|
||||||
|
* @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1)
|
||||||
|
* @param date the Calendar holding the date to convert
|
||||||
|
* @param use1904windowing Should 1900 or 1904 date windowing be used?
|
||||||
|
*/
|
||||||
|
public static double getExcelDate(Calendar date, boolean use1904windowing) {
|
||||||
|
// Don't alter the supplied Calendar as we do our work
|
||||||
|
return internalGetExcelDate( (Calendar)date.clone(), use1904windowing );
|
||||||
|
}
|
||||||
|
private static double internalGetExcelDate(Calendar date, boolean use1904windowing) {
|
||||||
|
if ((!use1904windowing && date.get(Calendar.YEAR) < 1900) ||
|
||||||
|
(use1904windowing && date.get(Calendar.YEAR) < 1904))
|
||||||
{
|
{
|
||||||
return BAD_DATE;
|
return BAD_DATE;
|
||||||
} else {
|
} else {
|
||||||
@ -83,12 +99,12 @@ public class HSSFDateUtil
|
|||||||
// be 4 hours.
|
// be 4 hours.
|
||||||
// E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
|
// E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours
|
||||||
// and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
|
// and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours
|
||||||
double fraction = (((calStart.get(Calendar.HOUR_OF_DAY) * 60
|
double fraction = (((date.get(Calendar.HOUR_OF_DAY) * 60
|
||||||
+ calStart.get(Calendar.MINUTE)
|
+ date.get(Calendar.MINUTE)
|
||||||
) * 60 + calStart.get(Calendar.SECOND)
|
) * 60 + date.get(Calendar.SECOND)
|
||||||
) * 1000 + calStart.get(Calendar.MILLISECOND)
|
) * 1000 + date.get(Calendar.MILLISECOND)
|
||||||
) / ( double ) DAY_MILLISECONDS;
|
) / ( double ) DAY_MILLISECONDS;
|
||||||
calStart = dayStart(calStart);
|
Calendar calStart = dayStart(date);
|
||||||
|
|
||||||
double value = fraction + absoluteDay(calStart, use1904windowing);
|
double value = fraction + absoluteDay(calStart, use1904windowing);
|
||||||
|
|
||||||
@ -208,9 +224,9 @@ public class HSSFDateUtil
|
|||||||
// who knows what that starting bit is all about
|
// who knows what that starting bit is all about
|
||||||
fs = fs.replaceAll("\\[\\$\\-.*?\\]", "");
|
fs = fs.replaceAll("\\[\\$\\-.*?\\]", "");
|
||||||
|
|
||||||
// Otherwise, check it's only made up of:
|
// Otherwise, check it's only made up, in any case, of:
|
||||||
// y m d - / ,
|
// y m d h s - / , . :
|
||||||
if(fs.matches("^[ymd\\-/, ]+$")) {
|
if(fs.matches("^[yYmMdDhHsS\\-/,. :]+$")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,5 +306,28 @@ public class HSSFFont implements Font
|
|||||||
"}";
|
"}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((font == null) ? 0 : font.hashCode());
|
||||||
|
result = prime * result + index;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (obj == null) return false;
|
||||||
|
if (obj instanceof HSSFFont) {
|
||||||
|
final HSSFFont other = (HSSFFont) obj;
|
||||||
|
if (font == null) {
|
||||||
|
if (other.font != null)
|
||||||
|
return false;
|
||||||
|
} else if (!font.equals(other.font))
|
||||||
|
return false;
|
||||||
|
if (index != other.index)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,9 @@ import java.util.List;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a hyperlink.
|
* Represents an Excel hyperlink.
|
||||||
*
|
*
|
||||||
* @author Yegor Kozlov
|
* @author Yegor Kozlov (yegor at apache dot org)
|
||||||
*/
|
*/
|
||||||
public class HSSFHyperlink {
|
public class HSSFHyperlink {
|
||||||
|
|
||||||
@ -49,67 +49,145 @@ public class HSSFHyperlink {
|
|||||||
public static final int LINK_EMAIL = 3;
|
public static final int LINK_EMAIL = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unknown type
|
* Link to a file
|
||||||
*/
|
*/
|
||||||
public static final int LINK_UNKNOWN = 4;
|
public static final int LINK_FILE = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low-level record object that stores the actual hyperlink data
|
* Low-level record object that stores the actual hyperlink data
|
||||||
*/
|
*/
|
||||||
private HyperlinkRecord record = null;
|
protected HyperlinkRecord record = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we create a new hypelrink remember its type
|
||||||
|
*/
|
||||||
|
protected int link_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new hyperlink
|
||||||
|
*
|
||||||
|
* @param type the type of hyperlink to create
|
||||||
|
*/
|
||||||
|
public HSSFHyperlink( int type )
|
||||||
|
{
|
||||||
|
this.link_type = type;
|
||||||
|
record = new HyperlinkRecord();
|
||||||
|
switch(type){
|
||||||
|
case LINK_URL:
|
||||||
|
case LINK_EMAIL:
|
||||||
|
record.newUrlLink();
|
||||||
|
break;
|
||||||
|
case LINK_FILE:
|
||||||
|
record.newFileLink();
|
||||||
|
break;
|
||||||
|
case LINK_DOCUMENT:
|
||||||
|
record.newDocumentLink();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the hyperlink by a <code>HyperlinkRecord</code> record
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
*/
|
||||||
protected HSSFHyperlink( HyperlinkRecord record )
|
protected HSSFHyperlink( HyperlinkRecord record )
|
||||||
{
|
{
|
||||||
this.record = record;
|
this.record = record;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the row of the cell that contains the hyperlink
|
* Return the row of the first cell that contains the hyperlink
|
||||||
*
|
*
|
||||||
* @return the 0-based row of the cell that contains the hyperlink
|
* @return the 0-based row of the cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public int getRow(){
|
public int getFirstRow(){
|
||||||
return record.getRow();
|
return record.getFirstRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the row of the cell that contains the hyperlink
|
* Set the row of the first cell that contains the hyperlink
|
||||||
*
|
*
|
||||||
* @param row the 0-based row of the cell that contains the hyperlink
|
* @param row the 0-based row of the first cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public void setRow(int row){
|
public void setFirstRow(int row){
|
||||||
record.setRow(row);
|
record.setFirstRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the column of the cell that contains the hyperlink
|
* Return the row of the last cell that contains the hyperlink
|
||||||
*
|
*
|
||||||
* @return the 0-based column of the cell that contains the hyperlink
|
* @return the 0-based row of the last cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public short getColumn(){
|
public int getLastRow(){
|
||||||
return record.getColumn();
|
return record.getLastRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the column of the cell that contains the hyperlink
|
* Set the row of the last cell that contains the hyperlink
|
||||||
*
|
*
|
||||||
* @param col the 0-based column of the cell that contains the hyperlink
|
* @param row the 0-based row of the last cell that contains the hyperlink
|
||||||
*/
|
*/
|
||||||
public void setColumn(short col){
|
public void setLastRow(int row){
|
||||||
record.setColumn(col);
|
record.setLastRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hypelink address. Depending on the hyperlink type it can be URL, e-mail, etc.
|
* Return the column of the first cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @return the 0-based column of the first cell that contains the hyperlink
|
||||||
|
*/
|
||||||
|
public short getFirstColumn(){
|
||||||
|
return record.getFirstColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the column of the first cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @param col the 0-based column of the first cell that contains the hyperlink
|
||||||
|
*/
|
||||||
|
public void setFirstColumn(short col){
|
||||||
|
record.setFirstColumn(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the column of the last cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @return the 0-based column of the last cell that contains the hyperlink
|
||||||
|
*/
|
||||||
|
public short getLastColumn(){
|
||||||
|
return record.getLastColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the column of the last cell that contains the hyperlink
|
||||||
|
*
|
||||||
|
* @param col the 0-based column of the last cell that contains the hyperlink
|
||||||
|
*/
|
||||||
|
public void setLastColumn(short col){
|
||||||
|
record.setLastColumn(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hypelink address. Depending on the hyperlink type it can be URL, e-mail, patrh to a file, etc.
|
||||||
*
|
*
|
||||||
* @return the address of this hyperlink
|
* @return the address of this hyperlink
|
||||||
*/
|
*/
|
||||||
public String getAddress(){
|
public String getAddress(){
|
||||||
return record.getUrlString();
|
return record.getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return text to display for this hyperlink
|
* Hypelink address. Depending on the hyperlink type it can be URL, e-mail, patrh to a file, etc.
|
||||||
|
*
|
||||||
|
* @param address the address of this hyperlink
|
||||||
|
*/
|
||||||
|
public void setAddress(String address){
|
||||||
|
record.setAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return text label for this hyperlink
|
||||||
*
|
*
|
||||||
* @return text to display
|
* @return text to display
|
||||||
*/
|
*/
|
||||||
@ -117,12 +195,21 @@ public class HSSFHyperlink {
|
|||||||
return record.getLabel();
|
return record.getLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets text label for this hyperlink
|
||||||
|
*
|
||||||
|
* @param label text label for this hyperlink
|
||||||
|
*/
|
||||||
|
public void setLabel(String label){
|
||||||
|
record.setLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the type of this hyperlink
|
* Return the type of this hyperlink
|
||||||
*
|
*
|
||||||
* @return the type of this hyperlink
|
* @return the type of this hyperlink
|
||||||
*/
|
*/
|
||||||
public int getType(){
|
protected int getType(){
|
||||||
throw new RuntimeException("Not implemented");
|
return link_type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,7 +471,6 @@ public class HSSFRow
|
|||||||
* @return cell iterator of the physically defined cells. Note element 4 may
|
* @return cell iterator of the physically defined cells. Note element 4 may
|
||||||
* actually be row cell depending on how many are defined!
|
* actually be row cell depending on how many are defined!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Iterator cellIterator()
|
public Iterator cellIterator()
|
||||||
{
|
{
|
||||||
return new CellIterator();
|
return new CellIterator();
|
||||||
@ -481,7 +480,7 @@ public class HSSFRow
|
|||||||
* foreach loops
|
* foreach loops
|
||||||
*/
|
*/
|
||||||
public Iterator iterator() {
|
public Iterator iterator() {
|
||||||
return cellIterator();
|
return cellIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CellIterator implements Iterator
|
private class CellIterator implements Iterator
|
||||||
|
@ -724,8 +724,9 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
|
|||||||
* foreach loops
|
* foreach loops
|
||||||
*/
|
*/
|
||||||
public Iterator iterator() {
|
public Iterator iterator() {
|
||||||
return rowIterator();
|
return rowIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* used internally in the API to get the low level Sheet record represented by this
|
* used internally in the API to get the low level Sheet record represented by this
|
||||||
|
Binary file not shown.
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.hwpf.extractor;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for bugs with the WordExtractor
|
||||||
|
*
|
||||||
|
* @author Nick Burch (nick at torchbox dot com)
|
||||||
|
*/
|
||||||
|
public class TestWordExtractorBugs extends TestCase {
|
||||||
|
private String dirname;
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
dirname = System.getProperty("HWPF.testdata.path");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testProblemMetadata() throws Exception {
|
||||||
|
String filename = dirname + "/ProblemExtracting.doc";
|
||||||
|
WordExtractor extractor =
|
||||||
|
new WordExtractor(new FileInputStream(filename));
|
||||||
|
|
||||||
|
// Check it gives text without error
|
||||||
|
extractor.getText();
|
||||||
|
extractor.getParagraphText();
|
||||||
|
extractor.getTextFromPieces();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,7 +20,6 @@ package org.apache.poi.hpsf.basic;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -227,46 +226,6 @@ public class TestBasic extends TestCase
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>This test methods reads all property set streams from all POI
|
|
||||||
* filesystems in the "data" directory.</p>
|
|
||||||
*/
|
|
||||||
public void testReadAllFiles()
|
|
||||||
{
|
|
||||||
final File dataDir =
|
|
||||||
new File(System.getProperty("HPSF.testdata.path"));
|
|
||||||
final File[] fileList = dataDir.listFiles(new FileFilter()
|
|
||||||
{
|
|
||||||
public boolean accept(final File f)
|
|
||||||
{
|
|
||||||
return f.isFile();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (int i = 0; i < fileList.length; i++)
|
|
||||||
{
|
|
||||||
File f = fileList[i];
|
|
||||||
/* Read the POI filesystem's property set streams: */
|
|
||||||
final POIFile[] psf1 = Util.readPropertySets(f);
|
|
||||||
|
|
||||||
for (int j = 0; j < psf1.length; j++)
|
|
||||||
{
|
|
||||||
final InputStream in =
|
|
||||||
new ByteArrayInputStream(psf1[j].getBytes());
|
|
||||||
PropertySetFactory.create(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
final String s = org.apache.poi.hpsf.Util.toString(t);
|
|
||||||
fail(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Runs the test cases stand-alone.</p>
|
* <p>Runs the test cases stand-alone.</p>
|
||||||
*
|
*
|
||||||
|
110
src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java
Normal file
110
src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.hpsf.basic;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.poi.hpsf.PropertySetFactory;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Tests some HPSF functionality by reading all property sets from all files
|
||||||
|
* in the "data" directory. If you want to ensure HPSF can deal with a certain
|
||||||
|
* OLE2 file, just add it to the "data" directory and run this test case.</p>
|
||||||
|
*
|
||||||
|
* @author Rainer Klute (klute@rainer-klute.de)
|
||||||
|
* @since 2008-02-08
|
||||||
|
* @version $Id: TestBasic.java 489730 2006-12-22 19:18:16Z bayard $
|
||||||
|
*/
|
||||||
|
public class TestReadAllFiles extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Test case constructor.</p>
|
||||||
|
*
|
||||||
|
* @param name The test case's name.
|
||||||
|
*/
|
||||||
|
public TestReadAllFiles(final String name)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This test methods reads all property set streams from all POI
|
||||||
|
* filesystems in the "data" directory.</p>
|
||||||
|
*/
|
||||||
|
public void testReadAllFiles()
|
||||||
|
{
|
||||||
|
final File dataDir =
|
||||||
|
new File(System.getProperty("HPSF.testdata.path"));
|
||||||
|
final File[] fileList = dataDir.listFiles(new FileFilter()
|
||||||
|
{
|
||||||
|
public boolean accept(final File f)
|
||||||
|
{
|
||||||
|
return f.isFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fileList.length; i++)
|
||||||
|
{
|
||||||
|
final File f = fileList[i];
|
||||||
|
/* Read the POI filesystem's property set streams: */
|
||||||
|
final POIFile[] psf1 = Util.readPropertySets(f);
|
||||||
|
|
||||||
|
for (int j = 0; j < psf1.length; j++)
|
||||||
|
{
|
||||||
|
final InputStream in =
|
||||||
|
new ByteArrayInputStream(psf1[j].getBytes());
|
||||||
|
PropertySetFactory.create(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
final String s = org.apache.poi.hpsf.Util.toString(t);
|
||||||
|
fail(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Runs the test cases stand-alone.</p>
|
||||||
|
*
|
||||||
|
* @param args Command-line arguments (ignored)
|
||||||
|
*
|
||||||
|
* @exception Throwable if any sort of exception or error occurs
|
||||||
|
*/
|
||||||
|
public static void main(final String[] args) throws Throwable
|
||||||
|
{
|
||||||
|
System.setProperty("HPSF.testdata.path",
|
||||||
|
"./src/testcases/org/apache/poi/hpsf/data");
|
||||||
|
junit.textui.TestRunner.run(TestReadAllFiles.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
src/testcases/org/apache/poi/hpsf/data/TestBug44375.xls
Normal file
BIN
src/testcases/org/apache/poi/hpsf/data/TestBug44375.xls
Normal file
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/42564-2.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/42564-2.xls
Normal file
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/42564.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/42564.xls
Normal file
Binary file not shown.
@ -18,114 +18,311 @@ package org.apache.poi.hssf.record;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test HyperlinkRecord
|
||||||
|
*
|
||||||
|
* @author Nick Burch
|
||||||
|
* @author Yegor Kozlov
|
||||||
|
*/
|
||||||
public class TestHyperlinkRecord extends TestCase {
|
public class TestHyperlinkRecord extends TestCase {
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] data = new byte[] {
|
//link to http://www.lakings.com/
|
||||||
-72, 1, 110, 0,
|
byte[] data1 = { 0x02, 0x00, //First row of the hyperlink
|
||||||
// ??, Row, col, xf
|
0x02, 0x00, //Last row of the hyperlink
|
||||||
6, 0, 3, 0, 2, 0, 2, 0,
|
0x00, 0x00, //First column of the hyperlink
|
||||||
|
0x00, 0x00, //Last column of the hyperlink
|
||||||
// ??
|
|
||||||
-48, -55, -22, 121, -7, -70, -50, 17,
|
|
||||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
|
||||||
2, 0, 0, 0,
|
|
||||||
|
|
||||||
// URL length
|
|
||||||
23, 0, 0, 0,
|
|
||||||
|
|
||||||
// Label length
|
|
||||||
4, 0, 0, 0,
|
|
||||||
|
|
||||||
// Label
|
|
||||||
76, 0, 44, 0, 65, 0, 0, 0,
|
|
||||||
|
|
||||||
// ??
|
|
||||||
-32, -55, -22, 121, -7, -70, -50, 17,
|
|
||||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
|
||||||
46, 0, 0, 0,
|
|
||||||
|
|
||||||
// URL
|
|
||||||
104, 0, 116, 0, 116, 0, 112, 0, 58, 0, 47, 0, 47, 0, 119,
|
|
||||||
0, 119, 0, 119, 0, 46, 0, 108, 0, 97, 0, 107, 0, 105,
|
|
||||||
0, 110, 0, 103, 0, 115, 0, 46, 0, 99, 0, 111, 0,
|
|
||||||
109, 0,
|
|
||||||
0, 0 };
|
|
||||||
|
|
||||||
private byte[] data2 = new byte[] {
|
|
||||||
-72, 1, -126, 0,
|
|
||||||
// ??, Row, col, xf
|
|
||||||
2, 0, 2, 0, 4, 0, 4, 0,
|
|
||||||
|
|
||||||
// ??
|
//16-byte GUID. Seems to be always the same. Does not depend on the hyperlink type
|
||||||
-48, -55, -22, 121, -7, -70, -50, 17,
|
(byte)0xD0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B,
|
||||||
2, 0, 0, 0,
|
|
||||||
|
|
||||||
// URL and Label lengths
|
|
||||||
23, 0, 0, 0,
|
|
||||||
15, 0, 0, 0,
|
|
||||||
|
|
||||||
// Label
|
0x02, 0x00, 0x00, 0x00, //integer, always 2
|
||||||
83, 0, 116, 0, 97, 0, 99, 0, 105, 0,
|
|
||||||
101, 0, 64, 0, 65, 0, 66, 0, 67, 0,
|
|
||||||
46, 0, 99, 0, 111, 0, 109, 0, 0, 0,
|
|
||||||
|
|
||||||
// ??
|
// flags. Define the type of the hyperlink:
|
||||||
-32, -55, -22, 121, -7, -70, -50, 17,
|
// HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_ABS | HyperlinkRecord.HLINK_LABEL
|
||||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
0x17, 0x00, 0x00, 0x00,
|
||||||
44, 0, 0, 0,
|
|
||||||
|
|
||||||
// URL
|
0x08, 0x00, 0x00, 0x00, //length of the label including the trailing '\0'
|
||||||
109, 0, 97, 0, 105, 0, 108, 0, 116, 0,
|
|
||||||
111, 0, 58, 0, 83, 0, 116, 0, 97, 0,
|
|
||||||
99, 0, 105, 0, 101, 0, 64, 0, 65, 0,
|
|
||||||
66, 0, 67, 0, 46, 0, 99, 0, 111, 0,
|
|
||||||
109, 0, 0, 0 };
|
|
||||||
|
|
||||||
public void testRecordParsing() throws Exception {
|
//label:
|
||||||
RecordInputStream inp = new RecordInputStream(
|
0x4D, 0x00, 0x79, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6B, 0x00, 0x00, 0x00,
|
||||||
new ByteArrayInputStream(data)
|
|
||||||
);
|
|
||||||
inp.nextRecord();
|
|
||||||
|
|
||||||
HyperlinkRecord r = new HyperlinkRecord(inp);
|
//16-byte link moniker: HyperlinkRecord.URL_MONIKER
|
||||||
|
(byte)0xE0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
assertEquals(3, r.getRow());
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B,
|
||||||
assertEquals(2, r.getColumn());
|
|
||||||
assertEquals(2, r.getXFIndex());
|
//count of bytes in the address including the tail
|
||||||
|
0x48, 0x00, 0x00, 0x00, //integer
|
||||||
assertEquals("L,A", r.getLabel());
|
|
||||||
assertEquals("http://www.lakings.com", r.getUrlString());
|
//the actual link, terminated by '\u0000'
|
||||||
assertEquals(new URL("http://www.lakings.com"), r.getUrl());
|
0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3A, 0x00, 0x2F, 0x00,
|
||||||
|
0x2F, 0x00, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x2E, 0x00, 0x6C, 0x00,
|
||||||
// Check it serialises as expected
|
0x61, 0x00, 0x6B, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x73, 0x00,
|
||||||
assertEquals(data.length, r.getRecordSize());
|
0x2E, 0x00, 0x63, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x2F, 0x00, 0x00, 0x00,
|
||||||
byte[] d = r.serialize();
|
|
||||||
assertEquals(data.length, d.length);
|
//standard 24-byte tail of a URL link. Seems to always be the same for all URL HLINKs
|
||||||
for(int i=0; i<data.length; i++) {
|
0x79, 0x58, (byte)0x81, (byte)0xF4, 0x3B, 0x1D, 0x7F, 0x48, (byte)0xAF, 0x2C,
|
||||||
assertEquals(data[i], d[i]);
|
(byte)0x82, 0x5D, (byte)0xC4, (byte)0x85, 0x27, 0x63, 0x00, 0x00, 0x00,
|
||||||
|
0x00, (byte)0xA5, (byte)0xAB, 0x00, 0x00};
|
||||||
|
|
||||||
|
//link to a file in the current directory: link1.xls
|
||||||
|
byte[] data2 = {0x00, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
//16-bit GUID. Seems to be always the same. Does not depend on the hyperlink type
|
||||||
|
(byte)0xD0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B,
|
||||||
|
|
||||||
|
0x02, 0x00, 0x00, 0x00, //integer, always 2
|
||||||
|
|
||||||
|
0x15, 0x00, 0x00, 0x00, //options: HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_LABEL
|
||||||
|
|
||||||
|
0x05, 0x00, 0x00, 0x00, //length of the label
|
||||||
|
//label
|
||||||
|
0x66, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00,
|
||||||
|
|
||||||
|
//16-byte link moniker: HyperlinkRecord.FILE_MONIKER
|
||||||
|
0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
|
||||||
|
|
||||||
|
0x00, 0x00, //level
|
||||||
|
0x0A, 0x00, 0x00, 0x00, //length of the path )
|
||||||
|
|
||||||
|
//path to the file (plain ISO-8859 bytes, NOT UTF-16LE!)
|
||||||
|
0x6C, 0x69, 0x6E, 0x6B, 0x31, 0x2E, 0x78, 0x6C, 0x73, 0x00,
|
||||||
|
|
||||||
|
//standard 28-byte tail of a file link
|
||||||
|
(byte)0xFF, (byte)0xFF, (byte)0xAD, (byte)0xDE, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
// mailto:ebgans@mail.ru?subject=Hello,%20Ebgans!
|
||||||
|
byte[] data3 = {0x01, 0x00,
|
||||||
|
0x01, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
|
||||||
|
//16-bit GUID. Seems to be always the same. Does not depend on the hyperlink type
|
||||||
|
(byte)0xD0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B,
|
||||||
|
|
||||||
|
0x02, 0x00, 0x00, 0x00, //integer, always 2
|
||||||
|
|
||||||
|
0x17, 0x00, 0x00, 0x00, //options: HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_ABS | HyperlinkRecord.HLINK_LABEL
|
||||||
|
|
||||||
|
0x06, 0x00, 0x00, 0x00, //length of the label
|
||||||
|
0x65, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x00, 0x00, //label
|
||||||
|
|
||||||
|
//16-byte link moniker: HyperlinkRecord.URL_MONIKER
|
||||||
|
(byte)0xE0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B,
|
||||||
|
|
||||||
|
//length of the address including the tail.
|
||||||
|
0x76, 0x00, 0x00, 0x00,
|
||||||
|
|
||||||
|
//the address is terminated by '\u0000'
|
||||||
|
0x6D, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x74, 0x00, 0x6F, 0x00,
|
||||||
|
0x3A, 0x00, 0x65, 0x00, 0x62, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6E, 0x00,
|
||||||
|
0x73, 0x00, 0x40, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6C, 0x00,
|
||||||
|
0x2E, 0x00, 0x72, 0x00, 0x75, 0x00, 0x3F, 0x00, 0x73, 0x00, 0x75, 0x00,
|
||||||
|
0x62, 0x00, 0x6A, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3D, 0x00,
|
||||||
|
0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x2C, 0x00,
|
||||||
|
0x25, 0x00, 0x32, 0x00, 0x30, 0x00, 0x45, 0x00, 0x62, 0x00, 0x67, 0x00,
|
||||||
|
0x61, 0x00, 0x6E, 0x00, 0x73, 0x00, 0x21, 0x00, 0x00, 0x00,
|
||||||
|
|
||||||
|
//standard 24-byte tail of a URL link
|
||||||
|
0x79, 0x58, (byte)0x81, (byte)0xF4, 0x3B, 0x1D, 0x7F, 0x48, (byte)0xAF, (byte)0x2C,
|
||||||
|
(byte)0x82, 0x5D, (byte)0xC4, (byte)0x85, 0x27, 0x63, 0x00, 0x00, 0x00,
|
||||||
|
0x00, (byte)0xA5, (byte)0xAB, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
//link to a place in worksheet: Sheet1!A1
|
||||||
|
byte[] data4 = {0x03, 0x00,
|
||||||
|
0x03, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
|
||||||
|
//16-bit GUID. Seems to be always the same. Does not depend on the hyperlink type
|
||||||
|
(byte)0xD0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
|
||||||
|
(byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B,
|
||||||
|
|
||||||
|
0x02, 0x00, 0x00, 0x00, //integer, always 2
|
||||||
|
|
||||||
|
0x1C, 0x00, 0x00, 0x00, //flags: HyperlinkRecord.HLINK_LABEL | HyperlinkRecord.HLINK_PLACE
|
||||||
|
|
||||||
|
0x06, 0x00, 0x00, 0x00, //length of the label
|
||||||
|
|
||||||
|
0x70, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x00, 0x00, //label
|
||||||
|
|
||||||
|
0x0A, 0x00, 0x00, 0x00, //length of the document link including trailing zero
|
||||||
|
|
||||||
|
//link: Sheet1!A1
|
||||||
|
0x53, 0x00, 0x68, 0x00, 0x65, 0x00, 0x65, 0x00, 0x74, 0x00, 0x31, 0x00, 0x21,
|
||||||
|
0x00, 0x41, 0x00, 0x31, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
public void testReadURLLink(){
|
||||||
|
RecordInputStream is = new TestcaseRecordInputStream((short)HyperlinkRecord.sid, (short)data1.length, data1);
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord(is);
|
||||||
|
assertEquals(2, link.getFirstRow());
|
||||||
|
assertEquals(2, link.getLastRow());
|
||||||
|
assertEquals(0, link.getFirstColumn());
|
||||||
|
assertEquals(0, link.getLastColumn());
|
||||||
|
assertTrue(Arrays.equals(HyperlinkRecord.STD_MONIKER, link.getGuid()));
|
||||||
|
assertTrue(Arrays.equals(HyperlinkRecord.URL_MONIKER, link.getMoniker()));
|
||||||
|
assertEquals(2, link.getLabelOptions());
|
||||||
|
int opts = HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_ABS | HyperlinkRecord.HLINK_LABEL;
|
||||||
|
assertEquals(0x17, opts);
|
||||||
|
assertEquals(opts, link.getLinkOptions());
|
||||||
|
assertEquals(0, link.getFileOptions());
|
||||||
|
|
||||||
|
assertEquals("My Link", link.getLabel());
|
||||||
|
assertEquals("http://www.lakings.com/", link.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReadFileLink(){
|
||||||
|
RecordInputStream is = new TestcaseRecordInputStream((short)HyperlinkRecord.sid, (short)data2.length, data2);
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord(is);
|
||||||
|
assertEquals(0, link.getFirstRow());
|
||||||
|
assertEquals(0, link.getLastRow());
|
||||||
|
assertEquals(0, link.getFirstColumn());
|
||||||
|
assertEquals(0, link.getLastColumn());
|
||||||
|
assertTrue(Arrays.equals(HyperlinkRecord.STD_MONIKER, link.getGuid()));
|
||||||
|
assertTrue(Arrays.equals(HyperlinkRecord.FILE_MONIKER, link.getMoniker()));
|
||||||
|
assertEquals(2, link.getLabelOptions());
|
||||||
|
int opts = HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_LABEL;
|
||||||
|
assertEquals(0x15, opts);
|
||||||
|
assertEquals(opts, link.getLinkOptions());
|
||||||
|
|
||||||
|
assertEquals("file", link.getLabel());
|
||||||
|
assertEquals("link1.xls", link.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReadEmailLink(){
|
||||||
|
RecordInputStream is = new TestcaseRecordInputStream((short)HyperlinkRecord.sid, (short)data3.length, data3);
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord(is);
|
||||||
|
assertEquals(1, link.getFirstRow());
|
||||||
|
assertEquals(1, link.getLastRow());
|
||||||
|
assertEquals(0, link.getFirstColumn());
|
||||||
|
assertEquals(0, link.getLastColumn());
|
||||||
|
assertTrue(Arrays.equals(HyperlinkRecord.STD_MONIKER, link.getGuid()));
|
||||||
|
assertTrue(Arrays.equals(HyperlinkRecord.URL_MONIKER, link.getMoniker()));
|
||||||
|
assertEquals(2, link.getLabelOptions());
|
||||||
|
int opts = HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_ABS | HyperlinkRecord.HLINK_LABEL;
|
||||||
|
assertEquals(0x17, opts);
|
||||||
|
assertEquals(opts, link.getLinkOptions());
|
||||||
|
|
||||||
|
assertEquals("email", link.getLabel());
|
||||||
|
assertEquals("mailto:ebgans@mail.ru?subject=Hello,%20Ebgans!", link.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReadDocumentLink(){
|
||||||
|
RecordInputStream is = new TestcaseRecordInputStream((short)HyperlinkRecord.sid, (short)data4.length, data4);
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord(is);
|
||||||
|
assertEquals(3, link.getFirstRow());
|
||||||
|
assertEquals(3, link.getLastRow());
|
||||||
|
assertEquals(0, link.getFirstColumn());
|
||||||
|
assertEquals(0, link.getLastColumn());
|
||||||
|
assertTrue(Arrays.equals(HyperlinkRecord.STD_MONIKER, link.getGuid()));
|
||||||
|
assertEquals(2, link.getLabelOptions());
|
||||||
|
int opts = HyperlinkRecord.HLINK_LABEL | HyperlinkRecord.HLINK_PLACE;
|
||||||
|
assertEquals(0x1C, opts);
|
||||||
|
assertEquals(opts, link.getLinkOptions());
|
||||||
|
|
||||||
|
assertEquals("place", link.getLabel());
|
||||||
|
assertEquals("Sheet1!A1", link.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void serialize(byte[] data){
|
||||||
|
RecordInputStream is = new TestcaseRecordInputStream((short)HyperlinkRecord.sid, (short)data.length, data);
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord(is);
|
||||||
|
byte[] bytes1 = link.serialize();
|
||||||
|
is = new RecordInputStream(new ByteArrayInputStream(bytes1));
|
||||||
|
is.nextRecord();
|
||||||
|
link = new HyperlinkRecord(is);
|
||||||
|
byte[] bytes2 = link.serialize();
|
||||||
|
assertEquals(bytes1.length, bytes2.length);
|
||||||
|
assertTrue(Arrays.equals(bytes1, bytes2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSerialize(){
|
||||||
|
serialize(data1);
|
||||||
|
serialize(data2);
|
||||||
|
serialize(data3);
|
||||||
|
serialize(data4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateURLRecord() throws Exception {
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord();
|
||||||
|
link.newUrlLink();
|
||||||
|
link.setFirstRow((short)2);
|
||||||
|
link.setLastRow((short)2);
|
||||||
|
link.setLabel("My Link");
|
||||||
|
link.setAddress("http://www.lakings.com/");
|
||||||
|
|
||||||
|
byte[] tmp = link.serialize();
|
||||||
|
byte[] ser = new byte[tmp.length-4];
|
||||||
|
System.arraycopy(tmp, 4, ser, 0, ser.length);
|
||||||
|
assertEquals(data1.length, ser.length);
|
||||||
|
assertTrue(Arrays.equals(data1, ser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateFileRecord() throws Exception {
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord();
|
||||||
|
link.newFileLink();
|
||||||
|
link.setFirstRow((short)0);
|
||||||
|
link.setLastRow((short)0);
|
||||||
|
link.setLabel("file");
|
||||||
|
link.setAddress("link1.xls");
|
||||||
|
|
||||||
|
byte[] tmp = link.serialize();
|
||||||
|
byte[] ser = new byte[tmp.length-4];
|
||||||
|
System.arraycopy(tmp, 4, ser, 0, ser.length);
|
||||||
|
assertEquals(data2.length, ser.length);
|
||||||
|
assertTrue(Arrays.equals(data2, ser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateDocumentRecord() throws Exception {
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord();
|
||||||
|
link.newDocumentLink();
|
||||||
|
link.setFirstRow((short)3);
|
||||||
|
link.setLastRow((short)3);
|
||||||
|
link.setLabel("place");
|
||||||
|
link.setAddress("Sheet1!A1");
|
||||||
|
|
||||||
|
byte[] tmp = link.serialize();
|
||||||
|
byte[] ser = new byte[tmp.length-4];
|
||||||
|
System.arraycopy(tmp, 4, ser, 0, ser.length);
|
||||||
|
assertEquals(data4.length, ser.length);
|
||||||
|
assertTrue(Arrays.equals(data4, ser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateEmailtRecord() throws Exception {
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord();
|
||||||
|
link.newUrlLink();
|
||||||
|
link.setFirstRow((short)1);
|
||||||
|
link.setLastRow((short)1);
|
||||||
|
link.setLabel("email");
|
||||||
|
link.setAddress("mailto:ebgans@mail.ru?subject=Hello,%20Ebgans!");
|
||||||
|
|
||||||
|
byte[] tmp = link.serialize();
|
||||||
|
byte[] ser = new byte[tmp.length-4];
|
||||||
|
System.arraycopy(tmp, 4, ser, 0, ser.length);
|
||||||
|
assertEquals(data3.length, ser.length);
|
||||||
|
assertTrue(Arrays.equals(data3, ser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testClone() throws Exception {
|
||||||
|
byte[][] data = {data1, data2, data3, data4};
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
RecordInputStream is = new TestcaseRecordInputStream((short)HyperlinkRecord.sid, (short)data[i].length, data[i]);
|
||||||
|
HyperlinkRecord link = new HyperlinkRecord(is);
|
||||||
|
HyperlinkRecord clone = (HyperlinkRecord)link.clone();
|
||||||
|
assertTrue(Arrays.equals(link.serialize(), clone.serialize()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void testSecondRecord() throws Exception {
|
}
|
||||||
RecordInputStream inp = new RecordInputStream(
|
|
||||||
new ByteArrayInputStream(data2)
|
|
||||||
);
|
|
||||||
inp.nextRecord();
|
|
||||||
|
|
||||||
HyperlinkRecord r = new HyperlinkRecord(inp);
|
|
||||||
|
|
||||||
assertEquals(2, r.getRow());
|
|
||||||
assertEquals(4, r.getColumn());
|
|
||||||
assertEquals(4, r.getXFIndex());
|
|
||||||
|
|
||||||
assertEquals("Stacie@ABC.com", r.getLabel());
|
|
||||||
assertEquals("mailto:Stacie@ABC.com", r.getUrlString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1010,6 +1010,48 @@ extends TestCase {
|
|||||||
assertTrue("No Exceptions while reading file", true);
|
assertTrue("No Exceptions while reading file", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bug 42564: Some files from Access were giving a RecordFormatException
|
||||||
|
* when reading the BOFRecord
|
||||||
|
*/
|
||||||
|
public void test42564() throws Exception {
|
||||||
|
FileInputStream in = new FileInputStream(new File(cwd, "42564.xls"));
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook(in);
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
assertTrue("No Exceptions while reading file", true);
|
||||||
|
|
||||||
|
//serialize and read again
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
wb.write(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
|
||||||
|
assertTrue("No Exceptions while reading file", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bug 42564: Some files from Access also have issues
|
||||||
|
* with the NameRecord, once you get past the BOFRecord
|
||||||
|
* issue.
|
||||||
|
* TODO - still broken
|
||||||
|
*/
|
||||||
|
public void DISABLEDtest42564Alt() throws Exception {
|
||||||
|
FileInputStream in = new FileInputStream(new File(cwd, "42564-2.xls"));
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook(in);
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
assertTrue("No Exceptions while reading file", true);
|
||||||
|
|
||||||
|
//serialize and read again
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
wb.write(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
|
||||||
|
assertTrue("No Exceptions while reading file", true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bug 42618: RecordFormatException reading a file containing
|
* Bug 42618: RecordFormatException reading a file containing
|
||||||
* =CHOOSE(2,A2,A3,A4)
|
* =CHOOSE(2,A2,A3,A4)
|
||||||
|
@ -137,6 +137,35 @@ public class TestCellStyle
|
|||||||
assertEquals("FIRST ROW ", 0, s.getFirstRowNum());
|
assertEquals("FIRST ROW ", 0, s.getFirstRowNum());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testHashEquals() {
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFSheet s = wb.createSheet();
|
||||||
|
HSSFCellStyle cs1 = wb.createCellStyle();
|
||||||
|
HSSFCellStyle cs2 = wb.createCellStyle();
|
||||||
|
HSSFRow row = s.createRow((short)0);
|
||||||
|
HSSFCell cell1 = row.createCell((short)1);
|
||||||
|
HSSFCell cell2 = row.createCell((short)2);
|
||||||
|
|
||||||
|
cs1.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy"));
|
||||||
|
cs2.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/dd/yy"));
|
||||||
|
|
||||||
|
cell1.setCellStyle(cs1);
|
||||||
|
cell1.setCellValue(new Date());
|
||||||
|
|
||||||
|
cell2.setCellStyle(cs2);
|
||||||
|
cell2.setCellValue(new Date());
|
||||||
|
|
||||||
|
assertEquals(cs1.hashCode(), cs1.hashCode());
|
||||||
|
assertEquals(cs2.hashCode(), cs2.hashCode());
|
||||||
|
assertTrue(cs1.equals(cs1));
|
||||||
|
assertTrue(cs2.equals(cs2));
|
||||||
|
|
||||||
|
// Change cs1, hash will alter
|
||||||
|
int hash1 = cs1.hashCode();
|
||||||
|
cs1.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/dd/yy"));
|
||||||
|
assertFalse(hash1 == cs1.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TEST NAME: Test Write Sheet Style <P>
|
* TEST NAME: Test Write Sheet Style <P>
|
||||||
|
@ -320,8 +320,8 @@ extends TestCase {
|
|||||||
|
|
||||||
assertEquals("Foo", link.getLabel());
|
assertEquals("Foo", link.getLabel());
|
||||||
assertEquals("http://poi.apache.org/", link.getAddress());
|
assertEquals("http://poi.apache.org/", link.getAddress());
|
||||||
assertEquals(4, link.getRow());
|
assertEquals(4, link.getFirstRow());
|
||||||
assertEquals(0, link.getColumn());
|
assertEquals(0, link.getFirstColumn());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -339,16 +339,16 @@ extends TestCase {
|
|||||||
assertNotNull(link1);
|
assertNotNull(link1);
|
||||||
assertEquals("Foo", link1.getLabel());
|
assertEquals("Foo", link1.getLabel());
|
||||||
assertEquals("http://poi.apache.org/", link1.getAddress());
|
assertEquals("http://poi.apache.org/", link1.getAddress());
|
||||||
assertEquals(4, link1.getRow());
|
assertEquals(4, link1.getFirstRow());
|
||||||
assertEquals(0, link1.getColumn());
|
assertEquals(0, link1.getFirstColumn());
|
||||||
|
|
||||||
HSSFCell cell2 = sheet.getRow(8).getCell((short)1);
|
HSSFCell cell2 = sheet.getRow(8).getCell((short)1);
|
||||||
HSSFHyperlink link2 = cell2.getHyperlink();
|
HSSFHyperlink link2 = cell2.getHyperlink();
|
||||||
assertNotNull(link2);
|
assertNotNull(link2);
|
||||||
assertEquals("Bar", link2.getLabel());
|
assertEquals("Bar", link2.getLabel());
|
||||||
assertEquals("http://poi.apache.org/", link2.getAddress());
|
assertEquals("http://poi.apache.org/hssf/", link2.getAddress());
|
||||||
assertEquals(8, link2.getRow());
|
assertEquals(8, link2.getFirstRow());
|
||||||
assertEquals(1, link2.getColumn());
|
assertEquals(1, link2.getFirstColumn());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,6 +196,29 @@ public class TestHSSFDateUtil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that we deal with timezones properly
|
||||||
|
*/
|
||||||
|
public void testCalendarConversion() {
|
||||||
|
GregorianCalendar date = new GregorianCalendar(2002, 0, 1, 12, 1, 1);
|
||||||
|
Date expected = date.getTime();
|
||||||
|
double expectedExcel = HSSFDateUtil.getExcelDate(expected);
|
||||||
|
|
||||||
|
// Iteratating over the hours exposes any rounding issues.
|
||||||
|
for (int hour = -12; hour <= 12; hour++)
|
||||||
|
{
|
||||||
|
String id = "GMT" + (hour < 0 ? "" : "+") + hour + ":00";
|
||||||
|
date.setTimeZone(TimeZone.getTimeZone(id));
|
||||||
|
date.set(Calendar.HOUR_OF_DAY, 12);
|
||||||
|
double excelDate = HSSFDateUtil.getExcelDate(date, false);
|
||||||
|
Date javaDate = HSSFDateUtil.getJavaDate(excelDate);
|
||||||
|
|
||||||
|
// Should match despite timezone
|
||||||
|
assertEquals("Checking timezone " + id, expected.getTime(), javaDate.getTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that we correctly detect date formats as such
|
* Tests that we correctly detect date formats as such
|
||||||
*/
|
*/
|
||||||
@ -228,6 +251,7 @@ public class TestHSSFDateUtil
|
|||||||
"yyyy-mm-dd", "yyyy/mm/dd", "yy/mm/dd", "yy/mmm/dd",
|
"yyyy-mm-dd", "yyyy/mm/dd", "yy/mm/dd", "yy/mmm/dd",
|
||||||
"dd/mm/yy", "dd/mm/yyyy", "dd/mmm/yy",
|
"dd/mm/yy", "dd/mm/yyyy", "dd/mmm/yy",
|
||||||
"dd-mm-yy", "dd-mm-yyyy",
|
"dd-mm-yy", "dd-mm-yyyy",
|
||||||
|
"DD-MM-YY", "DD-mm-YYYY",
|
||||||
"dd\\-mm\\-yy", // Sometimes escaped
|
"dd\\-mm\\-yy", // Sometimes escaped
|
||||||
|
|
||||||
// These crazy ones are valid
|
// These crazy ones are valid
|
||||||
@ -242,9 +266,18 @@ public class TestHSSFDateUtil
|
|||||||
assertTrue( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
assertTrue( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then time based ones too
|
||||||
|
formats = new String[] {
|
||||||
|
"yyyy-mm-dd hh:mm:ss", "yyyy/mm/dd HH:MM:SS",
|
||||||
|
"mm/dd HH:MM", "yy/mmm/dd SS",
|
||||||
|
};
|
||||||
|
for(int i=0; i<formats.length; i++) {
|
||||||
|
assertTrue( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
||||||
|
}
|
||||||
|
|
||||||
// Then invalid ones
|
// Then invalid ones
|
||||||
formats = new String[] {
|
formats = new String[] {
|
||||||
"yyyy:mm:dd",
|
"yyyy*mm*dd",
|
||||||
"0.0", "0.000",
|
"0.0", "0.000",
|
||||||
"0%", "0.0%",
|
"0%", "0.0%",
|
||||||
"", null
|
"", null
|
||||||
@ -252,6 +285,15 @@ public class TestHSSFDateUtil
|
|||||||
for(int i=0; i<formats.length; i++) {
|
for(int i=0; i<formats.length; i++) {
|
||||||
assertFalse( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
assertFalse( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And these are ones we probably shouldn't allow,
|
||||||
|
// but would need a better regexp
|
||||||
|
formats = new String[] {
|
||||||
|
"yyyy:mm:dd",
|
||||||
|
};
|
||||||
|
for(int i=0; i<formats.length; i++) {
|
||||||
|
// assertFalse( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
191
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHyperlink.java
Executable file
191
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHyperlink.java
Executable file
@ -0,0 +1,191 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests HSSFHyperlink.
|
||||||
|
*
|
||||||
|
* @author Yegor Kozlov
|
||||||
|
*/
|
||||||
|
public class TestHSSFHyperlink extends TestCase {
|
||||||
|
protected String cwd = System.getProperty("HSSF.testdata.path");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that we can read hyperlinks.
|
||||||
|
*/
|
||||||
|
public void testRead() throws Exception {
|
||||||
|
|
||||||
|
FileInputStream is = new FileInputStream(new File(cwd, "HyperlinksOnManySheets.xls"));
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook(is);
|
||||||
|
is.close();
|
||||||
|
|
||||||
|
HSSFSheet sheet;
|
||||||
|
HSSFCell cell;
|
||||||
|
HSSFHyperlink link;
|
||||||
|
|
||||||
|
sheet = wb.getSheet("WebLinks");
|
||||||
|
cell = sheet.getRow(4).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("POI", link.getLabel());
|
||||||
|
assertEquals("POI", cell.getRichStringCellValue().getString());
|
||||||
|
assertEquals("http://poi.apache.org/", link.getAddress());
|
||||||
|
|
||||||
|
cell = sheet.getRow(8).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("HSSF", link.getLabel());
|
||||||
|
assertEquals("HSSF", cell.getRichStringCellValue().getString());
|
||||||
|
assertEquals("http://poi.apache.org/hssf/", link.getAddress());
|
||||||
|
|
||||||
|
sheet = wb.getSheet("Emails");
|
||||||
|
cell = sheet.getRow(4).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("dev", link.getLabel());
|
||||||
|
assertEquals("dev", cell.getRichStringCellValue().getString());
|
||||||
|
assertEquals("mailto:dev@poi.apache.org", link.getAddress());
|
||||||
|
|
||||||
|
sheet = wb.getSheet("Internal");
|
||||||
|
cell = sheet.getRow(4).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("Link To First Sheet", link.getLabel());
|
||||||
|
assertEquals("Link To First Sheet", cell.getRichStringCellValue().getString());
|
||||||
|
assertEquals("WebLinks!A1", link.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testModify() throws Exception {
|
||||||
|
FileInputStream is = new FileInputStream(new File(cwd, "HyperlinksOnManySheets.xls"));
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook(is);
|
||||||
|
is.close();
|
||||||
|
|
||||||
|
HSSFSheet sheet;
|
||||||
|
HSSFCell cell;
|
||||||
|
HSSFHyperlink link;
|
||||||
|
|
||||||
|
sheet = wb.getSheet("WebLinks");
|
||||||
|
cell = sheet.getRow(4).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
//modify the link
|
||||||
|
link.setAddress("www.apache.org");
|
||||||
|
|
||||||
|
//serialize and read again
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
wb.write(out);
|
||||||
|
|
||||||
|
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
|
||||||
|
sheet = wb.getSheet("WebLinks");
|
||||||
|
cell = sheet.getRow(4).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("www.apache.org", link.getAddress());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreate() throws Exception {
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//serialize and read again
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
wb.write(out);
|
||||||
|
|
||||||
|
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
|
||||||
|
sheet = wb.getSheet("Hyperlinks");
|
||||||
|
cell = sheet.getRow(0).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("http://poi.apache.org/", link.getAddress());
|
||||||
|
|
||||||
|
cell = sheet.getRow(1).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("link1.xls", link.getAddress());
|
||||||
|
|
||||||
|
cell = sheet.getRow(2).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("mailto:poi@apache.org?subject=Hyperlinks", link.getAddress());
|
||||||
|
|
||||||
|
cell = sheet.getRow(3).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("'Target Sheet'!A1", link.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCloneSheet() throws Exception {
|
||||||
|
FileInputStream is = new FileInputStream(new File(cwd, "HyperlinksOnManySheets.xls"));
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook(is);
|
||||||
|
is.close();
|
||||||
|
|
||||||
|
HSSFCell cell;
|
||||||
|
HSSFHyperlink link;
|
||||||
|
|
||||||
|
HSSFSheet sheet = wb.cloneSheet(0);
|
||||||
|
|
||||||
|
cell = sheet.getRow(4).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("http://poi.apache.org/", link.getAddress());
|
||||||
|
|
||||||
|
cell = sheet.getRow(8).getCell((short)0);
|
||||||
|
link = cell.getHyperlink();
|
||||||
|
assertNotNull(link);
|
||||||
|
assertEquals("http://poi.apache.org/hssf/", link.getAddress());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user