View | Details | Raw Unified | Return to bug 58557
Collapse All | Expand All

(-)src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java (-3 / +6 lines)
Lines 23-28 Link Here
23
import org.apache.poi.openxml4j.opc.PackageRelationship;
23
import org.apache.poi.openxml4j.opc.PackageRelationship;
24
import org.apache.poi.ss.usermodel.Hyperlink;
24
import org.apache.poi.ss.usermodel.Hyperlink;
25
import org.apache.poi.ss.util.CellReference;
25
import org.apache.poi.ss.util.CellReference;
26
import org.apache.poi.util.Internal;
26
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink;
27
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink;
27
28
28
/**
29
/**
Lines 33-40 Link Here
33
public class XSSFHyperlink implements Hyperlink {
34
public class XSSFHyperlink implements Hyperlink {
34
    private int _type;
35
    private int _type;
35
    private PackageRelationship _externalRel;
36
    private PackageRelationship _externalRel;
36
    private CTHyperlink _ctHyperlink;
37
    private CTHyperlink _ctHyperlink; //contains a reference to the cell where the hyperlink is anchored, getRef()
37
    private String _location;
38
    private String _location; //what the hyperlink refers to
38
39
39
    /**
40
    /**
40
     * Create a new XSSFHyperlink. This method is protected to be used only by XSSFCreationHelper
41
     * Create a new XSSFHyperlink. This method is protected to be used only by XSSFCreationHelper
Lines 94-99 Link Here
94
    /**
95
    /**
95
     * @return the underlying CTHyperlink object
96
     * @return the underlying CTHyperlink object
96
     */
97
     */
98
    @Internal
97
    public CTHyperlink getCTHyperlink() {
99
    public CTHyperlink getCTHyperlink() {
98
        return _ctHyperlink;
100
        return _ctHyperlink;
99
    }
101
    }
Lines 219-225 Link Here
219
    /**
221
    /**
220
     * Assigns this hyperlink to the given cell reference
222
     * Assigns this hyperlink to the given cell reference
221
     */
223
     */
222
    protected void setCellReference(String ref) {
224
    @Internal
225
    public void setCellReference(String ref) {
223
        _ctHyperlink.setRef(ref);
226
        _ctHyperlink.setRef(ref);
224
    }
227
    }
225
    protected void setCellReference(CellReference ref) {
228
    protected void setCellReference(CellReference ref) {
(-)src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java (+32 lines)
Lines 25-30 Link Here
25
import java.io.OutputStream;
25
import java.io.OutputStream;
26
import java.util.ArrayList;
26
import java.util.ArrayList;
27
import java.util.Arrays;
27
import java.util.Arrays;
28
import java.util.Collections;
28
import java.util.Comparator;
29
import java.util.Comparator;
29
import java.util.HashMap;
30
import java.util.HashMap;
30
import java.util.Iterator;
31
import java.util.Iterator;
Lines 669-674 Link Here
669
                vml == null ? null : vml.findCommentShape(row, column));
670
                vml == null ? null : vml.findCommentShape(row, column));
670
    }
671
    }
671
672
673
    /**
674
     * Get a Hyperlink in this sheet anchored at row, column
675
     *
676
     * @param row
677
     * @param column
678
     * @return hyperlink if there is a hyperlink anchored at row, column; otherwise returns null
679
     */
672
    public XSSFHyperlink getHyperlink(int row, int column) {
680
    public XSSFHyperlink getHyperlink(int row, int column) {
673
        String ref = new CellReference(row, column).formatAsString();
681
        String ref = new CellReference(row, column).formatAsString();
674
        for(XSSFHyperlink hyperlink : hyperlinks) {
682
        for(XSSFHyperlink hyperlink : hyperlinks) {
Lines 678-683 Link Here
678
        }
686
        }
679
        return null;
687
        return null;
680
    }
688
    }
689
    
690
    /**
691
     * Get a list of Hyperlinks in this sheet
692
     *
693
     * @return
694
     */
695
    public List<XSSFHyperlink> getHyperlinkList() {
696
        return Collections.unmodifiableList(hyperlinks);
697
    }
681
698
682
    @SuppressWarnings("deprecation")
699
    @SuppressWarnings("deprecation")
