You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ce...@apache.org on 2019/01/27 09:57:40 UTC

svn commit: r1852277 - in /poi/trunk: src/integrationtest/org/apache/poi/stress/ src/java/org/apache/poi/ss/formula/function/ src/java/org/apache/poi/ss/formula/ptg/ src/resources/main/org/apache/poi/ss/formula/function/ src/testcases/org/apache/poi/hs...

Author: centic
Date: Sun Jan 27 09:57:39 2019
New Revision: 1852277

URL: http://svn.apache.org/viewvc?rev=1852277&view=rev
Log:
Bug 60405: Add initial support for cetab functions so some macros can be
parsed
Add some function-definitions for Excel 4 Macros and missing functions
found in regression tests

Added:
    poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadataCetab.txt
    poi/trunk/src/testcases/org/apache/poi/ss/formula/function/ExcelCetabFunctionExtractor.java
    poi/trunk/test-data/spreadsheet/60405.xls
Modified:
    poi/trunk/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java
    poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionDataBuilder.java
    poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadata.java
    poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java
    poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataRegistry.java
    poi/trunk/src/java/org/apache/poi/ss/formula/ptg/AbstractFunctionPtg.java
    poi/trunk/src/java/org/apache/poi/ss/formula/ptg/ExpPtg.java
    poi/trunk/src/java/org/apache/poi/ss/formula/ptg/FuncVarPtg.java
    poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadata.txt
    poi/trunk/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java

Modified: poi/trunk/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java (original)
+++ poi/trunk/src/integrationtest/org/apache/poi/stress/AbstractFileHandler.java Sun Jan 27 09:57:39 2019
@@ -31,9 +31,11 @@ import java.util.Set;
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.extractor.POIOLE2TextExtractor;
 import org.apache.poi.extractor.POITextExtractor;
+import org.apache.poi.hssf.extractor.EventBasedExcelExtractor;
 import org.apache.poi.ooxml.extractor.ExtractorFactory;
 import org.apache.poi.hpsf.extractor.HPSFPropertiesExtractor;
 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.ss.extractor.ExcelExtractor;
 import org.apache.poi.util.IOUtils;
 import org.apache.xmlbeans.XmlException;
 
@@ -83,6 +85,7 @@ public abstract class AbstractFileHandle
         long modified = file.lastModified();
         
         POITextExtractor extractor = null;
+        String fileAndParentName = file.getParentFile().getName() + "/" + file.getName();
         try {
             extractor = ExtractorFactory.createExtractor(file);
             assertNotNull("Should get a POITextExtractor but had none for file " + file, extractor);
@@ -95,7 +98,7 @@ public abstract class AbstractFileHandle
             assertNotNull(metadataExtractor.getText());
 
             assertFalse("Expected Extraction to fail for file " + file + " and handler " + this + ", but did not fail!",
-                    EXPECTED_EXTRACTOR_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName()));
+                    EXPECTED_EXTRACTOR_FAILURES.contains(fileAndParentName));
 
             assertEquals("File should not be modified by extractor", length, file.length());
             assertEquals("File should not be modified by extractor", modified, file.lastModified());
@@ -111,8 +114,24 @@ public abstract class AbstractFileHandle
                     assertNotNull(text);
                 }
             }
