support for excel hypelrinks

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@619310 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-02-07 08:56:59 +00:00
parent 142936990c
commit ab1c14b324
10 changed files with 1205 additions and 375 deletions

View File

@ -36,6 +36,7 @@
<!-- 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="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="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>

View File

@ -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>
@ -1322,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>

View File

@ -33,6 +33,7 @@
<!-- 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="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="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>

View 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();
}
}

View File

@ -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();
colLast = in.readShort();
// Some sort of opts // 16-byte GUID
field_6_label_opts = in.readInt(); guid = new byte[16];
in.read(guid);
// Now for lengths, in characters label_opts = in.readInt();
field_7_url_len = in.readInt(); link_opts = in.readInt();
field_8_label_len = in.readInt();
// Now we have the label, as little endian unicode, if ((link_opts & HLINK_LABEL) != 0){
// with a trailing \0 int label_len = in.readInt();
field_9_label = in.readUnicodeLEString(field_8_label_len); label = in.readUnicodeLEString(label_len);
}
// Next up is some more data we can't make sense of if ((link_opts & HLINK_URL) != 0){
field_10_unknown = new byte[16]; moniker = new byte[16];
try { in.read(moniker);
in.read(field_10_unknown);
} catch(IOException e) { throw new IllegalStateException(e.getMessage()); }
// Might need to nudge the length by one byte if(Arrays.equals(URL_MONIKER, moniker)){
// This is an empirical hack! int len = in.readInt();
field_11_url_opts = in.readInt();
if(field_11_url_opts == 44) { address = in.readUnicodeLEString(len/2);
field_7_url_len--;
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; System.arraycopy(guid, 0, data, pos, guid.length); pos += guid.length;
for(int i=0; i<field_5_unknown.length; i++) {
data[offset] = field_5_unknown[i]; LittleEndian.putInt(data, pos, label_opts); pos += 4;
offset++; 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){
LittleEndian.putInt(data, offset, field_6_label_opts); System.arraycopy(moniker, 0, data, pos, moniker.length); pos += moniker.length;
offset += 4; if(Arrays.equals(URL_MONIKER, moniker)){
LittleEndian.putInt(data, offset, field_7_url_len); LittleEndian.putInt(data, pos, address.length()*2 + tail.length); pos += 4;
offset += 4; StringUtil.putUnicodeLE(address, data, pos); pos += address.length()*2;
LittleEndian.putInt(data, offset, field_8_label_len); if(tail.length > 0){
offset += 4; System.arraycopy(tail, 0, data, pos, tail.length); pos += tail.length;
StringUtil.putUnicodeLE(field_9_label, data, offset); }
offset += field_9_label.length()*2; } else if (Arrays.equals(FILE_MONIKER, moniker)){
LittleEndian.putShort(data, pos, file_opts); pos += 2;
for(int i=0; i<field_10_unknown.length; i++) { LittleEndian.putInt(data, pos, address.length()); pos += 4;
data[offset] = field_10_unknown[i]; byte[] bytes = address.getBytes();
offset++; 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;
}
} }

View File

@ -1066,7 +1066,7 @@ public class HSSFCell
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);
} }
} }
@ -1080,6 +1080,25 @@ public class HSSFCell
* @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 );
} }
} }

View File

@ -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;
} }
} }

View File

