Author: nick
Date: Tue Nov  4 21:35:01 2014
New Revision: 1636742

URL: http://svn.apache.org/r1636742
Log:
Partial HSSF support for adding new external workbook formula references for 
#57184

Modified:
    poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
    poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java
    poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
    poi/trunk/src/java/org/apache/poi/ss/usermodel/Workbook.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
    
poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java?rev=1636742&r1=1636741&r2=1636742&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java Tue Nov  
4 21:35:01 2014
@@ -94,6 +94,7 @@ import org.apache.poi.ss.formula.ptg.Ptg
 import org.apache.poi.ss.formula.ptg.Ref3DPtg;
 import org.apache.poi.ss.formula.udf.UDFFinder;
 import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
@@ -1803,6 +1804,10 @@ public final class InternalWorkbook {
         }
         return linkTable;
     }
+    
+    public int linkExternalWorkbook(String name, Workbook externalWorkbook) {
+        return getOrCreateLinkTable().linkExternalWorkbook(name, 
externalWorkbook);
+    }
 
     /** 
      * Finds the first sheet name by his extern sheet index

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java?rev=1636742&r1=1636741&r2=1636742&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java Tue Nov  4 
21:35:01 2014
@@ -37,6 +37,7 @@ import org.apache.poi.ss.formula.ptg.Err
 import org.apache.poi.ss.formula.ptg.NameXPtg;
 import org.apache.poi.ss.formula.ptg.Ptg;
 import org.apache.poi.ss.formula.ptg.Ref3DPtg;
+import org.apache.poi.ss.usermodel.Workbook;
 
 /**
  * Link Table (OOO pdf reference: 4.10.3 ) <p/>
@@ -110,31 +111,39 @@ final class LinkTable {
                        _crnBlocks = new CRNBlock[temp.size()];
                        temp.toArray(_crnBlocks);
                }
-
-        /**
-         * Create a new block for internal references. It is called when 
constructing a new LinkTable.
-         *
-         * @see org.apache.poi.hssf.model.LinkTable#LinkTable(int, 
WorkbookRecordList)
-         */
-        public ExternalBookBlock(int numberOfSheets) {
-                       _externalBookRecord = 
SupBookRecord.createInternalReferences((short)numberOfSheets);
-                       _externalNameRecords = new ExternalNameRecord[0];
-                       _crnBlocks = new CRNBlock[0];
-               }
-
-        /**
-         * Create a new block for registering add-in functions
-         *
-         * @see org.apache.poi.hssf.model.LinkTable#addNameXPtg(String)
-         */
-        public ExternalBookBlock() {
-            _externalBookRecord = SupBookRecord.createAddInFunctions();
-            _externalNameRecords = new ExternalNameRecord[0];
-            _crnBlocks = new CRNBlock[0];
-        }
+               
+       /**
+        * Create a new block for external references.
+        */
+       public ExternalBookBlock(String url, String[] sheetNames) {
+           _externalBookRecord = SupBookRecord.createExternalReferences(url, 
sheetNames);
+           _crnBlocks = new CRNBlock[0];
+       }
+
+       /**
+        * Create a new block for internal references. It is called when 
constructing a new LinkTable.
+        *
+        * @see org.apache.poi.hssf.model.LinkTable#LinkTable(int, 
WorkbookRecordList)
+        */
+       public ExternalBookBlock(int numberOfSheets) {
+           _externalBookRecord = 
SupBookRecord.createInternalReferences((short)numberOfSheets);
+           _externalNameRecords = new ExternalNameRecord[0];
+           _crnBlocks = new CRNBlock[0];
+       }
+
+       /**
+        * Create a new block for registering add-in functions
+        *
+        * @see org.apache.poi.hssf.model.LinkTable#addNameXPtg(String)
+        */
+       public ExternalBookBlock() {
+           _externalBookRecord = SupBookRecord.createAddInFunctions();
+           _externalNameRecords = new ExternalNameRecord[0];
+           _crnBlocks = new CRNBlock[0];
+       }
 
                public SupBookRecord getExternalBookRecord() {
-                       return _externalBookRecord;
+                   return _externalBookRecord;
                }
 
                public String getNameText(int definedNameIndex) {
@@ -382,31 +391,68 @@ final class LinkTable {
             };
                }
        }