+
+            // test again with including formulas and cell-comments as this caused some bugs
+            if(extractor instanceof ExcelExtractor &&
+                    // comment-extraction and formula extraction are not well supported in event based extraction
+                    !(extractor instanceof EventBasedExcelExtractor)) {
+                ((ExcelExtractor)extractor).setFormulasNotResults(true);
+
+                String text = extractor.getText();
+                assertNotNull(text);
+                // */
+
+                ((ExcelExtractor) extractor).setIncludeCellComments(true);
+
+                text = extractor.getText();
+                assertNotNull(text);
+            }
         } catch (IllegalArgumentException e) {
-            if(!EXPECTED_EXTRACTOR_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName())) {
+            if(!EXPECTED_EXTRACTOR_FAILURES.contains(fileAndParentName)) {
                 throw e;
             }
         } catch (EncryptedDocumentException e) {

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionDataBuilder.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionDataBuilder.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionDataBuilder.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionDataBuilder.java Sun Jan 27 09:57:39 2019
@@ -81,8 +81,7 @@ final class FunctionDataBuilder {
 		FunctionMetadata[] jumbledArray =  new FunctionMetadata[_functionDataByName.size()];
 		_functionDataByName.values().toArray(jumbledArray);
 		FunctionMetadata[] fdIndexArray = new FunctionMetadata[_maxFunctionIndex+1];
-		for (int i = 0; i < jumbledArray.length; i++) {
-			FunctionMetadata fd = jumbledArray[i];
+		for (FunctionMetadata fd : jumbledArray) {
 			fdIndexArray[fd.getIndex()] = fd;
 		}
 

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadata.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadata.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadata.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadata.java Sun Jan 27 09:57:39 2019
@@ -31,6 +31,7 @@ public final class FunctionMetadata {
 	 * to make that file more version neutral.
 	 * @see org.apache.poi.ss.formula.FormulaParser#validateNumArgs(int, FunctionMetadata)
 	 */
+	@SuppressWarnings("JavadocReference")
 	private static final short FUNCTION_MAX_PARAMS = 30;
 
 	private final int _index;
@@ -49,27 +50,35 @@ public final class FunctionMetadata {
 		_returnClassCode = returnClassCode;
 		_parameterClassCodes = (parameterClassCodes == null) ? null : parameterClassCodes.clone();
 	}
+
 	public int getIndex() {
 		return _index;
 	}
+
 	public String getName() {
 		return _name;
 	}
+
 	public int getMinParams() {
 		return _minParams;
 	}
+
 	public int getMaxParams() {
 		return _maxParams;
 	}
+
 	public boolean hasFixedArgsLength() {
 		return _minParams == _maxParams;
 	}
+
 	public byte getReturnClassCode() {
 		return _returnClassCode;
 	}
+
 	public byte[] getParameterClassCodes() {
 		return _parameterClassCodes.clone();
 	}
+
 	/**
 	 * Some varags functions (like VLOOKUP) have a specific limit to the number of arguments that 
 	 * can be passed.  Other functions (like SUM) don't have such a limit.  For those functions,
@@ -80,11 +89,8 @@ public final class FunctionMetadata {
 	public boolean hasUnlimitedVarags() {
 		return FUNCTION_MAX_PARAMS == _maxParams;
 	}
+
 	public String toString() {
-		StringBuffer sb = new StringBuffer(64);
-		sb.append(getClass().getName()).append(" [");
-		sb.append(_index).append(" ").append(_name);
-		sb.append("]");
-		return sb.toString();
+		return getClass().getName() + " [" + _index + " " + _name + "]";
 	}
 }

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataReader.java Sun Jan 27 09:57:39 2019
@@ -41,6 +41,7 @@ final class FunctionMetadataReader {
 	private static final int MAX_RECORD_LENGTH = 100_000;
 
 	private static final String METADATA_FILE_NAME = "functionMetadata.txt";
+	private static final String METADATA_FILE_NAME_CETAB = "functionMetadataCetab.txt";
 
 	/** plain ASCII text metadata file uses three dots for ellipsis */
 	private static final String ELLIPSIS = "...";
@@ -52,51 +53,56 @@ final class FunctionMetadataReader {
 	private static final String[] DIGIT_ENDING_FUNCTION_NAMES = {
 		// Digits at the end of a function might be due to a left-over footnote marker.
 		// except in these cases
-		"LOG10", "ATAN2", "DAYS360", "SUMXMY2", "SUMX2MY2", "SUMX2PY2",
+		"LOG10", "ATAN2", "DAYS360", "SUMXMY2", "SUMX2MY2", "SUMX2PY2", "A1.R1C1",
 	};
 	private static final Set<String> DIGIT_ENDING_FUNCTION_NAMES_SET = new HashSet<>(Arrays.asList(DIGIT_ENDING_FUNCTION_NAMES));
 
 	public static FunctionMetadataRegistry createRegistry() {
-	    try {
-    		InputStream is = FunctionMetadataReader.class.getResourceAsStream(METADATA_FILE_NAME);
-    		if (is == null) {
-    			throw new RuntimeException("resource '" + METADATA_FILE_NAME + "' not found");
-    		}
-    
-    		try {
-        		try(BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
-        		    FunctionDataBuilder fdb = new FunctionDataBuilder(400);
-        
-        			while (true) {
-        				String line = br.readLine();
-        				if (line == null) {
-        					break;
-        				}
-        				if (line.length() < 1 || line.charAt(0) == '#') {
-        					continue;
-        				}
-        				String trimLine = line.trim();
-        				if (trimLine.length() < 1) {
-        					continue;
-        				}
-        				processLine(fdb, line);
-        			}
-
-        			return fdb.build();
-        		}
-    		} finally {
-    		    is.close();
-    		}
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-	    } 
+		FunctionDataBuilder fdb = new FunctionDataBuilder(800);
+		readResourceFile(fdb, METADATA_FILE_NAME);
+		return fdb.build();
+	}
+
+	public static FunctionMetadataRegistry createRegistryCetab() {
+		FunctionDataBuilder fdb = new FunctionDataBuilder(800);
+		readResourceFile(fdb, METADATA_FILE_NAME_CETAB);
+		return fdb.build();
+	}
+
+	private static void readResourceFile(FunctionDataBuilder fdb, String resourceFile) {
+		try (InputStream is = FunctionMetadataReader.class.getResourceAsStream(resourceFile)) {
+			if (is == null) {
+				throw new RuntimeException("resource '" + resourceFile + "' not found");
+			}
+
+			try(BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
+
+				while (true) {
+					String line = br.readLine();
+					if (line == null) {
+						break;
+					}
+					if (line.length() < 1 || line.charAt(0) == '#') {
+						continue;
+					}
+					String trimLine = line.trim();
+					if (trimLine.length() < 1) {
+						continue;
+					}
+					processLine(fdb, line);
+				}
+			}
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
 	}
 
 	private static void processLine(FunctionDataBuilder fdb, String line) {
 
 		String[] parts = TAB_DELIM_PATTERN.split(line, -2);
 		if(parts.length != 8) {
-			throw new RuntimeException("Bad line format '" + line + "' - expected 8 data fields");
+			throw new RuntimeException("Bad line format '" + line + "' - expected 8 data fields delimited by tab, " +
+					"but had " + parts.length + ": " + Arrays.toString(parts));
 		}
 		int functionIndex = parseInt(parts[0]);
 		String functionName = parts[1];

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataRegistry.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataRegistry.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataRegistry.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/function/FunctionMetadataRegistry.java Sun Jan 27 09:57:39 2019
@@ -37,6 +37,7 @@ public final class FunctionMetadataRegis
 	public static final short FUNCTION_INDEX_EXTERNAL = 255;
 
 	private static FunctionMetadataRegistry _instance;
+	private static FunctionMetadataRegistry _instanceCetab;
 
 	private final FunctionMetadata[] _functionDataByIndex;
 	private final Map<String, FunctionMetadata> _functionDataByName;
@@ -48,6 +49,13 @@ public final class FunctionMetadataRegis
 		return _instance;
 	}
 
+	private static FunctionMetadataRegistry getInstanceCetab() {
+		if (_instanceCetab == null) {
+			_instanceCetab = FunctionMetadataReader.createRegistryCetab();
+		}
+		return _instanceCetab;
+	}
+
 	/* package */ FunctionMetadataRegistry(FunctionMetadata[] functionDataByIndex, Map<String, FunctionMetadata> functionDataByName) {
 		_functionDataByIndex = (functionDataByIndex == null) ? null : functionDataByIndex.clone();
 		_functionDataByName = functionDataByName;
@@ -62,6 +70,10 @@ public final class FunctionMetadataRegis
 		return getInstance().getFunctionByIndexInternal(index);
 	}
 
+	public static FunctionMetadata getCetabFunctionByIndex(int index) {
+		return getInstanceCetab().getFunctionByIndexInternal(index);
+	}
+
 	private FunctionMetadata getFunctionByIndexInternal(int index) {
 		return _functionDataByIndex[index];
 	}
@@ -74,7 +86,11 @@ public final class FunctionMetadataRegis
 	public static short lookupIndexByName(String name) {
 		FunctionMetadata fd = getInstance().getFunctionByNameInternal(name);
 		if (fd == null) {
-			return -1;
+			// also try the cetab functions
+			fd = getInstanceCetab().getFunctionByNameInternal(name);
+			if (fd == null) {
+				return -1;
+			}
 		}
 		return (short) fd.getIndex();
 	}
@@ -83,8 +99,12 @@ public final class FunctionMetadataRegis
 		return _functionDataByName.get(name);
 	}
 
-
 	public static FunctionMetadata getFunctionByName(String name) {
-		return getInstance().getFunctionByNameInternal(name);
+		FunctionMetadata fm = getInstance().getFunctionByNameInternal(name);
+		if(fm == null) {
+			return getInstanceCetab().getFunctionByNameInternal(name);
+		}
+
+		return fm;
 	}
 }

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/ptg/AbstractFunctionPtg.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/ptg/AbstractFunctionPtg.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/ptg/AbstractFunctionPtg.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/ptg/AbstractFunctionPtg.java Sun Jan 27 09:57:39 2019
@@ -123,13 +123,22 @@ public abstract class AbstractFunctionPt
         return ix >= 0;
     }
 
-    protected final String lookupName(short index) {
+    protected String lookupName(short index) {
+        return lookupName(index, false);
+    }
+
+    protected final String lookupName(short index, boolean isCetab) {
         if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) {
             return "#external#";
         }
-        FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(index);
+        final FunctionMetadata fm;
+        if(isCetab) {
+            fm = FunctionMetadataRegistry.getCetabFunctionByIndex(index);
+        } else {
+            fm = FunctionMetadataRegistry.getFunctionByIndex(index);
+        }
         if(fm == null) {
-            throw new RuntimeException("bad function index (" + index + ")");
+            throw new RuntimeException("bad function index (" + index + ", " + isCetab + ")");
         }
         return fm.getName();
     }

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/ptg/ExpPtg.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/ptg/ExpPtg.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/ptg/ExpPtg.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/ptg/ExpPtg.java Sun Jan 27 09:57:39 2019
@@ -69,9 +69,6 @@ public final class ExpPtg extends Contro
 
     @Override
     public String toString() {
-        StringBuffer buffer = new StringBuffer("[Array Formula or Shared Formula]\n");
-        buffer.append("row = ").append(getRow()).append("\n");
-        buffer.append("col = ").append(getColumn()).append("\n");
-        return buffer.toString();
+        return "[Array Formula or Shared Formula]\n" + "row = " + getRow() + "\n" + "col = " + getColumn() + "\n";
     }
 }

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/ptg/FuncVarPtg.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/ptg/FuncVarPtg.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/ptg/FuncVarPtg.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/ptg/FuncVarPtg.java Sun Jan 27 09:57:39 2019
@@ -18,6 +18,8 @@
 package org.apache.poi.ss.formula.ptg;
 import org.apache.poi.ss.formula.function.FunctionMetadata;
 import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.LittleEndianInput;
 import org.apache.poi.util.LittleEndianOutput;
 
@@ -25,24 +27,29 @@ import org.apache.poi.util.LittleEndianO
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 public final class FuncVarPtg extends AbstractFunctionPtg{
-
     public final static byte sid  = 0x22;
     private final static int  SIZE = 4;
 
+    // See spec at 2.5.198.63 PtgFuncVar
+    private static final BitField ceFunc = BitFieldFactory.getInstance(0xF000);
+
     /**
      * Single instance of this token for 'sum() taking a single argument'
      */
     public static final OperationPtg SUM = FuncVarPtg.create("SUM", 1);
 
-    private FuncVarPtg(int functionIndex, int returnClass, byte[] paramClasses, int numArgs) {
+    private final boolean _isCetab;
+
+    private FuncVarPtg(int functionIndex, int returnClass, byte[] paramClasses, int numArgs, boolean isCetab) {
         super(functionIndex, returnClass, paramClasses, numArgs);
+        _isCetab = isCetab;
     }
 
     /**Creates new function pointer from a byte array
      * usually called while reading an excel file.
      */
     public static FuncVarPtg create(LittleEndianInput in)  {
-        return create(in.readByte(), in.readShort());
+        return create(in.readByte(), in.readUShort());
     }
 
     /**
@@ -53,12 +60,25 @@ public final class FuncVarPtg extends Ab
     }
 
     private static FuncVarPtg create(int numArgs, int functionIndex) {
-        FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(functionIndex);
-        if(fm == null) {
+        final FunctionMetadata fm;
+        boolean isCetab = ceFunc.isSet(functionIndex);
+        if(isCetab) {
+            functionIndex = ceFunc.clear(functionIndex);
+            fm = FunctionMetadataRegistry.getCetabFunctionByIndex(functionIndex);
+        } else {
+            fm = FunctionMetadataRegistry.getFunctionByIndex(functionIndex);
+        }
+
+        if (fm == null) {
             // Happens only as a result of a call to FormulaParser.parse(), with a non-built-in function name
-            return new FuncVarPtg(functionIndex, Ptg.CLASS_VALUE, new byte[] {Ptg.CLASS_VALUE}, numArgs);
+            return new FuncVarPtg(functionIndex, Ptg.CLASS_VALUE, new byte[]{Ptg.CLASS_VALUE}, numArgs, isCetab);
         }
-        return new FuncVarPtg(functionIndex, fm.getReturnClassCode(), fm.getParameterClassCodes(), numArgs);
+        return new FuncVarPtg(functionIndex, fm.getReturnClassCode(), fm.getParameterClassCodes(), numArgs, isCetab);
+    }
+
+    @Override
+    protected String lookupName(short index) {
+        return lookupName(index, _isCetab);
     }
 
     public void write(LittleEndianOutput out) {

Modified: poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadata.txt
URL: http://svn.apache.org/viewvc/poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadata.txt?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadata.txt (original)
+++ poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadata.txt Sun Jan 27 09:57:39 2019
@@ -74,6 +74,8 @@
 50	TREND	1	3	A	R R R		x
 51	LOGEST	1	2	A	R R		x
 52	GROWTH	1	3	A	R R R		x
+53	GOTO	1	1	R	R		
+55	RETURN	1	1	V	V		
 56	PV	3	5	V	V V V V V		
 # Built-In Sheet Functions in BIFF2
 57	FV	3	5	V	V V V V V		
@@ -98,8 +100,13 @@
 76	ROWS	1	1	V	A		
 77	COLUMNS	1	1	V	A		
 78	OFFSET	3	5	R	R V V V V	x	
+79	ABSREF	2	2	R	V R		
+80	RELREF	2	2	R	V V		
+81	ARGUMENT	0	3	V	V V R		
 82	SEARCH	2	3	V	V V V		
 83	TRANSPOSE	1	1	A	A		
+84	ERROR	0	2	V	V R		
+85	STEP	0	0				
 86	TYPE	1	1	V	V		
 97	ATAN2	2	2	V	V V		
 98	ASIN	1	1	V	V		
@@ -109,6 +116,7 @@
 102	VLOOKUP	3	3	V	V R R		x
 105	ISREF	1	1	V	R		
 109	LOG	1	2	V	V V		
+110	EXEC	1	4	V	V V V V		
 111	CHAR	1	1	V	V		
 112	LOWER	1	1	V	V		
 113	UPPER	1	1	V	V		
@@ -134,6 +142,7 @@
 143	SYD	4	4	V	V V V V		
 144	DDB	4	5	V	V V V V V		
 148	INDIRECT	1	2	R	V V	x	
+150	CALL	1	3	V	V R R		
 162	CLEAN	1	1	V	V		
 163	MDETERM	1	1	V	A		
 164	MINVERSE	1	1	A	A		
@@ -143,6 +152,10 @@
 169	COUNTA	0	30	V	R		
 183	PRODUCT	0	30	V	R		
 184	FACT	1	1	V	V		
+185	GET.CELL	1	2	V	V R		
+186	GET.WORKSPACE	1	1	V	V		
+187	GET.WINDOW	1	2	V	V V		
+188	GET.DOCUMENT	1	2	V	V V		
 189	DPRODUCT	3	3	V	R R R		
 190	ISNONTEXT	1	1	V	V		
 193	STDEVP	1	30	V	R		
@@ -174,6 +187,7 @@
 220	DAYS360	2	2	V	V V		x
 221	TODAY	0	0	V	-	x	
 222	VDB	5	7	V	V V V V V V V		
+225	END.IF	0	0				
 227	MEDIAN	1	30	V	R ...		
 228	SUMPRODUCT	1	30	V	A ...		
 229	SINH	1	1	V	V		
@@ -187,11 +201,20 @@
 # New Built-In Sheet Functions in BIFF4
 14	FIXED	1	3	V	V V V		x
 204	USDOLLAR	1	2	V	V V		x
+238	LAST.ERROR	0	0				
 215	DBCS	1	1	V	V		x
 216	RANK	2	3	V	V R V		
 247	DB	4	5	V	V V V V V		
 252	FREQUENCY	2	2	A	R R		
+257	EVALUATE	1	1	V	V		
 261	ERROR.TYPE	1	1	V	V		
+262	APP.TITLE	0	1	V	V		
+263	WINDOW.TITLE	0	1	V	V		
+264	SAVE.TOOLBAR	0	2	V	V V		
+265	ENABLE.TOOL	3	3	V	V V V		
+266	PRESS.TOOL	3	3	V	V V V		
+267	REGISTER.ID	2	3	V	V V V		
+268	GET.WORKBOOK	1	2	V	V V		
 269	AVEDEV	1	30	V	R ...		
 270	BETADIST	3	5	V	V V V V V		
 271	GAMMALN	1	1	V	V		
@@ -272,13 +295,13 @@
 350	ISPMT	4	4	V	V V V V		
 351	DATEDIF	3	3	V	V V V		
 352	DATESTRING	1	1	V	V		
-353	NUMBERSTRING	2	2	V	V V		
-354	ROMAN	1	2	V	V V		
-# New Built-In Sheet Functions in BIFF8
-358	GETPIVOTDATA	2	30	V	V R ...		
-359	HYPERLINK	1	2	V	V V		
-360	PHONETIC	1	1	V	R		
-361	AVERAGEA	1	30	V	R ...		
+353	NUMBERSTRING	2	2	V	V V		
+354	ROMAN	1	2	V	V V		
+# New Built-In Sheet Functions in BIFF8
+358	GETPIVOTDATA	2	30	V	V R ...		
+359	HYPERLINK	1	2	V	V V		
+360	PHONETIC	1	1	V	R		
+361	AVERAGEA	1	30	V	R ...		
 362	MAXA	1	30	V	R ...		
 363	MINA	1	30	V	R ...		
 364	STDEVPA	1	30	V	R ...		

Added: poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadataCetab.txt
URL: http://svn.apache.org/viewvc/poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadataCetab.txt?rev=1852277&view=auto
==============================================================================
--- poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadataCetab.txt (added)
+++ poi/trunk/src/resources/main/org/apache/poi/ss/formula/function/functionMetadataCetab.txt Sun Jan 27 09:57:39 2019
@@ -0,0 +1,417 @@
+# 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.
+
+# Created by (org.apache.poi.ss.formula.function.ExcelCetabFunctionExtractor)
+# from source file 'org/apache/poi/ss/formula/function/functionMetadataCetab-PDF.txt'
+#
+#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )
+
+# 
+0	BEEP	0	0				
+1	OPEN	0	0				
+2	OPEN.LINKS	0	0				
+3	CLOSE.ALL	0	0				
+4	SAVE	0	0				
+5	SAVE.AS	0	0				
+6	FILE.DELETE	0	0				
+7	PAGE.SETUP	0	0				
+8	PRINT	0	0				
+9	PRINTER.SETUP	0	0				
+10	QUIT	0	0				
+11	NEW.WINDOW	0	0				
+12	ARRANGE.ALL	0	0				
+13	WINDOW.SIZE	0	0				
+14	WINDOW.MOVE	0	0				
+15	FULL	0	0				
+16	CLOSE	0	0				
+17	RUN	0	0				
+22	SET.PRINT.AREA	0	0				
+23	SET.PRINT.TITLES	0	0				
+24	SET.PAGE.BREAK	0	0				
+25	REMOVE.PAGE.BREAK	0	0				
+26	FONT	0	0				
+27	DISPLAY	0	0				
+28	PROTECT.DOCUMENT	0	0				
+29	PRECISION	0	0				
+30	A1.R1C1	0	0				
+31	CALCULATE.NOW	0	0				
+32	CALCULATION	0	0				
+34	DATA.FIND	0	0				
+35	EXTRACT	0	0				
+36	DATA.DELETE	0	0				
+37	SET.DATABASE	0	0				
+38	SET.CRITERIA	0	0				
+39	SORT	0	0				
+40	DATA.SERIES	0	0				
+41	TABLE	0	0				
+42	FORMAT.NUMBER	0	0				
+43	ALIGNMENT	0	0				
+44	STYLE	0	0				
+45	BORDER	0	0				
+46	CELL.PROTECTION	0	0				
+47	COLUMN.WIDTH	0	0				
+48	UNDO	0	0				
+49	CUT	0	0				
+50	COPY	0	0				
+51	PASTE	0	0				
+52	CLEAR	0	0				
+53	PASTE.SPECIAL	0	0				
+54	EDIT.DELETE	0	0				
+55	INSERT	0	0				
+56	FILL.RIGHT	0	0				
+57	FILL.DOWN	0	0				
+61	DEFINE.NAME	0	0				
+62	CREATE.NAMES	0	0				
+63	FORMULA.GOTO	0	0				
+64	FORMULA.FIND	0	0				
+65	SELECT.LAST.CELL	0	0				
+66	SHOW.ACTIVE.CELL	0	0				
+67	GALLERY.AREA	0	0				
+68	GALLERY.BAR	0	0				
+69	GALLERY.COLUMN	0	0				
+70	GALLERY.LINE	0	0				
+71	GALLERY.PIE	0	0				
+72	GALLERY.SCATTER	0	0				
+73	COMBINATION	0	0				
+74	PREFERRED	0	0				
+75	ADD.OVERLAY	0	0				
+76	GRIDLINES	0	0				
+77	SET.PREFERRED	0	0				
+78	AXES	0	0				
+79	LEGEND	0	0				
+80	ATTACH.TEXT	0	0				
+81	ADD.ARROW	0	0				
+82	SELECT.CHART	0	0				
+83	SELECT.PLOT.AREA	0	0				
+84	PATTERNS	0	0				
+85	MAIN.CHART	0	0				
+86	OVERLAY	0	0				
+87	SCALE	0	0				
+88	FORMAT.LEGEND	0	0				
+89	FORMAT.TEXT	0	0				
+90	EDIT.REPEAT	0	0				
+91	PARSE	0	0				
+92	JUSTIFY	0	0				
+93	HIDE	0	0				
+94	UNHIDE	0	0				
+95	WORKSPACE	0	0				
+96	FORMULA	0	0				
+97	FORMULA.FILL	0	0				
+98	FORMULA.ARRAY	0	0				
+99	DATA.FIND.NEXT	0	0				
+100	DATA.FIND.PREV	0	0				
+101	FORMULA.FIND.NEXT	0	0				
+102	FORMULA.FIND.PREV	0	0				
+103	ACTIVATE	0	0				
+104	ACTIVATE.NEXT	0	0				
+105	ACTIVATE.PREV	0	0				
+106	UNLOCKED.NEXT	0	0				
+107	UNLOCKED.PREV	0	0				
+108	COPY.PICTURE	0	0				
+109	SELECT	0	0				
+110	DELETE.NAME	0	0				
+111	DELETE.FORMAT	0	0				
+112	VLINE	0	0				
+113	HLINE	0	0				
+114	VPAGE	0	0				
+115	HPAGE	0	0				
+116	VSCROLL	0	0				
+117	HSCROLL	0	0				
+118	ALERT	0	0				
+119	NEW	0	0				
+120	CANCEL.COPY	0	0				
+121	SHOW.CLIPBOARD	0	0				
+122	MESSAGE	0	0				
+124	PASTE.LINK	0	0				
+125	APP.ACTIVATE	0	0				
+126	DELETE.ARROW	0	0				
+127	ROW.HEIGHT	0	0				
+128	FORMAT.MOVE	0	0				
+129	FORMAT.SIZE	0	0				
+130	FORMULA.REPLACE	0	0				
+131	SEND.KEYS	0	0				
+132	SELECT.SPECIAL	0	0				
+133	APPLY.NAMES	0	0				
+134	REPLACE.FONT	0	0				
+135	FREEZE.PANES	0	0				
+136	SHOW.INFO	0	0				
+137	SPLIT	0	0				
+138	ON.WINDOW	0	0				
+139	ON.DATA	0	0				
+140	DISABLE.INPUT	0	0				
+142	OUTLINE	0	0				
+143	LIST.NAMES	0	0				
+144	FILE.CLOSE	0	0				
+145	SAVE.WORKBOOK	0	0				
+146	DATA.FORM	0	0				
+147	COPY.CHART	0	0				
+148	ON.TIME	0	0				
+149	WAIT	0	0				
+150	FORMAT.FONT	0	0				
+151	FILL.UP	0	0				
+152	FILL.LEFT	0	0				
+153	DELETE.OVERLAY	0	0				
+155	SHORT.MENUS	0	0				
+159	SET.UPDATE.STATUS	0	0				
+161	COLOR.PALETTE	0	0				
+162	DELETE.STYLE	0	0				
+163	WINDOW.RESTORE	0	0				
+164	WINDOW.MAXIMIZE	0	0				
+166	CHANGE.LINK	0	0				
+167	CALCULATE.DOCUMENT	0	0				
+168	ON.KEY	0	0				
+169	APP.RESTORE	0	0				
+170	APP.MOVE	0	0				
+171	APP.SIZE	0	0				
+172	APP.MINIMIZE	0	0				
+173	APP.MAXIMIZE	0	0				
+174	BRING.TO.FRONT	0	0				
+175	SEND.TO.BACK	0	0				
+185	MAIN.CHART.TYPE	0	0				
+186	OVERLAY.CHART.TYPE	0	0				
+187	SELECT.END	0	0				
+188	OPEN.MAIL	0	0				
+189	SEND.MAIL	0	0				
+190	STANDARD.FONT	0	0				
+191	CONSOLIDATE	0	0				
+192	SORT.SPECIAL	0	0				
+193	GALLERY.3D.AREA	0	0				
+194	GALLERY.3D.COLUMN	0	0				
+195	GALLERY.3D.LINE	0	0				
+196	GALLERY.3D.PIE	0	0				
+197	VIEW.3D	0	0				
+198	GOAL.SEEK	0	0				
+199	WORKGROUP	0	0				
+200	FILL.GROUP	0	0				
+201	UPDATE.LINK	0	0				
+202	PROMOTE	0	0				
+203	DEMOTE	0	0				
+204	SHOW.DETAIL	0	0				
+206	UNGROUP	0	0				
+207	OBJECT.PROPERTIES	0	0				
+208	SAVE.NEW.OBJECT	0	0				
+209	SHARE	0	0				
+210	SHARE.NAME	0	0				
+211	DUPLICATE	0	0				
+212	APPLY.STYLE	0	0				
+213	ASSIGN.TO.OBJECT	0	0				
+214	OBJECT.PROTECTION	0	0				
+215	HIDE.OBJECT	0	0				
+216	SET.EXTRACT	0	0				
+217	CREATE.PUBLISHER	0	0				
+218	SUBSCRIBE.TO	0	0				
+219	ATTRIBUTES	0	0				
+220	SHOW.TOOLBAR	0	0				
+222	PRINT.PREVIEW	0	0				
+223	EDIT.COLOR	0	0				
+224	SHOW.LEVELS	0	0				
+225	FORMAT.MAIN	0	0				
+226	FORMAT.OVERLAY	0	0				
+227	ON.RECALC	0	0				
+228	EDIT.SERIES	0	0				
+229	DEFINE.STYLE	0	0				
+240	LINE.PRINT	0	0				
+243	ENTER.DATA	0	0				
+249	GALLERY.RADAR	0	0				
+250	MERGE.STYLES	0	0				
+251	EDITION.OPTIONS	0	0				
+252	PASTE.PICTURE	0	0				
+253	PASTE.PICTURE.LINK	0	0				
+254	SPELLING	0	0				
+256	ZOOM	0	0				
+259	INSERT.OBJECT	0	0				
+260	WINDOW.MINIMIZE	0	0				
+265	SOUND.NOTE	0	0				
+266	SOUND.PLAY	0	0				
+267	FORMAT.SHAPE	0	0				
+268	EXTEND.POLYGON	0	0				
+269	FORMAT.AUTO	0	0				
+272	GALLERY.3D.BAR	0	0				
+273	GALLERY.3D.SURFACE	0	0				
+274	FILL.AUTO	0	0				
+276	CUSTOMIZE.TOOLBAR	0	0				
+277	ADD.TOOL	0	0				
+278	EDIT.OBJECT	0	0				
+279	ON.DOUBLECLICK	0	0				
+280	ON.ENTRY	0	0				
+281	WORKBOOK.ADD	0	0				
+282	WORKBOOK.MOVE	0	0				
+283	WORKBOOK.COPY	0	0				
+284	WORKBOOK.OPTIONS	0	0				
+285	SAVE.WORKSPACE	0	0				
+288	CHART.WIZARD	0	0				
+289	DELETE.TOOL	0	0				
+290	MOVE.TOOL	0	0				
+291	WORKBOOK.SELECT	0	0				
+292	WORKBOOK.ACTIVATE	0	0				
+293	ASSIGN.TO.TOOL	0	0				
+295	COPY.TOOL	0	0				
+296	RESET.TOOL	0	0				
+297	CONSTRAIN.NUMERIC	0	0				
+298	PASTE.TOOL	0	0				
+302	WORKBOOK.NEW	0	0				
+305	SCENARIO.CELLS	0	0				
+306	SCENARIO.DELETE	0	0				
+307	SCENARIO.ADD	0	0				
+308	SCENARIO.EDIT	0	0				
+309	SCENARIO.SHOW	0	0				
+310	SCENARIO.SHOW.NEXT	0	0				
+311	SCENARIO.SUMMARY	0	0				
+312	PIVOT.TABLE.WIZARD	0	0				
+313	PIVOT.FIELD.PROPERTIES	0	0				
+314	PIVOT.FIELD	0	0				
+315	PIVOT.ITEM	0	0				
+316	PIVOT.ADD.FIELDS	0	0				
+318	OPTIONS.CALCULATION	0	0				
+319	OPTIONS.EDIT	0	0				
+320	OPTIONS.VIEW	0	0				
+321	ADDIN.MANAGER	0	0				
+322	MENU.EDITOR	0	0				
+323	ATTACH.TOOLBARS	0	0				
+324	VBAActivate	0	0				
+325	OPTIONS.CHART	0	0				
+328	VBA.INSERT.FILE	0	0				
+330	VBA.PROCEDURE.DEFINITION	0	0				
+336	ROUTING.SLIP	0	0				
+338	ROUTE.DOCUMENT	0	0				
+339	MAIL.LOGON	0	0				
+342	INSERT.PICTURE	0	0				
+343	EDIT.TOOL	0	0				
+344	GALLERY.DOUGHNUT	0	0				
+350	CHART.TREND	0	0				
+352	PIVOT.ITEM.PROPERTIES	0	0				
+354	WORKBOOK.INSERT	0	0				
+355	OPTIONS.TRANSITION	0	0				
+356	OPTIONS.GENERAL	0	0				
+370	FILTER.ADVANCED	0	0				
+373	MAIL.ADD.MAILER	0	0				
+374	MAIL.DELETE.MAILER	0	0				
+375	MAIL.REPLY	0	0				
+376	MAIL.REPLY.ALL	0	0				
+377	MAIL.FORWARD	0	0				
+378	MAIL.NEXT.LETTER	0	0				
+379	DATA.LABEL	0	0				
+380	INSERT.TITLE	0	0				
+381	FONT.PROPERTIES	0	0				
+382	MACRO.OPTIONS	0	0				
+383	WORKBOOK.HIDE	0	0				
+384	WORKBOOK.UNHIDE	0	0				
+385	WORKBOOK.DELETE	0	0				
+386	WORKBOOK.NAME	0	0				
+388	GALLERY.CUSTOM	0	0				
+390	ADD.CHART.AUTOFORMAT	0	0				
+391	DELETE.CHART.AUTOFORMAT	0	0				
+392	CHART.ADD.DATA	0	0				
+393	AUTO.OUTLINE	0	0				
+394	TAB.ORDER	0	0				
+395	SHOW.DIALOG	0	0				
+396	SELECT.ALL	0	0				
+397	UNGROUP.SHEETS	0	0				
+398	SUBTOTAL.CREATE	0	0				
+399	SUBTOTAL.REMOVE	0	0				
+400	RENAME.OBJECT	0	0				
+412	WORKBOOK.SCROLL	0	0				
+413	WORKBOOK.NEXT	0	0				
+414	WORKBOOK.PREV	0	0				
+415	WORKBOOK.TAB.SPLIT	0	0				
+416	FULL.SCREEN	0	0				
+417	WORKBOOK.PROTECT	0	0				
+420	SCROLLBAR.PROPERTIES	0	0				
+421	PIVOT.SHOW.PAGES	0	0				
+422	TEXT.TO.COLUMNS	0	0				
+423	FORMAT.CHARTTYPE	0	0				
+424	LINK.FORMAT	0	0				
+425	TRACER.DISPLAY	0	0				
+430	TRACER.NAVIGATE	0	0				
+431	TRACER.CLEAR	0	0				
+432	TRACER.ERROR	0	0				
+433	PIVOT.FIELD.GROUP	0	0				
+434	PIVOT.FIELD.UNGROUP	0	0				
+435	CHECKBOX.PROPERTIES	0	0				
+436	LABEL.PROPERTIES	0	0				
+437	LISTBOX.PROPERTIES	0	0				
+438	EDITBOX.PROPERTIES	0	0				
+439	PIVOT.REFRESH	0	0				
+440	LINK.COMBO	0	0				
+441	OPEN.TEXT	0	0				
+442	HIDE.DIALOG	0	0				
+443	SET.DIALOG.FOCUS	0	0				
+444	ENABLE.OBJECT	0	0				
+445	PUSHBUTTON.PROPERTIES	0	0				
+446	SET.DIALOG.DEFAULT	0	0				
+447	FILTER	0	0				
+448	FILTER.SHOW.ALL	0	0				
+449	CLEAR.OUTLINE	0	0				
+450	FUNCTION.WIZARD	0	0				
+451	ADD.LIST.ITEM	0	0				
+452	SET.LIST.ITEM	0	0				
+453	REMOVE.LIST.ITEM	0	0				
+454	SELECT.LIST.ITEM	0	0				
+455	SET.CONTROL.VALUE	0	0				
+456	SAVE.COPY.AS	0	0				
+458	OPTIONS.LISTS.ADD	0	0				
+459	OPTIONS.LISTS.DELETE	0	0				
+460	SERIES.AXES	0	0				
+461	SERIES.X	0	0				
+462	SERIES.Y	0	0				
+463	ERRORBAR.X	0	0				
+464	ERRORBAR.Y	0	0				
+465	FORMAT.CHART	0	0				
+466	SERIES.ORDER	0	0				
+467	MAIL.LOGOFF	0	0				
+468	CLEAR.ROUTING.SLIP	0	0				
+469	APP.ACTIVATE.MICROSOFT	0	0				
+470	MAIL.EDIT.MAILER	0	0				
+471	ON.SHEET	0	0				
+472	STANDARD.WIDTH	0	0				
+473	SCENARIO.MERGE	0	0				
+474	SUMMARY.INFO	0	0				
+475	FIND.FILE	0	0				
+476	ACTIVE.CELL.FONT	0	0				
+477	ENABLE.TIPWIZARD	0	0				
+478	VBA.MAKE.ADDIN	0	0				
+480	INSERTDATATABLE	0	0				
+481	WORKGROUP.OPTIONS	0	0				
+482	MAIL.SEND.MAILER	0	0				
+485	AUTOCORRECT	0	0				
+489	POST.DOCUMENT	0	0				
+491	PICKLIST	0	0				
+493	VIEW.SHOW	0	0				
+494	VIEW.DEFINE	0	0				
+495	VIEW.DELETE	0	0				
+509	SHEET.BACKGROUND	0	0				
+510	INSERT.MAP.OBJECT	0	0				
+511	OPTIONS.MENONO	0	0				
+517	MSOCHECKS	0	0				
+518	NORMAL	0	0				
+519	LAYOUT	0	0				
+520	RM.PRINT.AREA	0	0				
+521	CLEAR.PRINT.AREA	0	0				
+522	ADD.PRINT.AREA	0	0				
+523	MOVE.BRK	0	0				
+545	HIDECURR.NOTE	0	0				
+546	HIDEALL.NOTES	0	0				
+547	DELETE.NOTE	0	0				
+548	TRAVERSE.NOTES	0	0				
+549	ACTIVATE.NOTES	0	0				
+620	PROTECT.REVISIONS	0	0				
+621	UNPROTECT.REVISIONS	0	0				
+647	OPTIONS.ME	0	0				
+653	WEB.PUBLISH	0	0				
+667	NEWWEBQUERY	0	0				
+673	PIVOT.TABLE.CHART	0	0				
+753	OPTIONS.SAVE	0	0				
+755	OPTIONS.SPELL	0	0				
+808	HIDEALL.INKANNOTS	0	0				

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java?rev=1852277&r1=1852276&r2=1852277&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java Sun Jan 27 09:57:39 2019
@@ -34,7 +34,6 @@ import org.apache.poi.hssf.usermodel.HSS
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.LocaleUtil;
-import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -130,50 +129,42 @@ public final class TestExcelExtractor {
     public void testEventExtractor() throws Exception {
 		// First up, a simple file with string
 		//  based formulas in it
-		EventBasedExcelExtractor extractor1 = null;
-		try {
-	        extractor1 = new EventBasedExcelExtractor(
-	                new POIFSFileSystem(
-	                        HSSFTestDataSamples.openSampleFileStream("SimpleWithFormula.xls")
-	                )
-	        );
-    		extractor1.setIncludeSheetNames(true);
-        
-    		String text = extractor1.getText();
-    		assertEquals("Sheet1\nreplaceme\nreplaceme\nreplacemereplaceme\nSheet2\nSheet3\n", text);
-    
-    		extractor1.setIncludeSheetNames(false);
-    		extractor1.setFormulasNotResults(true);
-    
-    		text = extractor1.getText();
-    		assertEquals("replaceme\nreplaceme\nCONCATENATE(A1,A2)\n", text);
-		} finally {
-		    if (extractor1 != null) extractor1.close();
+		try (EventBasedExcelExtractor extractor1 = new EventBasedExcelExtractor(
+				new POIFSFileSystem(
+						HSSFTestDataSamples.openSampleFileStream("SimpleWithFormula.xls")
+				)
+		)) {
+			extractor1.setIncludeSheetNames(true);
+
+			String text = extractor1.getText();
+			assertEquals("Sheet1\nreplaceme\nreplaceme\nreplacemereplaceme\nSheet2\nSheet3\n", text);
+
+			extractor1.setIncludeSheetNames(false);
+			extractor1.setFormulasNotResults(true);
+
+			text = extractor1.getText();
+			assertEquals("replaceme\nreplaceme\nCONCATENATE(A1,A2)\n", text);
 		}
 
 		// Now, a slightly longer file with numeric formulas
-		EventBasedExcelExtractor extractor2 = null;
-		try {
-		    extractor2 = new EventBasedExcelExtractor(
-	                new POIFSFileSystem(
-	                        HSSFTestDataSamples.openSampleFileStream("sumifformula.xls")
-	                )
-	        );		    
-		    
-    		extractor2.setIncludeSheetNames(false);
-    		extractor2.setFormulasNotResults(true);
-    
-    		String text = extractor2.getText();
-    		assertEquals(
-    				"1000\t1\tSUMIF(A1:A5,\">4000\",B1:B5)\n" +
-    				"2000\t2\n" +
-    				"3000\t3\n" +
-    				"4000\t4\n" +
-    				"5000\t5\n",
-    				text
-    		);
-		} finally {
-		    if (extractor2 != null) extractor2.close();
+		try (EventBasedExcelExtractor extractor2 = new EventBasedExcelExtractor(
+				new POIFSFileSystem(
+						HSSFTestDataSamples.openSampleFileStream("sumifformula.xls")
+				)
+		)) {
+
+			extractor2.setIncludeSheetNames(false);
+			extractor2.setFormulasNotResults(true);
+
+			String text = extractor2.getText();
+			assertEquals(
+					"1000\t1\tSUMIF(A1:A5,\">4000\",B1:B5)\n" +
+							"2000\t2\n" +
+							"3000\t3\n" +
+							"4000\t4\n" +
+							"5000\t5\n",
+					text
+			);
 		}
 	}
 
@@ -372,4 +363,25 @@ public final class TestExcelExtractor {
 			assertContains(txt, "NONBUSINESS");
 		}
 	}
+
+	@Test
+	public void test60405a() throws IOException {
+		//bug 61045. File is govdocs1 626534
+		try (ExcelExtractor extractor = createExtractor("60405.xls")) {
+			String txt = extractor.getText();
+			assertContains(txt, "Macro1");
+			assertContains(txt, "Macro2");
+		}
+	}
+
+	@Test
+	public void test60405b() throws IOException {
+		//bug 61045. File is govdocs1 626534
+		try (ExcelExtractor extractor = createExtractor("60405.xls")) {
+			extractor.setFormulasNotResults(true);
+			String txt = extractor.getText();
+			assertContains(txt, "Macro1");
+			assertContains(txt, "Macro2");
+		}
+	}
 }

Added: poi/trunk/src/testcases/org/apache/poi/ss/formula/function/ExcelCetabFunctionExtractor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/formula/function/ExcelCetabFunctionExtractor.java?rev=1852277&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/formula/function/ExcelCetabFunctionExtractor.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/ss/formula/function/ExcelCetabFunctionExtractor.java Sun Jan 27 09:57:39 2019
@@ -0,0 +1,378 @@
+/* ====================================================================
+   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.ss.formula.function;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class is not used during normal POI run-time but is used at development time to generate
+ * the file 'functionMetadataCetab.txt'.  There are more than 300 built-in functions in Excel in
+ * the Cetab and the intention of this class is to make it easier to maintain the metadata, by extracting
+ * it from a reliable source.
+ */
+public final class ExcelCetabFunctionExtractor {
+
+	private static final String SOURCE_DOC_FILE_NAME = "functionMetadataCetab-PDF.txt";
+
+	/**
+	 * For simplicity, the output file is strictly simple ASCII.
+	 * This method detects any unexpected characters.
+	 */
+	/* package */ static boolean isSimpleAscii(char c) {
+
+		if (c>=0x21 && c<=0x7E) {
+			// everything from '!' to '~' (includes letters, digits, punctuation
+			return true;
+		}
+		// some specific whitespace chars below 0x21:
+		switch(c) {
+			case ' ':
+			case '\t':
+			case '\r':
+			case '\n':
+				return true;
+		}
+		return false;
+	}
+
+
+	private static final class FunctionData {
+		// special characters from the ooo document
+		private static final int CHAR_ELLIPSIS_8230 = 8230;
+		private static final int CHAR_NDASH_8211 = 8211;
+
+		private final int _index;
+		private final boolean _hasFootnote;
+		private final String _name;
+		private final int _minParams;
+		private final int _maxParams;
+		private final String _returnClass;
+		private final String _paramClasses;
+		private final boolean _isVolatile;
+
+		public FunctionData(int funcIx, boolean hasFootnote, String funcName, int minParams, int maxParams,
+					String returnClass, String paramClasses, boolean isVolatile) {
+			_index = funcIx;
+			_hasFootnote = hasFootnote;
+			_name = funcName;
+			_minParams = minParams;
+			_maxParams = maxParams;
+			_returnClass = convertSpecialChars(returnClass);
+			_paramClasses = convertSpecialChars(paramClasses);
+			_isVolatile = isVolatile;
+		}
+		private static String convertSpecialChars(String ss) {
+			StringBuilder sb = new StringBuilder(ss.length() + 4);
+			for(int i=0; i<ss.length(); i++) {
+				char c = ss.charAt(i);
+				if (isSimpleAscii(c)) {
+					sb.append(c);
+					continue;
+				}
+				switch (c) {
+					case CHAR_NDASH_8211:
+						sb.append('-');
+						continue;
+					case CHAR_ELLIPSIS_8230:
+						sb.append("...");
+						continue;
+				}
+				throw new RuntimeException("bad char (" + ((int)c) + ") in string '" + ss + "'");
+			}
+			return sb.toString();
+		}
+		public int getIndex() {
+			return _index;
+		}
+		public String getName() {
+			return _name;
+		}
+		public boolean hasFootnote() {
+			return _hasFootnote;
+		}
+		public String formatAsDataLine() {
+			return _index + "\t" + _name + "\t" + _minParams + "\t"
+					+ _maxParams + "\t" + _returnClass + "\t" + _paramClasses
+					+ "\t" + checkMark(_isVolatile) + "\t" + checkMark(_hasFootnote);
+		}
+		private static String checkMark(boolean b) {
+			return b ? "x" : "";
+		}
+	}
+
+	private static final class FunctionDataCollector {
+
+		private final Map<Integer, FunctionData> _allFunctionsByIndex;
+		private final Map<String, FunctionData> _allFunctionsByName;
+		private final Set<Integer> _groupFunctionIndexes;
+		private final Set<String> _groupFunctionNames;
+		private final PrintStream _ps;
+
+		public FunctionDataCollector(PrintStream ps) {
+			_ps = ps;
+			_allFunctionsByIndex = new HashMap<>();
+			_allFunctionsByName = new HashMap<>();
+			_groupFunctionIndexes = new HashSet<>();
+			_groupFunctionNames = new HashSet<>();
+		}
+
+		public void addFunction(int funcIx, boolean hasFootnote, String funcName, int minParams, int maxParams,
+								String returnClass, String paramClasses, String volatileFlagStr) {
+			boolean isVolatile = volatileFlagStr.length() > 0;
+
+			Integer funcIxKey = Integer.valueOf(funcIx);
+			if(!_groupFunctionIndexes.add(funcIxKey)) {
+				throw new RuntimeException("Duplicate function index (" + funcIx + ")");
+			}
+			if(!_groupFunctionNames.add(funcName)) {
+				throw new RuntimeException("Duplicate function name '" + funcName + "'");
+			}
+
+			checkRedefinedFunction(hasFootnote, funcName, funcIxKey);
+			FunctionData fd = new FunctionData(funcIx, hasFootnote, funcName,
+					minParams, maxParams, returnClass, paramClasses, isVolatile);
+
+			_allFunctionsByIndex.put(funcIxKey, fd);
+			_allFunctionsByName.put(funcName, fd);
+		}
+
+		/**
+		 * Some extra validation here.
+		 * Any function which changes definition will have a footnote in the source document
+		 */
+		private void checkRedefinedFunction(boolean hasNote, String funcName, Integer funcIxKey) {
+			FunctionData fdPrev;
+			// check by index
+			fdPrev = _allFunctionsByIndex.get(funcIxKey);
+			if(fdPrev != null) {
+				if(!fdPrev.hasFootnote() || !hasNote) {
+					throw new RuntimeException("changing function ["
+							+ funcIxKey + "] definition without foot-note");
+				}
+				_allFunctionsByName.remove(fdPrev.getName());
+			}
+			// check by name
+			fdPrev = _allFunctionsByName.get(funcName);
+			if(fdPrev != null) {
+				if(!fdPrev.hasFootnote() || !hasNote) {
+					throw new RuntimeException("changing function '"
+							+ funcName + "' definition without foot-note");
+				}
+				_allFunctionsByIndex.remove(Integer.valueOf(fdPrev.getIndex()));
+			}
+		}
+
+		public void endTableGroup(String headingText) {
+			Integer[] keys = new Integer[_groupFunctionIndexes.size()];
+			_groupFunctionIndexes.toArray(keys);
+			_groupFunctionIndexes.clear();
+			_groupFunctionNames.clear();
+			Arrays.sort(keys);
+
+			_ps.println("# " + headingText);
+			for (Integer key : keys) {
+				FunctionData fd = _allFunctionsByIndex.get(key);
+				_ps.println(fd.formatAsDataLine());
+			}
+		}
+	}
+
+	private static final Pattern ID_MATCH = Pattern.compile("0x([\\dA-F]+)");
+	private static final Pattern NAME_MATCH = Pattern.compile("([0-9A-Z.]+)");
+	private static final Pattern ID_NAME_MATCH = Pattern.compile("0x([\\dA-F]+)\\s+([0-9A-Z.]+)");
+
+	private static final Set<String> IGNORED_LINES = new HashSet<>();
+	static {
+		IGNORED_LINES.add("[MS-XLS] — v20141018");
+		IGNORED_LINES.add("Excel Binary File Format (.xls) Structure");
+		IGNORED_LINES.add("Copyright © 2014 Microsoft Corporation.");
+		IGNORED_LINES.add("Release: October 30, 2014Value Meaning");
+		IGNORED_LINES.add("Release: October 30, 2014Value");
+		IGNORED_LINES.add("Meaning");
+	}
+
+	private static void extractFunctionData(FunctionDataCollector fdc, InputStream is) throws IOException {
+		try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
+
+			String id = null;
+			String name = null;
+			while (true) {
+				String line = reader.readLine();
+				if(line == null) {
+					break;
+				}
+
+				if(IGNORED_LINES.contains(line) || line.matches("\\d+ / \\d+")) {
+					continue;
+				}
+
+				Matcher idMatcher = ID_MATCH.matcher(line);
+				boolean foundID = idMatcher.matches();
+				Matcher nameMatcher = NAME_MATCH.matcher(line);
+				boolean foundName = nameMatcher.matches();
+				Matcher idAndNameMatcher = ID_NAME_MATCH.matcher(line);
+				boolean foundIDandName = idAndNameMatcher.matches();
+				if(foundID && foundName ||
+						foundName && foundIDandName ||
+						foundID && foundIDandName) {
+					throw new IllegalStateException("Should not find id and name: " +
+							foundID + "/" + foundName + "/" + foundIDandName +
+							", line: " + line);
+				}
+
+				if(foundID && id != null) {
+					throw new IllegalStateException("Found ID, but already had one: " + id + ", line: " + line);
+				}
+				if(foundName && name != null) {
+					throw new IllegalStateException("Found name, but already had one: " + name + ", line: " + line);
+				}
+				if(foundIDandName && (name != null || id != null)) {
+					throw new IllegalStateException("Found name and id, but already had one: id: " + id + ", name: " + name + ", line: " + line);
+				}
+
+				if(foundID) {
+					id = idMatcher.group(1);
+				} else if (foundName) {
+					name = nameMatcher.group(1);
+				} else if (foundIDandName) {
+					id = idAndNameMatcher.group(1);
+					name = idAndNameMatcher.group(2);
+					// manual override for one function name which contains lowercase characters
+				} else if(line.equals("VBAActivate")) {
+					name = line;
+				} else if (id == null || name == null) {
+					throw new IllegalStateException("Found params, but had empty id or name, id: " + id +
+							", name: " + name + ", line: " + line);
+				} else {
+					System.out.println("Found function " + id + " " + name + " " + line);
+					fdc.addFunction(Integer.parseInt(id, 16), false, name, 0, 0,
+							"", "", "");
+
+					id = null;
+					name = null;
+				}
+			}
+		}
+
+		fdc.endTableGroup("");
+	}
+	/**
+	 * To be sure that no tricky unicode chars make it through to the output file.
+	 */
+	private static final class SimpleAsciiOutputStream extends OutputStream {
+
+		private final OutputStream _os;
+
+		public SimpleAsciiOutputStream(OutputStream os) {
+			_os = os;
+		}
+		
+		@Override
+        public void write(int b) throws IOException {
+			checkByte(b);
+			_os.write(b);
+		}
+
+		private static void checkByte(int b) {
+			if (!isSimpleAscii((char)b)) {
+				throw new RuntimeException("Encountered char (" + b + ") which was not simple ascii as expected");
+			}
+		}
+
+		@Override
+        public void write(byte[] b, int off, int len) throws IOException {
+			for (int i = 0; i < len; i++) {
+				checkByte(b[i + off]);
+
+			}
+			_os.write(b, off, len);
+		}
+	}
+
+	private static void processFile(InputStream input, File outFile) throws IOException {
+		try (OutputStream os = new SimpleAsciiOutputStream(new FileOutputStream(outFile));
+		PrintStream ps = new PrintStream(os, true, "UTF-8")) {
+
+			outputLicenseHeader(ps);
+			Class<?> genClass = ExcelCetabFunctionExtractor.class;
+			ps.println("# Created by (" + genClass.getName() + ")");
+			// identify the source file
+			ps.println("# from source file '" + SOURCE_DOC_FILE_NAME + "'");
+			ps.println("#");
+			ps.println("#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )");
+			ps.println();
+			extractFunctionData(new FunctionDataCollector(ps), input);
+			ps.close();
+
+			String canonicalOutputFileName = outFile.getCanonicalPath();
+			System.out.println("Successfully output to '" + canonicalOutputFileName + "'");
+		}
+	}
+
+	private static void outputLicenseHeader(PrintStream ps) {
+		String[] lines= {
+			"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.",
+		};
+		for (String line : lines) {
+			ps.print("# ");
+			ps.println(line);
+		}
+		ps.println();
+	}
+
+	public static void main(String[] args) throws IOException {
+		if(!new File(SOURCE_DOC_FILE_NAME).exists()) {
+			throw new IllegalStateException("Did not find file " + SOURCE_DOC_FILE_NAME + " in the resources");
+		}
+
+		try (InputStream stream = new FileInputStream(SOURCE_DOC_FILE_NAME)) {
+			File outFile = new File("functionMetadataCetab.txt");
+
+			processFile(stream, outFile);
+		}
+	}
+}

Added: poi/trunk/test-data/spreadsheet/60405.xls
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/60405.xls?rev=1852277&view=auto
==============================================================================
Binary files poi/trunk/test-data/spreadsheet/60405.xls (added) and poi/trunk/test-data/spreadsheet/60405.xls Sun Jan 27 09:57:39 2019 differ



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org