683
    private int[] getBreaks(CTPageBreak ctPageBreak) {
700
    private int[] getBreaks(CTPageBreak ctPageBreak) {
Lines 2610-2615 Link Here
2610
                // remove row from _rows
2627
                // remove row from _rows
2611
                it.remove();
2628
                it.remove();
2612
2629
2630
                // FIXME: (performance optimization) this should be moved outside the for-loop so that comments only needs to be iterated over once.
2613
                // also remove any comments associated with this row
2631
                // also remove any comments associated with this row
2614
                if(sheetComments != null){
2632
                if(sheetComments != null){
2615
                    CTCommentList lst = sheetComments.getCTComments().getCommentList();
2633
                    CTCommentList lst = sheetComments.getCTComments().getCommentList();
Lines 2624-2629 Link Here
2624
                    	}
2642
                    	}
2625
                    }
2643
                    }
2626
                }
2644
                }
2645
                // FIXME: (performance optimization) this should be moved outside the for-loop so that hyperlinks only needs to be iterated over once.
2646
                // also remove any hyperlinks associated with this row
2647
                if (hyperlinks != null) {
2648
                    for (XSSFHyperlink link : new ArrayList<XSSFHyperlink>(hyperlinks)) {
2649
                        CellReference ref = new CellReference(link.getCellRef());
2650
                        if (ref.getRow() == rownum) {
2651
                            hyperlinks.remove(link);
2652
                        }
2653
                    }
2654
                }
2627
            }
2655
            }
2628
        }
2656
        }
2629
2657
Lines 2707-2712 Link Here
2707
        rowShifter.updateFormulas(shifter);
2735
        rowShifter.updateFormulas(shifter);
2708
        rowShifter.shiftMerged(startRow, endRow, n);
2736
        rowShifter.shiftMerged(startRow, endRow, n);
2709
        rowShifter.updateConditionalFormatting(shifter);
2737
        rowShifter.updateConditionalFormatting(shifter);
2738
        rowShifter.updateHyperlinks(shifter);
2710
2739
2711
        //rebuild the _rows map
2740
        //rebuild the _rows map
2712
        SortedMap<Integer, XSSFRow> map = new TreeMap<Integer, XSSFRow>();
2741
        SortedMap<Integer, XSSFRow> map = new TreeMap<Integer, XSSFRow>();
Lines 2902-2907 Link Here
2902
     */
2931
     */
2903
    @Internal
2932
    @Internal
2904
    public void removeHyperlink(int row, int column) {
2933
    public void removeHyperlink(int row, int column) {
2934
        // CTHyperlinks is regenerated from scratch when writing out the spreadsheet
2935
        // so don't worry about maintaining hyperlinks and CTHyperlinks in parallel.
2936
        // only maintain hyperlinks
2905
        String ref = new CellReference(row, column).formatAsString();
2937
        String ref = new CellReference(row, column).formatAsString();
2906
        for (Iterator<XSSFHyperlink> it = hyperlinks.iterator(); it.hasNext();) {
2938
        for (Iterator<XSSFHyperlink> it = hyperlinks.iterator(); it.hasNext();) {
2907
            XSSFHyperlink hyperlink = it.next();
2939
            XSSFHyperlink hyperlink = it.next();
(-)src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java (+26 lines)
Lines 22-27 Link Here
22
import java.util.List;
22
import java.util.List;
23
import java.util.Set;
23
import java.util.Set;
24
24
25
import org.apache.poi.common.usermodel.Hyperlink;
25
import org.apache.poi.ss.formula.FormulaParseException;
26
import org.apache.poi.ss.formula.FormulaParseException;
26
import org.apache.poi.ss.formula.FormulaParser;
27
import org.apache.poi.ss.formula.FormulaParser;
27
import org.apache.poi.ss.formula.FormulaRenderer;
28
import org.apache.poi.ss.formula.FormulaRenderer;
Lines 38-43 Link Here
38
import org.apache.poi.util.POILogger;
39
import org.apache.poi.util.POILogger;
39
import org.apache.poi.xssf.usermodel.XSSFCell;
40
import org.apache.poi.xssf.usermodel.XSSFCell;
40
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
41
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
42
import org.apache.poi.xssf.usermodel.XSSFHyperlink;
41
import org.apache.poi.xssf.usermodel.XSSFName;
43
import org.apache.poi.xssf.usermodel.XSSFName;
42
import org.apache.poi.xssf.usermodel.XSSFRow;
44
import org.apache.poi.xssf.usermodel.XSSFRow;
43
import org.apache.poi.xssf.usermodel.XSSFSheet;
45
import org.apache.poi.xssf.usermodel.XSSFSheet;
Lines 280-285 Link Here
280
            }
282
            }
281
        }
283
        }
