pjfanning opened a new issue, #1416:
URL: https://github.com/apache/poi/issues/1416

   ## Summary
   
   Add an opt-in `CellCopyPolicy` setting that makes `CellUtil.copyCell` adjust 
a copied formula's relative references by the row/column offset between the 
source and destination cells. This is what Excel does when you copy and paste a 
formula.
   
   ## Motivation
   
   Today, `CellUtil.copyCell(src, dest, policy, context)` copies a formula 
verbatim:
   
   ```java
   case FORMULA:
       destCell.setCellFormula(srcCell.getCellFormula());
   ```
   
   Formula references are only adjusted when a whole row is copied with 
`XSSFRow.copyRowFrom` / `HSSFRow.copyRowFrom`, which run 
`FormulaShifter.createForRowCopy` over the destination row afterwards. This 
leaves gaps:
   
   - **No column copy.** There is no `copyColumn` equivalent, so copying a 
formula sideways (e.g. a horizontal template fill) has no POI API.
   - **No SXSSF.** `SXSSFRow` has no `copyRowFrom`, so streaming writers cannot 
copy a formula row at all.
   - **All or nothing.** `copyRowFrom` copies every cell in the row. Template 
engines want to copy the formula cells and write their own values elsewhere. 
`CellCopyPolicy` cannot copy formulas without also copying values 
(`copyCellFormula` only applies when `copyCellValue` is set).
   
   As a result, downstream libraries reimplement the logic themselves: parse 
with an `*EvaluationWorkbook`, build a row-copy or column-copy 
`FormulaShifter`, `adjustFormula`, then render with `FormulaRenderer`. Apache 
Fesod is currently adding about 100 lines of exactly this for template fills 
(apache/fesod#1215), including a separate code path for the horizontal 
direction.
   
   The public `SharedFormula.convertSharedFormulas(ptgs, row, col)` looks like 
it would do this, but it is not suitable as a general copy helper. It rebuilds 
every `RefPtgBase`/`AreaPtgBase` as a plain `RefPtg`/`AreaPtg`, so 3D 
references (`Ref3DPtg`, `Area3DPxg`, …) lose their sheet: `Sheet2!A1` becomes 
`A1`. It also wraps references past the sheet edge instead of producing `#REF!`.
   
   ## Proposal
   
   1. Add `CellCopyPolicy#isAdjustFormulaReferences()` / 
`Builder#adjustFormulaReferences(boolean)`, **defaulting to `false`**.
   2. When it is set and a formula is copied, `CellUtil.copyCell` parses the 
source formula against the destination workbook, shifts relative row references 
by `destRow - srcRow` and relative column references by `destCol - srcCol`, and 
renders the result. Absolute references and absolute parts of mixed references 
stay put. Areas, 3D references and references to other sheets keep their sheet. 
References that would go past the destination's `SpreadsheetVersion` limits 
become `#REF!`, as `FormulaShifter` already does.
      - Implementation: `FormulaShifter.createForRowCopy` followed by 
`createForColumnCopy` (or a combined row+column copy mode), with the 
parsing/rendering workbook chosen from the destination cell's workbook: 
`HSSFEvaluationWorkbook`, `XSSFEvaluationWorkbook` or 
`SXSSFEvaluationWorkbook`. Because it works on a single `Cell`, it covers HSSF, 
XSSF and SXSSF destinations alike.
      - Same-workbook copies are the main case. Copying across workbooks can 
keep the current verbatim behaviour, or be documented as unsupported.
   3. Optional follow-up: allow formulas to be copied without values, e.g. a 
`copyCellValue=false, copyCellFormula=true` combination meaning "formulas 
only", or a cell filter. That is a separate change and could be left out of 
this one.
   
   With this, a template engine can copy just the formula cells into each 
created row or column:
   
   ```java
   CellCopyPolicy policy = new CellCopyPolicy.Builder()
           .adjustFormulaReferences(true)
           .build();
   CellUtil.copyCell(templateFormulaCell, targetCell, policy, context);
   ```
   
   ## Behaviour-change risk
   
   None by default: the setting is off unless the caller turns it on, and 
current verbatim copying is unchanged.
   
   Things to decide before switching it on anywhere by default:
   
   - **`XSSFRow.copyRowFrom`/`HSSFRow.copyRowFrom`** already shift the 
destination row after copying. If they are moved onto the new setting, they 
must not shift twice. They could set the flag internally and drop their 
post-copy `updateRowFormulas`, which gives the same results for existing 
callers.
   - **Existing `CellUtil.copyCell` callers** that copy formulas and then 
adjust references themselves would double-shift if a future major release 
turned the setting on by default. Keeping it opt-in avoids that.
   - **Copies across workbooks** depend on sheet names and defined names 
resolving in the destination workbook. Falling back to verbatim (or throwing) 
when parsing fails is safer than silently guessing.
   
   ## Related
   
   - #1414: `HSSFRow.copyRowFrom` passes `SpreadsheetVersion.EXCEL2007` and the 
workbook sheet index to `createForRowCopy`. A shared implementation that takes 
the version and extern sheet index from the destination workbook would fix that 
as well.
   - A possible follow-up: `SXSSFRow.copyRowFrom(Row, CellCopyPolicy)`, limited 
to sources still in memory (an SXSSF row still in the window, or an `XSSFRow` 
from the wrapped template workbook), built on this per-cell support.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to