+       
+       private int getExternalWorkbookIndex(String workbookName) {
+           for (int i=0; i<_externalBookBlocks.length; i++) {
+               SupBookRecord ebr = 
_externalBookBlocks[i].getExternalBookRecord();
+               if (!ebr.isExternalReferences()) {
+                   continue;
+               }
+               if (workbookName.equals(ebr.getURL())) { // not sure if 
'equals()' works when url has a directory
+                   return i;
+               }
+           } 
+           return -1;
+       }
+       
+       public int linkExternalWorkbook(String name, Workbook externalWorkbook) 
{
+        int extBookIndex = getExternalWorkbookIndex(name);
+        if (extBookIndex != -1) {
+            // Already linked!
+            return extBookIndex;
+        }
+        
+        // Create a new SupBookRecord
+        String[] sheetNames = new String[externalWorkbook.getNumberOfSheets()];
+        for (int sn=0; sn<sheetNames.length; sn++) {
+            sheetNames[sn] = externalWorkbook.getSheetName(sn);
+        }
+        String url = "\000" + name;
+        ExternalBookBlock block = new ExternalBookBlock(url, sheetNames);
+        
+        // Add it into the list + records
+        extBookIndex = extendExternalBookBlocks(block);
+
+        // add the created SupBookRecord before ExternSheetRecord
+        int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
+        if (idx == -1) {
+            idx = _workbookRecordList.size();
+        }
+        _workbookRecordList.add(idx, block.getExternalBookRecord());
+        
+        // Setup links for the sheets
+        for (int sn=0; sn<sheetNames.length; sn++) {
+            _externSheetRecord.addRef(extBookIndex, sn, sn);
+        }
+        
+        // Report where it went
+        return extBookIndex;
+       }
 
        public int getExternalSheetIndex(String workbookName, String 
firstSheetName, String lastSheetName) {
-               SupBookRecord ebrTarget = null;
-               int externalBookIndex = -1;
-               for (int i=0; i<_externalBookBlocks.length; i++) {
-                       SupBookRecord ebr = 
_externalBookBlocks[i].getExternalBookRecord();
-                       if (!ebr.isExternalReferences()) {
-                               continue;
-                       }
-                       if (workbookName.equals(ebr.getURL())) { // not sure if 
'equals()' works when url has a directory
-                               ebrTarget = ebr;
-                               externalBookIndex = i;
-                               break;
-                       }
-               }
-               if (ebrTarget == null) {
-                       throw new RuntimeException("No external workbook with 
name '" + workbookName + "'");
-               }
+           int externalBookIndex = getExternalWorkbookIndex(workbookName);
+        if (externalBookIndex == -1) {
+            throw new RuntimeException("No external workbook with name '" + 
workbookName + "'");
+        }
+        SupBookRecord ebrTarget = 
_externalBookBlocks[externalBookIndex].getExternalBookRecord();
+
                int firstSheetIndex = getSheetIndex(ebrTarget.getSheetNames(), 
firstSheetName);
         int lastSheetIndex = getSheetIndex(ebrTarget.getSheetNames(), 
lastSheetName);
 
+        // Find or add the external sheet record definition for this
                int result = 
_externSheetRecord.getRefIxForSheet(externalBookIndex, firstSheetIndex, 
lastSheetIndex);
                if (result < 0) {
-                       throw new RuntimeException("ExternSheetRecord does not 
contain combination ("
-                                       + externalBookIndex + ", " + 
firstSheetIndex + ", " + lastSheetIndex + ")");
+                   result = _externSheetRecord.addRef(externalBookIndex, 
firstSheetIndex, lastSheetIndex);
                }
                return result;
        }
@@ -580,13 +626,7 @@ final class LinkTable {
         // An ExternalBlock for Add-In functions was not found. Create a new 
one.
         if (extBlock == null) {
             extBlock = new ExternalBookBlock();
-
-            ExternalBookBlock[] tmp = new 
ExternalBookBlock[_externalBookBlocks.length + 1];
-            System.arraycopy(_externalBookBlocks, 0, tmp, 0, 
_externalBookBlocks.length);
-            tmp[tmp.length - 1] = extBlock;
-            _externalBookBlocks = tmp;
-
-            extBlockIndex = _externalBookBlocks.length - 1;
+            extBlockIndex = extendExternalBookBlocks(extBlock);
 
             // add the created SupBookRecord before ExternSheetRecord
             int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
@@ -620,6 +660,14 @@ final class LinkTable {
         int ix = _externSheetRecord.getRefIxForSheet(extBlockIndex, 
fakeSheetIdx, fakeSheetIdx);
         return new NameXPtg(ix, nameIndex);
     }
+    private int extendExternalBookBlocks(ExternalBookBlock newBlock) {
+        ExternalBookBlock[] tmp = new 
ExternalBookBlock[_externalBookBlocks.length + 1];
+        System.arraycopy(_externalBookBlocks, 0, tmp, 0, 
_externalBookBlocks.length);
+        tmp[tmp.length - 1] = newBlock;
+        _externalBookBlocks = tmp;
+
+        return (_externalBookBlocks.length - 1);
+    }
 
     private int findRefIndexFromExtBookIndex(int extBookIndex) {
                return 
_externSheetRecord.findRefIndexFromExtBookIndex(extBookIndex);

Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java?rev=1636742&r1=1636741&r2=1636742&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java Tue Nov  
4 21:35:01 2014
@@ -79,6 +79,7 @@ import org.apache.poi.ss.formula.udf.Agg
 import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
 import org.apache.poi.ss.formula.udf.UDFFinder;
 import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
+import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.WorkbookUtil;
 import org.apache.poi.util.Configurator;
@@ -1851,6 +1852,19 @@ public final class HSSFWorkbook extends 
     }
     
     /**
+     * Adds the LinkTable records required to allow formulas referencing
+     *  the specified external workbook to be added to this one. Allows
+     *  formulas such as "[MyOtherWorkbook]Sheet3!$A$5" to be added to the 
+     *  file, for workbooks not already referenced.
+     *
+     * @param name The name the workbook will be referenced as in formulas
+     * @param workbook The open workbook to fetch the link required 
information from
+     */
+    public int linkExternalWorkbook(String name, Workbook workbook) {
+        return this.workbook.linkExternalWorkbook(name, workbook);
+    }
+    
+    /**
      * Is the workbook protected with a password (not encrypted)?
      */
     public boolean isWriteProtected() {

Modified: poi/trunk/src/java/org/apache/poi/ss/usermodel/Workbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/usermodel/Workbook.java?rev=1636742&r1=1636741&r2=1636742&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/usermodel/Workbook.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/usermodel/Workbook.java Tue Nov  4 
21:35:01 2014
@@ -407,6 +407,22 @@ public interface Workbook extends Closea
      */
     void removeName(String name);
 
+    /**
+     * Adds the linking required to allow formulas referencing
+     *  the specified external workbook to be added to this one.
+     * <p>In order for formulas such as "[MyOtherWorkbook]Sheet3!$A$5"
+     *  to be added to the file, some linking information must first
+     *  be recorded. Once a given external workbook has been linked,
+     *  then formulas using it can added. Each workbook needs linking
+     *  only once. 
+     * <p>This linking only applies for writing formulas. To link things
+     *  for evaluation, see {@link 
FormulaEvaluator#setupReferencedWorkbooks(java.util.Map)}
+     *
+     * @param name The name the workbook will be referenced as in formulas
+     * @param workbook The open workbook to fetch the link required 
information from
+     */
+    int linkExternalWorkbook(String name, Workbook workbook);
+    
      /**
      * Sets the printarea for the sheet provided
      * <p>

Modified: 
poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java?rev=1636742&r1=1636741&r2=1636742&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java 
Tue Nov  4 21:35:01 2014
@@ -1174,6 +1174,20 @@ public class SXSSFWorkbook implements Wo
     {
         _wb.setSheetHidden(sheetIx,hidden);
     }
+    
+    /**
+     * Adds the LinkTable records required to allow formulas referencing
+     *  the specified external workbook to be added to this one. Allows
+     *  formulas such as "[MyOtherWorkbook]Sheet3!$A$5" to be added to the 
+     *  file, for workbooks not already referenced.
+     *
+     * @param name The name the workbook will be referenced as in formulas
+     * @param workbook The open workbook to fetch the link required 
information from
+     */
+    public int linkExternalWorkbook(String name, Workbook workbook) {
+        throw new RuntimeException("NotImplemented");
+    }
+    
     /**
      * Register a new toolpack in this workbook.
      *

Modified: 
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java?rev=1636742&r1=1636741&r2=1636742&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java 
Tue Nov  4 21:35:01 2014
@@ -1704,6 +1704,18 @@ public class XSSFWorkbook extends POIXML
        return mapInfo;
     }
 
+    /**
+     * Adds the LinkTable records required to allow formulas referencing
+     *  the specified external workbook to be added to this one. Allows
+     *  formulas such as "[MyOtherWorkbook.xlsx]Sheet3!$A$5" to be added to 
the 
+     *  file, for workbooks not already referenced.
+     *
+     * @param name The name the workbook will be referenced as in formulas
+     * @param workbook The open workbook to fetch the link required 
information from
+     */
+    public int linkExternalWorkbook(String name, Workbook workbook) {
+        throw new RuntimeException("NotImplemented");
+    }
 
        /**
         * Specifies a boolean value that indicates whether structure of 
workbook is locked. <br/>

Modified: 
poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java?rev=1636742&r1=1636741&r2=1636742&view=diff
==============================================================================
--- 
poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java
 (original)
+++ 
poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java
 Tue Nov  4 21:35:01 2014
@@ -231,14 +231,35 @@ public final class TestHSSFFormulaEvalua
       assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCachedFormulaResultType());
       assertEquals(36.90, cell.getNumericCellValue(), 0.0001);
       
-/*      
-      // Now add a formula that refers to yet another (different) workbook
-      cell = wb.getSheetAt(0).getRow(1).createCell(42);
-      cell.setCellFormula("[alt.xls]Sheet1!$A$1");
       
-      // Check it - TODO Is this correct? Or should it become [2]Sheet1!$A$1 ?
-      assertEquals("[alt.xls]Sheet1!$A$1", cell.getCellFormula());
+      // Add a formula that refers to one of the existing external workbooks
+      cell = wb.getSheetAt(0).getRow(1).createCell(40);
+      cell.setCellFormula("Cost*[XRefCalcData.xls]MarkupSheet!$B$1");
       
+      // Check is was stored correctly
+      assertEquals("Cost*[XRefCalcData.xls]MarkupSheet!$B$1", 
cell.getCellFormula());
+      
+      // Check it evaluates correctly
+      eval.evaluateFormulaCell(cell);
+      assertEquals(24.60*1.8, cell.getNumericCellValue());
+      
+      
+      // Try to add a formula for a new external workbook, won't be allowed to 
start
+      try {
+          cell = wb.getSheetAt(0).getRow(1).createCell(42);
+          cell.setCellFormula("[alt.xls]Sheet0!$A$1");
+          fail("New workbook not linked, shouldn't be able to add");
+      } catch(Exception e) {}
+      
+      // Link our new workbook
+      HSSFWorkbook alt = new HSSFWorkbook();
+      alt.createSheet().createRow(0).createCell(0).setCellValue("In another 
workbook");
+      wb.linkExternalWorkbook("alt.xls", alt);
+      
+      // Now add a formula that refers to our new workbook
+      cell.setCellFormula("[alt.xls]Sheet0!$A$1");
+      assertEquals("[alt.xls]Sheet0!$A$1", cell.getCellFormula());
+
       // Evaluate it, without a link to that workbook
       try {
           eval.evaluate(cell);
@@ -246,8 +267,6 @@ public final class TestHSSFFormulaEvalua
       } catch(Exception e) {}
       
       // Add a link, check it does
-      HSSFWorkbook alt = new HSSFWorkbook();
-      alt.createSheet().createRow(0).createCell(0).setCellValue("In another 
workbook");
       HSSFFormulaEvaluator.setupEnvironment(
               new String[] { "XRefCalc.xls", "XRefCalcData.xls", "alt.xls" },
               new HSSFFormulaEvaluator[] {
@@ -256,10 +275,33 @@ public final class TestHSSFFormulaEvalua
                     new HSSFFormulaEvaluator(alt)
               }
       );
+      eval.evaluateFormulaCell(cell);
+      assertEquals("In another workbook", cell.getStringCellValue());
+      
+      
+      // Save and re-load
+      wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+      eval = new HSSFFormulaEvaluator(wb);
+      HSSFFormulaEvaluator.setupEnvironment(
+              new String[] { "XRefCalc.xls", "XRefCalcData.xls", "alt.xls" },
+              new HSSFFormulaEvaluator[] {
+                    eval,
+                    new HSSFFormulaEvaluator(wbData),
+                    new HSSFFormulaEvaluator(alt)
+              }
+      );
+      
+      // Check the one referring to the previously existing workbook behaves
+      cell = wb.getSheetAt(0).getRow(1).getCell(40);
+      assertEquals("Cost*[XRefCalcData.xls]MarkupSheet!$B$1", 
cell.getCellFormula());
+      eval.evaluateFormulaCell(cell);
+      assertEquals(24.60*1.8, cell.getNumericCellValue());
       
-      eval.evaluate(cell);
+      // Now check the newly added reference
+      cell = wb.getSheetAt(0).getRow(1).getCell(42);
+      assertEquals("[alt.xls]Sheet0!$A$1", cell.getCellFormula());
+      eval.evaluateFormulaCell(cell);
       assertEquals("In another workbook", cell.getStringCellValue());
-*/      
    }
 
     public void testSharedFormulas(){



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

Reply via email to