282
    }
284
    }
285
    
286
    /**
287
     * Shift the Hyperlink anchors (not the hyperlink text, even if the hyperlink
288
     * is of type LINK_DOCUMENT and refers to a cell that was shifted). Hyperlinks
289
     * do not track the content they point to.
290
     *
291
     * @param shifter
292
     */
293
    public void updateHyperlinks(FormulaShifter shifter) {
294
        int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet);
295
        List<XSSFHyperlink> hyperlinkList = sheet.getHyperlinkList();
296
        
297
        for (XSSFHyperlink hyperlink : hyperlinkList) {
298
            String cellRef = hyperlink.getCellRef();
299
            CellRangeAddress cra = CellRangeAddress.valueOf(cellRef);
300
            CellRangeAddress shiftedRange = shiftRange(shifter, cra, sheetIndex);
301
            if (shiftedRange != null && shiftedRange != cra) {
302
                // shiftedRange should not be null. If shiftedRange is null, that means
303
                // that a hyperlink wasn't deleted at the beginning of shiftRows when
304
                // identifying rows that should be removed because they will be overwritten
305
                hyperlink.setCellReference(shiftedRange.formatAsString());
306
            }
307
        }
308
    }
283
309
284
    private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) {
310
    private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) {
285
        // FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
311
        // FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
(-)src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetShiftRows.java (+106 lines)
Lines 21-27 Link Here
21
21
22
import org.apache.poi.ss.usermodel.BaseTestSheetShiftRows;
22
import org.apache.poi.ss.usermodel.BaseTestSheetShiftRows;
23
import org.apache.poi.ss.usermodel.Cell;
23
import org.apache.poi.ss.usermodel.Cell;
24
import org.apache.poi.ss.usermodel.CellStyle;
24
import org.apache.poi.ss.usermodel.Comment;
25
import org.apache.poi.ss.usermodel.Comment;
26
import org.apache.poi.ss.usermodel.CreationHelper;
27
import org.apache.poi.ss.usermodel.Font;
28
import org.apache.poi.ss.usermodel.Hyperlink;
29
import org.apache.poi.ss.usermodel.IndexedColors;
25
import org.apache.poi.ss.usermodel.Row;
30
import org.apache.poi.ss.usermodel.Row;
26
import org.apache.poi.ss.usermodel.Sheet;
31
import org.apache.poi.ss.usermodel.Sheet;
27
import org.apache.poi.ss.usermodel.Workbook;
32
import org.apache.poi.ss.usermodel.Workbook;
Lines 366-369 Link Here
366
        
371
        
367
        wb.close();
372
        wb.close();
368
    }
373
    }
374
    