@ -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
// ?? //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 length 0x02, 0x00, 0x00, 0x00, //integer, always 2
23, 0, 0, 0,
// Label length // flags. Define the type of the hyperlink:
4, 0, 0, 0, // HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_ABS | HyperlinkRecord.HLINK_LABEL
0x17, 0x00, 0x00, 0x00,
// Label 0x08, 0x00, 0x00, 0x00, //length of the label including the trailing '\0'
76, 0, 44, 0, 65, 0, 0, 0,
// ?? //label:
-32, -55, -22, 121, -7, -70, -50, 17, 0x4D, 0x00, 0x79, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6B, 0x00, 0x00, 0x00,
-116, -126, 0, -86, 0, 75, -87, 11,
46, 0, 0, 0,
// URL //16-byte link moniker: HyperlinkRecord.URL_MONIKER
104, 0, 116, 0, 116, 0, 112, 0, 58, 0, 47, 0, 47, 0, 119, (byte)0xE0, (byte)0xC9, (byte)0xEA, 0x79, (byte)0xF9, (byte)0xBA, (byte)0xCE, 0x11,
0, 119, 0, 119, 0, 46, 0, 108, 0, 97, 0, 107, 0, 105, (byte)0x8C, (byte)0x82, 0x00, (byte)0xAA, 0x00, 0x4B, (byte)0xA9, 0x0B,
0, 110, 0, 103, 0, 115, 0, 46, 0, 99, 0, 111, 0,
109, 0,
0, 0 };
private byte[] data2 = new byte[] { //count of bytes in the address including the tail
-72, 1, -126, 0, 0x48, 0x00, 0x00, 0x00, //integer
// ??, Row, col, xf
2, 0, 2, 0, 4, 0, 4, 0,
// ?? //the actual link, terminated by '\u0000'
-48, -55, -22, 121, -7, -70, -50, 17, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3A, 0x00, 0x2F, 0x00,
-116, -126, 0, -86, 0, 75, -87, 11, 0x2F, 0x00, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x2E, 0x00, 0x6C, 0x00,
2, 0, 0, 0, 0x61, 0x00, 0x6B, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x73, 0x00,
0x2E, 0x00, 0x63, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x2F, 0x00, 0x00, 0x00,
// URL and Label lengths //standard 24-byte tail of a URL link. Seems to always be the same for all URL HLINKs
23, 0, 0, 0, 0x79, 0x58, (byte)0x81, (byte)0xF4, 0x3B, 0x1D, 0x7F, 0x48, (byte)0xAF, 0x2C,
15, 0, 0, 0, (byte)0x82, 0x5D, (byte)0xC4, (byte)0x85, 0x27, 0x63, 0x00, 0x00, 0x00,
0x00, (byte)0xA5, (byte)0xAB, 0x00, 0x00};
// Label //link to a file in the current directory: link1.xls
83, 0, 116, 0, 97, 0, 99, 0, 105, 0, byte[] data2 = {0x00, 0x00,
101, 0, 64, 0, 65, 0, 66, 0, 67, 0, 0x00, 0x00,
46, 0, 99, 0, 111, 0, 109, 0, 0, 0, 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
-32, -55, -22, 121, -7, -70, -50, 17,
-116, -126, 0, -86, 0, 75, -87, 11,
44, 0, 0, 0,
// URL 0x15, 0x00, 0x00, 0x00, //options: HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_LABEL
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 { 0x05, 0x00, 0x00, 0x00, //length of the label
RecordInputStream inp = new RecordInputStream( //label
new ByteArrayInputStream(data) 0x66, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00,
);
inp.nextRecord();
HyperlinkRecord r = new HyperlinkRecord(inp); //16-byte link moniker: HyperlinkRecord.FILE_MONIKER
0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
assertEquals(3, r.getRow()); 0x00, 0x00, //level
assertEquals(2, r.getColumn()); 0x0A, 0x00, 0x00, 0x00, //length of the path )
assertEquals(2, r.getXFIndex());
assertEquals("L,A", r.getLabel()); //path to the file (plain ISO-8859 bytes, NOT UTF-16LE!)
assertEquals("http://www.lakings.com", r.getUrlString()); 0x6C, 0x69, 0x6E, 0x6B, 0x31, 0x2E, 0x78, 0x6C, 0x73, 0x00,
assertEquals(new URL("http://www.lakings.com"), r.getUrl());
// Check it serialises as expected //standard 28-byte tail of a file link
assertEquals(data.length, r.getRecordSize()); (byte)0xFF, (byte)0xFF, (byte)0xAD, (byte)0xDE, 0x00, 0x00, 0x00, 0x00,
byte[] d = r.serialize(); 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
assertEquals(data.length, d.length); 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
for(int i=0; i<data.length; i++) {
assertEquals(data[i], d[i]); // 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());
}
} }

View File

@ -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());
} }

View 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());
}
}