]> source.dussan.org Git - poi.git/commitdiff
support for excel hypelrinks
authorYegor Kozlov <yegor@apache.org>
Thu, 7 Feb 2008 08:56:59 +0000 (08:56 +0000)
committerYegor Kozlov <yegor@apache.org>
Thu, 7 Feb 2008 08:56:59 +0000 (08:56 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@619310 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/hssf/quick-guide.xml
src/documentation/content/xdocs/status.xml
src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java [new file with mode: 0755]
src/java/org/apache/poi/hssf/record/HyperlinkRecord.java
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java
src/testcases/org/apache/poi/hssf/record/TestHyperlinkRecord.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHyperlink.java [new file with mode: 0755]

index d2430ca19ec6e0a56235859d2d507ece85a08c6d..2b017fee7b3927cc973950fdc0443a1dbf80c4b3 100644 (file)
@@ -36,6 +36,7 @@
 
                <!-- Don't forget to update status.xml too! -->
         <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="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>
index 69850afe88920777845d608f71989228a8e8a18d..66da60489237b7d74d36238ddbc91ca4b33d5d54 100644 (file)
@@ -69,6 +69,7 @@
                     <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="#Autofit">How to adjust column width to fit the contents</link></li>
+                    <li><link href="#Hyperlinks">Hyperlinks</link></li>
                 </ul>
             </section>
             <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>).
         </warning>
      </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>
 </document>
index 8d43546e19f5fe0b4e498fc1d68f834aa14147c3..feab74a973b8a4334e516642c58814695c0a27b0 100644 (file)
@@ -33,6 +33,7 @@
        <!-- Don't forget to update changes.xml too! -->
     <changes>
         <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="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>
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/Hyperlinks.java
new file mode 100755 (executable)
index 0000000..24b3f18
--- /dev/null
@@ -0,0 +1,91 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.usermodel.examples;\r
+\r
+import org.apache.poi.hssf.usermodel.*;\r
+import org.apache.poi.hssf.util.HSSFColor;\r
+\r
+import java.io.IOException;\r
+import java.io.FileOutputStream;\r
+\r
+/**\r
+ * Demonstrates how to create hyperlinks.\r
+ *\r
+ * @author Yegor Kozlov (yegor at apach.org)\r
+ */\r
+public class Hyperlinks {\r
+\r
+    public static void main(String[] args) throws IOException  {\r
+        HSSFWorkbook wb = new HSSFWorkbook();\r
+\r
+        //cell style for hyperlinks\r
+        //by default hypelrinks are blue and underlined\r
+        HSSFCellStyle hlink_style = wb.createCellStyle();\r
+        HSSFFont hlink_font = wb.createFont();\r
+        hlink_font.setUnderline(HSSFFont.U_SINGLE);\r
+        hlink_font.setColor(HSSFColor.BLUE.index);\r
+        hlink_style.setFont(hlink_font);\r
+\r
+        HSSFCell cell;\r
+        HSSFSheet sheet = wb.createSheet("Hyperlinks");\r
+\r
+        //URL\r
+        cell = sheet.createRow(0).createCell((short)0);\r
+        cell.setCellValue("URL Link");\r
+        HSSFHyperlink link = new HSSFHyperlink(HSSFHyperlink.LINK_URL);\r
+        link.setAddress("http://poi.apache.org/");\r
+        cell.setHyperlink(link);\r
+        cell.setCellStyle(hlink_style);\r
+\r
+        //link to a file in the current directory\r
+        cell = sheet.createRow(1).createCell((short)0);\r
+        cell.setCellValue("File Link");\r
+        link = new HSSFHyperlink(HSSFHyperlink.LINK_FILE);\r
+        link.setAddress("link1.xls");\r
+        cell.setHyperlink(link);\r
+        cell.setCellStyle(hlink_style);\r
+\r
+        //e-mail link\r
+        cell = sheet.createRow(2).createCell((short)0);\r
+        cell.setCellValue("Email Link");\r
+        link = new HSSFHyperlink(HSSFHyperlink.LINK_EMAIL);\r
+        //note, if subject contains white spaces, make sure they are url-encoded\r
+        link.setAddress("mailto:poi@apache.org?subject=Hyperlinks");\r
+        cell.setHyperlink(link);\r
+        cell.setCellStyle(hlink_style);\r
+\r
+        //link to a place in this workbook\r
+\r
+        //create a target sheet and cell\r
+        HSSFSheet sheet2 = wb.createSheet("Target Sheet");\r
+        sheet2.createRow(0).createCell((short)0).setCellValue("Target Cell");\r
+\r
+        cell = sheet.createRow(3).createCell((short)0);\r
+        cell.setCellValue("Worksheet Link");\r
+        link = new HSSFHyperlink(HSSFHyperlink.LINK_DOCUMENT);\r
+        link.setAddress("'Target Sheet'!A1");\r
+        cell.setHyperlink(link);\r
+        cell.setCellStyle(hlink_style);\r
+\r
+        FileOutputStream out = new FileOutputStream("hssf-links.xls");\r
+        wb.write(out);\r
+        out.close();\r
+\r
+    }\r
+}\r
index 0dcd45a7248cb79ad2234327b6eefc20a396f49e..798d4e1ff544827c450fb624514323201a439fe5 100644 (file)
@@ -1,29 +1,27 @@
 /* ====================================================================
- Copyright 2002-2004   Apache Software Foundation
-
- Licensed 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.
- ==================================================================== */
+   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.record;
 
 import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import java.util.Arrays;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