375
    public void testBug46742_shiftHyperlinks() throws IOException {
376
        XSSFWorkbook wb = new XSSFWorkbook();
377
        Sheet sheet = wb.createSheet("test");
378
        Row row = sheet.createRow(0);
379
        
380
        // How to create hyperlinks
381
        // https://poi.apache.org/spreadsheet/quick-guide.html#Hyperlinks
382
        XSSFCreationHelper helper = wb.getCreationHelper();
383
        CellStyle hlinkStyle = wb.createCellStyle();
384
        Font hlinkFont = wb.createFont();
385
        hlinkFont.setUnderline(Font.U_SINGLE);
386
        hlinkFont.setColor(IndexedColors.BLUE.getIndex());
387
        hlinkStyle.setFont(hlinkFont);
388
389
        // 3D relative document link
390
        Cell cell = row.createCell(0);
391
        cell.setCellStyle(hlinkStyle);
392
        createHyperlink(helper, cell, Hyperlink.LINK_DOCUMENT, "test!E1");
393
        
394
        // URL
395
        cell = row.createCell(1);
396
        cell.setCellStyle(hlinkStyle);
397
        createHyperlink(helper, cell, Hyperlink.LINK_URL, "http://poi.apache.org/");
398
        
399
        // row0 will be shifted on top of row1, so this URL should be removed from the workbook
400
        Row overwrittenRow = sheet.createRow(3);
401
        cell = overwrittenRow.createCell(2);
402
        cell.setCellStyle(hlinkStyle);
403
        createHyperlink(helper, cell, Hyperlink.LINK_EMAIL, "mailto:poi@apache.org");
404
        
405
        // hyperlinks on this row are unaffected by the row shifting, so the hyperlinks should not move
406
        Row unaffectedRow = sheet.createRow(20);
407
        cell = unaffectedRow.createCell(3);
408
        cell.setCellStyle(hlinkStyle);
409
        createHyperlink(helper, cell, Hyperlink.LINK_FILE, "54524.xlsx");
410
        
411
        cell = wb.createSheet("other").createRow(0).createCell(0);
412
        cell.setCellStyle(hlinkStyle);
413
        createHyperlink(helper, cell, Hyperlink.LINK_URL, "http://apache.org/");
414
        
415
        int startRow = 0;
416
        int endRow = 0;
417
        int n = 3;
418
        sheet.shiftRows(startRow, endRow, n);
419
        
420
        XSSFWorkbook read = XSSFTestDataSamples.writeOutAndReadBack(wb);
421
        wb.close();
422
        
423
        XSSFSheet sh = read.getSheet("test");
424
        
425
        Row shiftedRow = sh.getRow(3);
426
        
427
        // document link anchored on a shifted cell should be moved
428
        // Note that hyperlinks do not track what they point to, so this hyperlink should still refer to test!E1
429
        verifyHyperlink(shiftedRow.getCell(0), Hyperlink.LINK_DOCUMENT, "test!E1");
430
        
431
        // URL, EMAIL, and FILE links anchored on a shifted cell should be moved
432
        verifyHyperlink(shiftedRow.getCell(1), Hyperlink.LINK_URL, "http://poi.apache.org/");
433
        
434
        // Make sure hyperlinks were moved and not copied
435
        assertNull(sh.getRow(0));
436
        
437
        // Make sure hyperlink in overwritten row is deleted
438
        assertEquals(3, sh.getHyperlinkList().size());
439
        for (XSSFHyperlink link : sh.getHyperlinkList()) {
440
            if ("C4".equals(link.getCellRef())) {
441
                fail("Row 4, including the hyperlink at C4, should have been deleted when Row 1 was shifted on top of it.");
442
            }
443
        }
444
        
445
        // Make sure unaffected rows are not shifted
446
        Cell unaffectedCell = sh.getRow(20).getCell(3);
447
        assertTrue(cellHasHyperlink(unaffectedCell));
448
        verifyHyperlink(unaffectedCell, Hyperlink.LINK_FILE, "54524.xlsx");
449
        
450
        // Make sure cells on other sheets are not affected
451
        unaffectedCell = read.getSheet("other").getRow(0).getCell(0);
452
        assertTrue(cellHasHyperlink(unaffectedCell));
453
        verifyHyperlink(unaffectedCell, Hyperlink.LINK_URL, "http://apache.org/");
454
        
455
        read.close();
456
    }
457
    
458
    private void createHyperlink(CreationHelper helper, Cell cell, int linkType, String ref) {
459
        cell.setCellValue(ref);
460
        Hyperlink link = helper.createHyperlink(linkType);
461
        link.setAddress(ref);
462
        cell.setHyperlink(link);
463
    }
464
    
465
    private void verifyHyperlink(Cell cell, int linkType, String ref) {
466
        assertTrue(cellHasHyperlink(cell));
467
        Hyperlink link = cell.getHyperlink();
468
        assertEquals(linkType, link.getType());
469
        assertEquals(ref, link.getAddress());
470
    }
471
    
472
    private boolean cellHasHyperlink(Cell cell) {
473
        return (cell != null) && (cell.getHyperlink() != null);
474
    }
369
}
475
}

Return to bug 58557