+import org.apache.poi.util.HexDump;
 
 /**
  * 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://) 
  *
  * @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
-{
-    /** Indicates the URL in the Record */
-    private static byte[] GUID_OF_URL_MONIKER =
-    { -32, -55, -22, 121, -7, -70, -50, 17, -116, -126, 0, -86, 0, 75, -87, 11 };
+public class HyperlinkRecord extends Record {
+    /**
+     * Link flags
+     */
+    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 */
-    public static final Log log = LogFactory.getLog(HyperlinkRecord.class);
+    protected final static byte[] STD_MONIKER = {(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 };
+    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;
 
-    private short field_1_unknown;
-    private int field_2_row;
-    private short field_3_column;
-    private short field_4_xf_index;
-    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 */
+    /**
+     * First row of the hyperlink
+     */
+    private int rwFirst;
+
+    /**
+     * 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()
     {
+
     }
 
-    /** Real Constructor */
+    /**
+     * Read hyperlink from input stream
+     *
+     * @param in the stream to read from
+     */
     public HyperlinkRecord(RecordInputStream 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
+     *
+     * @return the 0-based column of the last cell that contains the hyperlink
+    */
+    public short getLastColumn()
+    {
+        return colLast;
+    }
+
+    /**
+     * 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 short getXFIndex()
+    public void setLastColumn(short col)
     {
-        return field_4_xf_index;
+        this.colLast = col;
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.poi.hssf.record.CellValueRecordInterface#isAfter(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 isAfter(CellValueRecordInterface i)
+    public int getFirstRow()
     {
-        if (this.getRow() < i.getRow())
-        {
-            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;
+        return rwFirst;
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.poi.hssf.record.CellValueRecordInterface#isBefore(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 isBefore(CellValueRecordInterface i)
+    public void setFirstRow(int row)
     {
-        if (this.getRow() > i.getRow())
-        {
-            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;
+        this.rwFirst = row;
+    }
+
+    /**
+     * 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 int getLastRow()
+    {
+        return rwLast;
+    }
+
+    /**
+     * 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 setLastRow(int row)
+    {
+        this.rwLast = row;
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.poi.hssf.record.CellValueRecordInterface#isEqual(org.apache.poi.hssf.record.CellValueRecordInterface)
+    /**
+     * Returns a 16-byte guid identifier. Seems to always equal {@link STD_MONIKER}
+     *
+     * @return 16-byte guid identifier
      */
-    public boolean isEqual(CellValueRecordInterface i)
+    public byte[] getGuid()
     {
-        return ((this.getRow() == i.getRow()) && (this.getColumn() == i.getColumn()));
+        return guid;
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.poi.hssf.record.CellValueRecordInterface#setColumn(short)
+    /**
+     * Returns a 16-byte moniker.
+     *
+     * @return 16-byte moniker
      */
-    public void setColumn(short col)
+    public byte[] getMoniker()
     {
-        this.field_3_column = col;
+        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);
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.poi.hssf.record.CellValueRecordInterface#setRow(int)
+    /**
+     * Sets text label for this hyperlink
+     *
+     * @param label text label for this hyperlink
      */
-    public void setRow(int row)
+     public void setLabel(String label)
     {
-        this.field_2_row = row;
+        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);
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.poi.hssf.record.CellValueRecordInterface#setXFIndex(short)
+    /**
+     * 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 setXFIndex(short xf)
+    public void setAddress(String address)
     {
-        this.field_4_xf_index = xf;
+        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)
     {
-//     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 {
-        in.read(field_5_unknown);
-        } catch(IOException e) { throw new IllegalStateException(e.getMessage()); }
-        
-        // Some sort of opts
-        field_6_label_opts = in.readInt();
-        
-        // Now for lengths, in characters
-        field_7_url_len = in.readInt();
-        field_8_label_len = in.readInt();
-        
-        // Now we have the label, as little endian unicode,
-        //  with a trailing \0
-        field_9_label = in.readUnicodeLEString(field_8_label_len);
-        
-        // Next up is some more data we can't make sense of
-        field_10_unknown = new byte[16];
         try {
-        in.read(field_10_unknown);
-        } catch(IOException e) { throw new IllegalStateException(e.getMessage()); }
-        
-        // Might need to nudge the length by one byte
-        // This is an empirical hack!
-        field_11_url_opts = in.readInt();
-        if(field_11_url_opts == 44) {
-               field_7_url_len--;
+            rwFirst = in.readShort();
+            rwLast = in.readUShort();
+            colFirst = in.readShort();
+            colLast = in.readShort();
+
+            // 16-byte GUID
+            guid = new byte[16];
+            in.read(guid);
+
+            label_opts = in.readInt();
+            link_opts = in.readInt();
+
+            if ((link_opts & HLINK_LABEL) != 0){
+                int label_len = in.readInt();
+                label = in.readUnicodeLEString(label_len);
+            }
+
+            if ((link_opts & HLINK_URL) != 0){
+                moniker = new byte[16];
+                in.read(moniker);
+
+                if(Arrays.equals(URL_MONIKER, moniker)){
+                    int len = in.readInt();
+
+                    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()
     {
         return HyperlinkRecord.sid;
@@ -244,55 +378,75 @@ public class HyperlinkRecord extends Record implements CellValueRecordInterface
 
     public int serialize(int offset, byte[] data)
     {
-        LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset,
-                              ( short )(getRecordSize()-4));
-        LittleEndian.putShort(data, 4 + offset, field_1_unknown);
-        LittleEndian.putUShort(data, 6 + offset, field_2_row);
-        LittleEndian.putShort(data, 8 + offset, field_3_column);
-        LittleEndian.putShort(data, 10 + offset, field_4_xf_index);
-        
-        offset += 12;
-        for(int i=0; i<field_5_unknown.length; i++) {
-               data[offset] = field_5_unknown[i];
-               offset++;
+        int pos = offset;
+        LittleEndian.putShort(data, pos, sid); pos += 2;
+        LittleEndian.putShort(data, pos, ( short )(getRecordSize()-4)); pos += 2;
+        LittleEndian.putUShort(data, pos, rwFirst); pos += 2;
+        LittleEndian.putUShort(data, pos, rwLast); pos += 2;
+        LittleEndian.putShort(data, pos, colFirst); pos += 2;
+        LittleEndian.putShort(data, pos, colLast); pos += 2;
+
+        System.arraycopy(guid, 0, data, pos, guid.length); pos += guid.length;
+
+        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;
         }
-        
-        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++) {
-               data[offset] = field_10_unknown[i];
-               offset++;
+        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();
     }
 
     public int getRecordSize()
     {
-       // We have:
-       // 4 shorts
-       // junk
-       // 3 ints
-       // label
-       // junk
-       // int
-       // url
-       return 4 + 4*2 + field_5_unknown.length +
-               3*4 + field_9_label.length()*2 +
-               field_10_unknown.length + 4 +
-               field_12_url.length()*2;
+        int size = 4;
+        size += 2 + 2 + 2 + 2;  //rwFirst, rwLast, colFirst, colLast
+        size += guid.length;
+        size += 4;  //label_opts
+        size += 4;  //link_opts
+        if ((link_opts & HLINK_LABEL) != 0){
+            size += 4;  //link length
+            size += label.length()*2;
+        }
+        if ((link_opts & HLINK_URL) != 0){
+            size += moniker.length;  //moniker length
+            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()
@@ -300,71 +454,89 @@ public class HyperlinkRecord extends Record implements CellValueRecordInterface
         StringBuffer buffer = new StringBuffer();
 
         buffer.append("[HYPERLINK RECORD]\n");
-        buffer.append("    .row            = ").append(Integer.toHexString(getRow())).append("\n");
-        buffer.append("    .column         = ").append(Integer.toHexString(getColumn())).append("\n");
-        buffer.append("    .xfindex        = ").append(Integer.toHexString(getXFIndex())).append("\n");
-        buffer.append("    .label          = ").append(field_9_label).append("\n");
-        buffer.append("    .url            = ").append(field_12_url).append("\n");
+        buffer.append("    .rwFirst            = ").append(Integer.toHexString(getFirstRow())).append("\n");
+        buffer.append("    .rwLast         = ").append(Integer.toHexString(getLastRow())).append("\n");
+        buffer.append("    .colFirst            = ").append(Integer.toHexString(getFirstColumn())).append("\n");
+        buffer.append("    .colLast         = ").append(Integer.toHexString(getLastColumn())).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");
         return buffer.toString();
     }
 
     /**
-     * @return Returns the label.
+     * Initialize a new url link
      */
-    public String getLabel()
-    {
-       if(field_9_label.length() == 0) {
-               return "";
-       } else {
-               // Trim off \0
-            return field_9_label.substring(0, field_9_label.length() - 1);
-       }
+    public void newUrlLink(){
+        rwFirst = 0;
+        rwLast = 0;
+        colFirst = 0;
+        colLast = 0;
+        guid = STD_MONIKER;
+        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)
-    {
-        this.field_9_label = label + '\u0000';
-        this.field_8_label_len = field_9_label.length();
+    public void newFileLink(){
+        rwFirst = 0;
+        rwLast = 0;
+        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
-    {
-        return new URL(getUrlString());
-    }
-    public String getUrlString()
-    {
-       if(field_12_url.length() == 0) {
-               return "";
-       } else {
-               // Trim off \0
-            return field_12_url.substring(0, field_12_url.length() - 1);
-       }
+    public void newDocumentLink(){
+        rwFirst = 0;
+        rwLast = 0;
+        colFirst = 0;
+        colLast = 0;
+        guid = STD_MONIKER;
+        label_opts = 0x2;
+        link_opts = HLINK_LABEL | HLINK_PLACE;
+        label = "" + '\u0000';
+        moniker = FILE_MONIKER;
+        address = "" + '\0';
+        tail = new byte[]{};
     }
 
-    /**
-     * @param url The url to set.
-     */
-    public void setUrl(URL url)
-    {
-       setUrl(url.toString());
-    }
-    /**
-     * @param url The url to set.
-     */
-    public void setUrl(String url)
-    {
-        this.field_12_url = url + '\u0000';
-        this.field_7_url_len = field_12_url.length();
+    public Object clone() {
+        HyperlinkRecord rec = new HyperlinkRecord();
+        rec.rwFirst = rwFirst;
+        rec.rwLast = rwLast;
+        rec.colFirst = colFirst;
+        rec.colLast = colLast;
+        rec.guid = guid;
+        rec.label_opts = label_opts;
+        rec.link_opts = link_opts;
+        rec.file_opts = file_opts;
+        rec.label = label;
+        rec.address = address;
+        rec.moniker = moniker;
+        rec.tail = tail;
+        return rec;
     }
 
-    public int getOptions(){
-        return field_11_url_opts;
-    }
+
 }
index 7b6102b69de1bd978ffc46b240ff2f64d5000789..646efe3103443add8c6f01f6e784cff84096247f 100644 (file)
@@ -1066,7 +1066,7 @@ public class HSSFCell
             Record rec = ( Record ) it.next();
             if (rec instanceof HyperlinkRecord){
                 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);
                 }
             }
@@ -1080,6 +1080,25 @@ public class HSSFCell
      * @param link hypelrink associated with this cell
      */
     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 );
     }
 }
index e1bd28af6cb90820e35673be534cb21248a3fa11..7f1c2639c9d488dd3b629e82147d90eaf92cc340 100755 (executable)
@@ -27,9 +27,9 @@ import java.util.List;
 import java.util.Iterator;\r
 \r
 /**\r
- * Represents a hyperlink.\r
+ * Represents an Excel hyperlink.\r
  *\r
- * @author Yegor Kozlov\r
+ * @author Yegor Kozlov (yegor at apache dot org)\r
  */\r
 public class HSSFHyperlink {\r
 \r
@@ -49,67 +49,145 @@ public class HSSFHyperlink {
     public static final int LINK_EMAIL = 3;\r
 \r
     /**\r
-     * Unknown type\r
+     * Link to a file\r
      */\r
-    public static final int LINK_UNKNOWN = 4;\r
+    public static final int LINK_FILE = 4;\r
 \r
     /**\r
      * Low-level record object that stores the actual hyperlink data\r
      */\r
-    private HyperlinkRecord record = null;\r
+    protected HyperlinkRecord record = null;\r
 \r
+    /**\r
+     * If we create a new hypelrink remember its type\r
+     */\r
+    protected int link_type;\r
+\r
+    /**\r
+     * Construct a new hyperlink\r
+     *\r
+     * @param type the type of hyperlink to create\r
+     */\r
+    public HSSFHyperlink( int type )\r
+    {\r
+        this.link_type = type;\r
+        record = new HyperlinkRecord();\r
+        switch(type){\r
+            case LINK_URL:\r
+            case LINK_EMAIL:\r
+                record.newUrlLink();\r
+                break;\r
+            case LINK_FILE:\r
+                record.newFileLink();\r
+                break;\r
+            case LINK_DOCUMENT:\r
+                record.newDocumentLink();\r
+                break;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Initialize the hyperlink by a <code>HyperlinkRecord</code> record\r
+     *\r
+     * @param record\r
+     */\r
     protected HSSFHyperlink( HyperlinkRecord record )\r
     {\r
         this.record = record;\r
     }\r
 \r
     /**\r
-     * Return the row of the cell that contains the hyperlink\r
+     * Return the row of the first cell that contains the hyperlink\r
      *\r
      * @return the 0-based row of the cell that contains the hyperlink\r
      */\r
-    public int getRow(){\r
-        return record.getRow();\r
+    public int getFirstRow(){\r
+        return record.getFirstRow();\r
+    }\r
+\r
+    /**\r
+     * Set the row of the first cell that contains the hyperlink\r
+     *\r
+     * @param row the 0-based row of the first cell that contains the hyperlink\r
+     */\r
+    public void setFirstRow(int row){\r
+        record.setFirstRow(row);\r
+    }\r
+\r
+    /**\r
+     * Return the row of the last cell that contains the hyperlink\r
+     *\r
+     * @return the 0-based row of the last cell that contains the hyperlink\r
+     */\r
+    public int getLastRow(){\r
+        return record.getLastRow();\r
+    }\r
+\r
+    /**\r
+     * Set the row of the last cell that contains the hyperlink\r
+     *\r
+     * @param row the 0-based row of the last cell that contains the hyperlink\r
+     */\r
+    public void setLastRow(int row){\r
+        record.setLastRow(row);\r
+    }\r
+\r
+    /**\r
+     * Return the column of the first cell that contains the hyperlink\r
+     *\r
+     * @return the 0-based column of the first cell that contains the hyperlink\r
+     */\r
+    public short getFirstColumn(){\r
+        return record.getFirstColumn();\r
     }\r
 \r
     /**\r
-     * Set the row of the cell that contains the hyperlink\r
+     * Set the column of the first cell that contains the hyperlink\r
      *\r
-     * @param row the 0-based row of the cell that contains the hyperlink\r
+     * @param col the 0-based column of the first cell that contains the hyperlink\r
      */\r
-    public void setRow(int row){\r
-        record.setRow(row);\r
+    public void setFirstColumn(short col){\r
+        record.setFirstColumn(col);\r
     }\r
 \r
     /**\r
-     * Return the column of the cell that contains the hyperlink\r
+     * Return the column of the last cell that contains the hyperlink\r
      *\r
-     * @return the 0-based column of the cell that contains the hyperlink\r
+     * @return the 0-based column of the last cell that contains the hyperlink\r
      */\r
-    public short getColumn(){\r
-        return record.getColumn();\r
+    public short getLastColumn(){\r
+        return record.getLastColumn();\r
     }\r
 \r
     /**\r
-     * Set the column of the cell that contains the hyperlink\r
+     * Set the column of the last cell that contains the hyperlink\r
      *\r
-     * @param col the 0-based column of the cell that contains the hyperlink\r
+     * @param col the 0-based column of the last cell that contains the hyperlink\r
      */\r
-    public void setColumn(short col){\r
-        record.setColumn(col);\r
+    public void setLastColumn(short col){\r
+        record.setLastColumn(col);\r
     }\r
 \r
     /**\r
-     * Hypelink address. Depending on the hyperlink type it can be URL, e-mail, etc.\r
+     * Hypelink address. Depending on the hyperlink type it can be URL, e-mail, patrh to a file, etc.\r
      *\r
      * @return  the address of this hyperlink\r
      */\r
     public String getAddress(){\r
-        return record.getUrlString();\r
+        return record.getAddress();\r
     }\r
 \r
     /**\r
-     * Return text to display for this hyperlink\r
+     * Hypelink address. Depending on the hyperlink type it can be URL, e-mail, patrh to a file, etc.\r
+     *\r
+     * @param address  the address of this hyperlink\r
+     */\r
+    public void setAddress(String address){\r
+        record.setAddress(address);\r
+    }\r
+\r
+    /**\r
+     * Return text label for this hyperlink\r
      *\r
      * @return  text to display\r
      */\r
@@ -117,12 +195,21 @@ public class HSSFHyperlink {
         return record.getLabel();\r
     }\r
 \r
+    /**\r
+     * Sets text label for this hyperlink\r
+     *\r
+     * @param label text label for this hyperlink\r
+     */\r
+    public void setLabel(String label){\r
+        record.setLabel(label);\r
+    }\r
+\r
     /**\r
      * Return the type of this hyperlink\r
      *\r
      * @return the type of this hyperlink\r
      */\r
-    public int getType(){\r
-        throw new RuntimeException("Not implemented");\r
+    protected int getType(){\r
+        return link_type;\r
     }\r
 }\r
index 70548fe95f1430df822e12094f242737d8e34ec6..3d2ca406ce4b9dd7b5e1f3e67525688f3d4883e2 100644 (file)
@@ -18,114 +18,311 @@ package org.apache.poi.hssf.record;
 
 import java.io.ByteArrayInputStream;
 import java.net.URL;
+import java.util.Arrays;
 
 import junit.framework.TestCase;
 
+/**
+ * Test HyperlinkRecord
+ *
+ * @author Nick Burch
+ * @author Yegor Kozlov
+ */
 public class TestHyperlinkRecord extends TestCase {
-       protected void setUp() throws Exception {
-               super.setUp();
-       }
-
-       private byte[] data = new byte[] { 
-               -72, 1, 110, 0,
-               // ??, Row, col, xf
-               6, 0, 3, 0,     2, 0, 2, 0, 
-               
-               // ??
-               -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,
-
-               // ??
-               -48, -55, -22, 121, -7, -70, -50, 17,
-               -116, -126, 0, -86, 0, 75, -87, 11,
-               2, 0, 0, 0,
-               
-               // URL and Label lengths
-               23, 0, 0, 0,
-               15, 0, 0, 0,
-
-               // Label
-               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,
-
-               // ??
-               -32, -55, -22, 121, -7, -70, -50, 17,
-               -116, -126, 0, -86, 0, 75, -87, 11,
-               44, 0, 0, 0,
-
-               // URL
-               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 {
-        RecordInputStream inp = new RecordInputStream(
-                new ByteArrayInputStream(data)
-        );
-        inp.nextRecord();
-
-        HyperlinkRecord r = new HyperlinkRecord(inp);
-        
-        assertEquals(3, r.getRow());
-        assertEquals(2, r.getColumn());
-        assertEquals(2, r.getXFIndex());
-        
-        assertEquals("L,A", r.getLabel());
-        assertEquals("http://www.lakings.com", r.getUrlString());
-        assertEquals(new URL("http://www.lakings.com"), r.getUrl());
-        
-        // Check it serialises as expected
-        assertEquals(data.length, r.getRecordSize());
-        byte[] d = r.serialize();
-        assertEquals(data.length, d.length);
-        for(int i=0; i<data.length; i++) {
-               assertEquals(data[i], d[i]);
+
+    //link to http://www.lakings.com/
+    byte[] data1 = { 0x02, 0x00,    //First row of the hyperlink
+                     0x02, 0x00,    //Last row of the hyperlink
+                     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
+                     (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
+
+                    // flags. Define the type of the hyperlink:
+                    // HyperlinkRecord.HLINK_URL | HyperlinkRecord.HLINK_ABS | HyperlinkRecord.HLINK_LABEL
+                    0x17, 0x00, 0x00, 0x00,
+
+                    0x08, 0x00, 0x00, 0x00, //length of the label including the trailing '\0'
+
+                    //label:
+                    0x4D, 0x00, 0x79, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6B, 0x00, 0x00, 0x00,
+
+                    //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,
+
+                    //count of bytes in the address including the tail
+                    0x48, 0x00, 0x00, 0x00, //integer
+
+                    //the actual link, terminated by '\u0000'
+                    0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3A, 0x00, 0x2F, 0x00,
+                    0x2F, 0x00, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x2E, 0x00, 0x6C, 0x00,
+                    0x61, 0x00, 0x6B, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x73, 0x00,
+                    0x2E, 0x00, 0x63, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x2F, 0x00, 0x00, 0x00,
+
+                    //standard 24-byte tail of a URL link. Seems to always be the same for all URL HLINKs
+                    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};
+
+    //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());
-       }
+
+    }
 }
index 80785ca182d8155a7ae4c03441d6eb3ad75dc3e9..6c604d1b4d6bd10c13d38f5ae193939e3646245d 100644 (file)
@@ -320,8 +320,8 @@ extends TestCase {
 
         assertEquals("Foo", link.getLabel());
         assertEquals("http://poi.apache.org/", link.getAddress());
-        assertEquals(4, link.getRow());
-        assertEquals(0, link.getColumn());
+        assertEquals(4, link.getFirstRow());
+        assertEquals(0, link.getFirstColumn());
     }
     
     /**
@@ -339,16 +339,16 @@ extends TestCase {
         assertNotNull(link1);
         assertEquals("Foo", link1.getLabel());
         assertEquals("http://poi.apache.org/", link1.getAddress());
-        assertEquals(4, link1.getRow());
-        assertEquals(0, link1.getColumn());
+        assertEquals(4, link1.getFirstRow());
+        assertEquals(0, link1.getFirstColumn());
 
         HSSFCell cell2 = sheet.getRow(8).getCell((short)1);
         HSSFHyperlink link2 = cell2.getHyperlink();
         assertNotNull(link2);
         assertEquals("Bar", link2.getLabel());
-        assertEquals("http://poi.apache.org/", link2.getAddress());
-        assertEquals(8, link2.getRow());
-        assertEquals(1, link2.getColumn());
+        assertEquals("http://poi.apache.org/hssf/", link2.getAddress());
+        assertEquals(8, link2.getFirstRow());
+        assertEquals(1, link2.getFirstColumn());
 
     }
     
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHyperlink.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHyperlink.java
new file mode 100755 (executable)
index 0000000..b87899c
--- /dev/null
@@ -0,0 +1,191 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hssf.usermodel;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.io.*;\r
+\r
+/**\r
+ * Tests HSSFHyperlink.\r
+ *\r
+ * @author  Yegor Kozlov\r
+ */\r
+public class TestHSSFHyperlink extends TestCase {\r
+    protected String cwd = System.getProperty("HSSF.testdata.path");\r
+\r
+    /**\r
+     * Test that we can read hyperlinks.\r
+     */\r
+    public void testRead() throws Exception {\r
+\r
+        FileInputStream is = new FileInputStream(new File(cwd, "HyperlinksOnManySheets.xls"));\r
+        HSSFWorkbook wb = new HSSFWorkbook(is);\r
+        is.close();\r
+\r
+        HSSFSheet sheet;\r
+        HSSFCell cell;\r
+        HSSFHyperlink link;\r
+\r
+        sheet = wb.getSheet("WebLinks");\r
+        cell = sheet.getRow(4).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("POI", link.getLabel());\r
+        assertEquals("POI", cell.getRichStringCellValue().getString());\r
+        assertEquals("http://poi.apache.org/", link.getAddress());\r
+\r
+        cell = sheet.getRow(8).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("HSSF", link.getLabel());\r
+        assertEquals("HSSF", cell.getRichStringCellValue().getString());\r
+        assertEquals("http://poi.apache.org/hssf/", link.getAddress());\r
+\r
+        sheet = wb.getSheet("Emails");\r
+        cell = sheet.getRow(4).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("dev", link.getLabel());\r
+        assertEquals("dev", cell.getRichStringCellValue().getString());\r
+        assertEquals("mailto:dev@poi.apache.org", link.getAddress());\r
+\r
+        sheet = wb.getSheet("Internal");\r
+        cell = sheet.getRow(4).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("Link To First Sheet", link.getLabel());\r
+        assertEquals("Link To First Sheet", cell.getRichStringCellValue().getString());\r
+        assertEquals("WebLinks!A1", link.getAddress());\r
+    }\r
+\r
+    public void testModify() throws Exception {\r
+        FileInputStream is = new FileInputStream(new File(cwd, "HyperlinksOnManySheets.xls"));\r
+        HSSFWorkbook wb = new HSSFWorkbook(is);\r
+        is.close();\r
+\r
+        HSSFSheet sheet;\r
+        HSSFCell cell;\r
+        HSSFHyperlink link;\r
+\r
+        sheet = wb.getSheet("WebLinks");\r
+        cell = sheet.getRow(4).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        //modify the link\r
+        link.setAddress("www.apache.org");\r
+\r
+        //serialize and read again\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        wb.write(out);\r
+\r
+        wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));\r
+        sheet = wb.getSheet("WebLinks");\r
+        cell = sheet.getRow(4).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("www.apache.org", link.getAddress());\r
+\r
+    }\r
+\r
+    public void testCreate() throws Exception {\r
+        HSSFWorkbook wb = new HSSFWorkbook();\r
+\r
+        HSSFCell cell;\r
+        HSSFSheet sheet = wb.createSheet("Hyperlinks");\r
+\r
+        //URL\r
+        cell = sheet.createRow(0).createCell((short)0);\r
+        cell.setCellValue("URL Link");\r
+        HSSFHyperlink link = new HSSFHyperlink(HSSFHyperlink.LINK_URL);\r
+        link.setAddress("http://poi.apache.org/");\r
+        cell.setHyperlink(link);\r
+\r
+        //link to a file in the current directory\r
+        cell = sheet.createRow(1).createCell((short)0);\r
+        cell.setCellValue("File Link");\r
+        link = new HSSFHyperlink(HSSFHyperlink.LINK_FILE);\r
+        link.setAddress("link1.xls");\r
+        cell.setHyperlink(link);\r
+\r
+        //e-mail link\r
+        cell = sheet.createRow(2).createCell((short)0);\r
+        cell.setCellValue("Email Link");\r
+        link = new HSSFHyperlink(HSSFHyperlink.LINK_EMAIL);\r
+        //note, if subject contains white spaces, make sure they are url-encoded\r
+        link.setAddress("mailto:poi@apache.org?subject=Hyperlinks");\r
+        cell.setHyperlink(link);\r
+\r
+        //link to a place in this workbook\r
+\r
+        //create a target sheet and cell\r
+        HSSFSheet sheet2 = wb.createSheet("Target Sheet");\r
+        sheet2.createRow(0).createCell((short)0).setCellValue("Target Cell");\r
+\r
+        cell = sheet.createRow(3).createCell((short)0);\r
+        cell.setCellValue("Worksheet Link");\r
+        link = new HSSFHyperlink(HSSFHyperlink.LINK_DOCUMENT);\r
+        link.setAddress("'Target Sheet'!A1");\r
+        cell.setHyperlink(link);\r
+\r
+        //serialize and read again\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        wb.write(out);\r
+\r
+        wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));\r
+        sheet = wb.getSheet("Hyperlinks");\r
+        cell = sheet.getRow(0).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("http://poi.apache.org/", link.getAddress());\r
+\r
+        cell = sheet.getRow(1).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("link1.xls", link.getAddress());\r
+\r
+        cell = sheet.getRow(2).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("mailto:poi@apache.org?subject=Hyperlinks", link.getAddress());\r
+\r
+        cell = sheet.getRow(3).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("'Target Sheet'!A1", link.getAddress());\r
+    }\r
+\r
+    public void testCloneSheet() throws Exception {\r
+        FileInputStream is = new FileInputStream(new File(cwd, "HyperlinksOnManySheets.xls"));\r
+        HSSFWorkbook wb = new HSSFWorkbook(is);\r
+        is.close();\r
+\r
+        HSSFCell cell;\r
+        HSSFHyperlink link;\r
+\r
+        HSSFSheet sheet = wb.cloneSheet(0);\r
+\r
+        cell = sheet.getRow(4).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("http://poi.apache.org/", link.getAddress());\r
+\r
+        cell = sheet.getRow(8).getCell((short)0);\r
+        link = cell.getHyperlink();\r
+        assertNotNull(link);\r
+        assertEquals("http://poi.apache.org/hssf/", link.getAddress());\r
+    }\r
+}\r