You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2021/05/21 21:22:41 UTC

svn commit: r1890089 [5/6] - in /poi/trunk: ./ poi-examples/ poi-examples/src/main/java/org/apache/poi/examples/hpsf/ poi-examples/src/main/java/org/apache/poi/examples/xssf/eventusermodel/ poi-examples/src/main/java/org/apache/poi/examples/xssf/stream...

Modified: poi/trunk/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java Fri May 21 21:22:40 2021
@@ -33,9 +33,8 @@ public interface HSSFShapeContainer exte
 
     /**
      * add shape to the list of child records
-     * @param shape
      */
-    public void addShape(HSSFShape shape);
+    void addShape(HSSFShape shape);
 
     /**
      * set coordinates of this group relative to the parent
@@ -47,27 +46,27 @@ public interface HSSFShapeContainer exte
     /**
      *@return The top left x coordinate of this group.
      */
-    public int getX1();
+    int getX1();
 
     /**
      *@return The top left y coordinate of this group.
      */
-    public int getY1();
+    int getY1();
 
     /**
      *@return The bottom right x coordinate of this group.
      */
-    public int getX2();
+    int getX2();
 
     /**
      * @return The bottom right y coordinate of this group.
      */
-    public int getY2();
+    int getY2();
 
     /**
      * remove first level shapes
      * @param shape to be removed
      * @return true if shape is removed else return false
      */
-    public boolean removeShape(HSSFShape shape);
+    boolean removeShape(HSSFShape shape);
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java Fri May 21 21:22:40 2021
@@ -26,10 +26,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 
-import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
 
 import com.zaxxer.sparsebits.SparseBitSet;
 import org.apache.logging.log4j.LogManager;
@@ -37,7 +34,6 @@ import org.apache.logging.log4j.Logger;
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
-import org.apache.poi.poifs.filesystem.POIFSWriterListener;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndian;
@@ -63,7 +59,7 @@ public abstract class ChunkedCipherOutpu
     private long pos;
     private long totalPos;
     private long written;
-    
+
     // the cipher can't be final, because for the last chunk we change the padding
     // and therefore need to change the cipher too
     private Cipher cipher;
@@ -133,7 +129,7 @@ public abstract class ChunkedCipherOutpu
     public void writePlain(byte[] b, int off, int len) throws IOException {
         write(b, off, len, true);
     }
-    
+
     protected void write(byte[] b, int off, int len, boolean writePlain) throws IOException {
         if (len == 0) {
             return;
@@ -214,10 +210,6 @@ public abstract class ChunkedCipherOutpu
     /**
      * Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher
      * and uses it's own implementation
-     *
-     * @throws BadPaddingException 
-     * @throws IllegalBlockSizeException 
-     * @throws ShortBufferException
      */
     protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException, IOException {
         byte[] plain = (plainByteFlags.isEmpty()) ? null : chunk.clone();
@@ -250,10 +242,10 @@ public abstract class ChunkedCipherOutpu
                 i = plainByteFlags.nextSetBit(i+1);
             }
         }
-        
+
         return ciLen;
     }
-    
+
     @Override
     public void close() throws IOException {
         if (isClosed) {
@@ -271,14 +263,14 @@ public abstract class ChunkedCipherOutpu
             if (fileOut != null) {
                 int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);
                 calculateChecksum(fileOut, (int)pos);
-                dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());
+                dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, this::processPOIFSWriterEvent);
                 createEncryptionInfoEntry(dir, fileOut);
             }
         } catch (GeneralSecurityException e) {
             throw new IOException(e);
         }
     }
-    
+
     protected byte[] getChunk() {
         return chunk;
     }
@@ -304,31 +296,28 @@ public abstract class ChunkedCipherOutpu
      */
     public void setNextRecordSize(int recordSize, boolean isPlain) {
     }
-    
-    private class EncryptedPackageWriter implements POIFSWriterListener {
-        @Override
-        public void processPOIFSWriterEvent(POIFSWriterEvent event) {
-            try {
-                try (OutputStream os = event.getStream();
-                     FileInputStream fis = new FileInputStream(fileOut)) {
-
-                    // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data
-                    // encrypted within the EncryptedData field, not including the size of the StreamSize field.
-                    // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
-                    // value, depending on the block size of the chosen encryption algorithm
-                    byte[] buf = new byte[LittleEndianConsts.LONG_SIZE];
-                    LittleEndian.putLong(buf, 0, pos);
-                    os.write(buf);
 
-                    IOUtils.copy(fis, os);
-                }
+    private void processPOIFSWriterEvent(POIFSWriterEvent event) {
+        try {
+            try (OutputStream os = event.getStream();
+                 FileInputStream fis = new FileInputStream(fileOut)) {
 
-                if (!fileOut.delete()) {
-                    LOG.atError().log("Can't delete temporary encryption file: {}", fileOut);
-                }
-            } catch (IOException e) {
-                throw new EncryptedDocumentException(e);
+                // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data
+                // encrypted within the EncryptedData field, not including the size of the StreamSize field.
+                // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
+                // value, depending on the block size of the chosen encryption algorithm
+                byte[] buf = new byte[LittleEndianConsts.LONG_SIZE];
+                LittleEndian.putLong(buf, 0, pos);
+                os.write(buf);
+
+                IOUtils.copy(fis, os);
+            }
+
+            if (!fileOut.delete()) {
+                LOG.atError().log("Can't delete temporary encryption file: {}", fileOut);
             }
+        } catch (IOException e) {
+            throw new EncryptedDocumentException(e);
         }
     }
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/EncryptionHeader.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/EncryptionHeader.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/EncryptionHeader.java Fri May 21 21:22:40 2021
@@ -30,24 +30,6 @@ import org.apache.poi.common.usermodel.G
  * The constants are largely based on ZIP constants.
  */
 public abstract class EncryptionHeader implements GenericRecord, Duplicatable {
-    public static final int ALGORITHM_RC4 = CipherAlgorithm.rc4.ecmaId;
-    public static final int ALGORITHM_AES_128 = CipherAlgorithm.aes128.ecmaId;
-    public static final int ALGORITHM_AES_192 = CipherAlgorithm.aes192.ecmaId;
-    public static final int ALGORITHM_AES_256 = CipherAlgorithm.aes256.ecmaId;
-
-    public static final int HASH_NONE   = HashAlgorithm.none.ecmaId;
-    public static final int HASH_SHA1   = HashAlgorithm.sha1.ecmaId;
-    public static final int HASH_SHA256 = HashAlgorithm.sha256.ecmaId;
-    public static final int HASH_SHA384 = HashAlgorithm.sha384.ecmaId;
-    public static final int HASH_SHA512 = HashAlgorithm.sha512.ecmaId;
-
-    public static final int PROVIDER_RC4 = CipherProvider.rc4.ecmaId;
-    public static final int PROVIDER_AES = CipherProvider.aes.ecmaId;
-
-    public static final int MODE_ECB = ChainingMode.ecb.ecmaId;
-    public static final int MODE_CBC = ChainingMode.cbc.ecmaId;
-    public static final int MODE_CFB = ChainingMode.cfb.ecmaId;
-
     private int flags;
     private int sizeExtra;
     private CipherAlgorithm cipherAlgorithm;
@@ -125,8 +107,6 @@ public abstract class EncryptionHeader i
      * Sets the keySize (in bits). Before calling this method, make sure
      * to set the cipherAlgorithm, as the amount of keyBits gets validated against
      * the list of allowed keyBits of the corresponding cipherAlgorithm
-     *
-     * @param keyBits
      */
     public void setKeySize(int keyBits) {
         this.keyBits = keyBits;
@@ -170,6 +150,7 @@ public abstract class EncryptionHeader i
         this.cspName = cspName;
     }
 
+    @Override
     public abstract EncryptionHeader copy();
 
     @Override

Modified: poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/package-info.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/package-info.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/package-info.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/poifs/crypt/package-info.java Fri May 21 21:22:40 2021
@@ -18,7 +18,7 @@
 /**
  * Implementation of the ECMA-376 and MS-propritary document encryptions<p>
  *
- * The implementation is split into the following packages:<p>
+ * The implementation is split into the following packages:
  *
  * <ul>
  * <li>This package contains common functions for both current implemented cipher modes.</li>

Modified: poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java Fri May 21 21:22:40 2021
@@ -32,9 +32,7 @@ import org.apache.poi.hpsf.ClassID;
  * managed by a Filesystem instance.
  */
 
-public interface DirectoryEntry
-    extends Entry, Iterable<Entry>
-{
+public interface DirectoryEntry extends Entry, Iterable<Entry> {
 
     /**
      * get an iterator of the Entry instances contained directly in
@@ -47,7 +45,7 @@ public interface DirectoryEntry
      *         implementations of Entry.
      */
 
-    public Iterator<Entry> getEntries();
+    Iterator<Entry> getEntries();
 
     /**
      * get the names of all the Entries contained directly in this
@@ -58,7 +56,7 @@ public interface DirectoryEntry
      *         getEntry(String), which may be empty (if this
      *         DirectoryEntry is empty)
      */
-    public Set<String> getEntryNames();
+    Set<String> getEntryNames();
 
     /**
      * is this DirectoryEntry empty?
@@ -66,7 +64,7 @@ public interface DirectoryEntry
      * @return true if this instance contains no Entry instances
      */
 
-    public boolean isEmpty();
+    boolean isEmpty();
 
     /**
      * find out how many Entry instances are contained directly within
@@ -76,13 +74,13 @@ public interface DirectoryEntry
      *         Entry instances
      */
 
-    public int getEntryCount();
+    int getEntryCount();
 
     /**
      * Checks if entry with specified name present
      */
 
-    public boolean hasEntry( final String name );
+    boolean hasEntry( final String name );
 
     /**
      * get a specified Entry by name
@@ -96,8 +94,7 @@ public interface DirectoryEntry
      *            name exists in this DirectoryEntry
      */
 
-    public Entry getEntry(final String name)
-        throws FileNotFoundException;
+    Entry getEntry(final String name) throws FileNotFoundException;
 
     /**
      * create a new DocumentEntry
@@ -107,12 +104,9 @@ public interface DirectoryEntry
      *               DocumentEntry
      *
      * @return the new DocumentEntry
-     *
-     * @exception IOException
      */
 
-    public DocumentEntry createDocument(final String name,
-                                        final InputStream stream)
+    DocumentEntry createDocument(final String name, final InputStream stream)
         throws IOException;
 
     /**
@@ -123,12 +117,9 @@ public interface DirectoryEntry
      * @param writer the writer of the new DocumentEntry
      *
      * @return the new DocumentEntry
-     *
-     * @exception IOException
      */
 
-    public DocumentEntry createDocument(final String name, final int size,
-                                        final POIFSWriterListener writer)
+    DocumentEntry createDocument(final String name, final int size, final POIFSWriterListener writer)
         throws IOException;
 
     /**
@@ -137,26 +128,21 @@ public interface DirectoryEntry
      * @param name the name of the new DirectoryEntry
      *
      * @return the new DirectoryEntry
-     *
-     * @exception IOException
      */
-
-    public DirectoryEntry createDirectory(final String name)
-        throws IOException;
+    DirectoryEntry createDirectory(final String name) throws IOException;
 
     /**
      * Gets the storage clsid of the directory entry
      *
      * @return storage Class ID
      */
-    public ClassID getStorageClsid();
+    ClassID getStorageClsid();
 
     /**
      * Sets the storage clsid for the directory entry
      *
      * @param clsidStorage storage Class ID
      */
-    public void setStorageClsid(ClassID clsidStorage);
-
-}   // end public interface DirectoryEntry
+    void setStorageClsid(ClassID clsidStorage);
+}
 

Modified: poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryNode.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryNode.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/DirectoryNode.java Fri May 21 21:22:40 2021
@@ -250,20 +250,22 @@ public class DirectoryNode
      *         implementations of Entry.
      */
 
+    @Override
     public Iterator<Entry> getEntries()
     {
         return _entries.iterator();
     }
-    
+
     /**
      * get the names of all the Entries contained directly in this
      * instance (in other words, names of children only; no grandchildren
      * etc).
      *
      * @return the names of all the entries that may be retrieved with
-     *         getEntry(String), which may be empty (if this 
+     *         getEntry(String), which may be empty (if this
      *         DirectoryEntry is empty)
      */
+    @Override
     public Set<String> getEntryNames()
     {
         return _byname.keySet();
@@ -275,6 +277,7 @@ public class DirectoryNode
      * @return true if this instance contains no Entry instances
      */
 
+    @Override
     public boolean isEmpty()
     {
         return _entries.isEmpty();
@@ -288,11 +291,13 @@ public class DirectoryNode
      *         Entry instances
      */
 
+    @Override
     public int getEntryCount()
     {
         return _entries.size();
     }
 
+    @Override
     public boolean hasEntry( String name )
     {
         return name != null && _byname.containsKey( name );
@@ -310,6 +315,7 @@ public class DirectoryNode
      *            name exists in this DirectoryEntry
      */
 
+    @Override
     public Entry getEntry(final String name) throws FileNotFoundException {
         Entry rval = null;
 
@@ -345,6 +351,7 @@ public class DirectoryNode
      * @exception IOException if the document can't be created
      */
 
+    @Override
     public DocumentEntry createDocument(final String name,
                                         final InputStream stream)
         throws IOException
@@ -364,6 +371,7 @@ public class DirectoryNode
      * @exception IOException if the document can't be created
      */
 
+    @Override
     public DocumentEntry createDocument(final String name, final int size,
                                         final POIFSWriterListener writer)
         throws IOException
@@ -381,6 +389,7 @@ public class DirectoryNode
      * @exception IOException if the directory can't be created
      */
 
+    @Override
     public DirectoryEntry createDirectory(final String name)
         throws IOException
     {
@@ -396,7 +405,7 @@ public class DirectoryNode
     }
 
     /**
-     * Set the contents of a document, creating if needed, 
+     * Set the contents of a document, creating if needed,
      *  otherwise updating. Returns the created / updated DocumentEntry
      *
      * @param name the name of the new or existing DocumentEntry
@@ -420,12 +429,13 @@ public class DirectoryNode
             return existing;
         }
     }
-    
+
     /**
      * Gets the storage clsid of the directory entry
      *
      * @return storage Class ID
      */
+    @Override
     public ClassID getStorageClsid()
     {
         return getProperty().getStorageClsid();
@@ -436,6 +446,7 @@ public class DirectoryNode
      *
      * @param clsidStorage storage Class ID
      */
+    @Override
     public void setStorageClsid(ClassID clsidStorage)
     {
         getProperty().setStorageClsid(clsidStorage);
@@ -485,6 +496,7 @@ public class DirectoryNode
      * @return an array of Object; may not be null, but may be empty
      */
 
+    @Override
     public Object [] getViewableArray()
     {
         return new Object[ 0 ];
@@ -497,6 +509,7 @@ public class DirectoryNode
      * @return an Iterator; may not be null, but may have an empty
      * back end store
      */
+    @Override
     public Iterator<Object> getViewableIterator() {
         List<Object> components = new ArrayList<>();
 
@@ -513,6 +526,7 @@ public class DirectoryNode
      *         a viewer should call getViewableIterator
      */
 
+    @Override
     public boolean preferArray()
     {
         return false;
@@ -525,6 +539,7 @@ public class DirectoryNode
      * @return short description
      */
 
+    @Override
     public String getShortDescription()
     {
         return getName();
@@ -533,6 +548,7 @@ public class DirectoryNode
     /**
      * Returns an Iterator over all the entries
      */
+    @Override
     public Iterator<Entry> iterator() {
         return getEntries();
     }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/FilteringDirectoryNode.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/FilteringDirectoryNode.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/FilteringDirectoryNode.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/poifs/filesystem/FilteringDirectoryNode.java Fri May 21 21:22:40 2021
@@ -45,13 +45,13 @@ public class FilteringDirectoryNode impl
    /**
     * The names of our entries to exclude
     */
-   private Set<String> excludes;
+   private final Set<String> excludes;
    /**
     * Excludes of our child directories
     */
-   private Map<String,List<String>> childExcludes;
+   private final Map<String,List<String>> childExcludes;
 
-   private DirectoryEntry directory;
+   private final DirectoryEntry directory;
 
    /**
     * Creates a filter round the specified directory, which
@@ -88,28 +88,34 @@ public class FilteringDirectoryNode impl
       }
    }
 
+   @Override
    public DirectoryEntry createDirectory(String name) throws IOException {
       return directory.createDirectory(name);
    }
 
+   @Override
    public DocumentEntry createDocument(String name, InputStream stream)
          throws IOException {
       return directory.createDocument(name, stream);
    }
 
+   @Override
    public DocumentEntry createDocument(String name, int size,
          POIFSWriterListener writer) throws IOException {
       return directory.createDocument(name, size, writer);
    }
 
+   @Override
    public Iterator<Entry> getEntries() {
       return new FilteringIterator();
    }
 
+   @Override
    public Iterator<Entry> iterator() {
       return getEntries();
    }
 
+   @Override
    public int getEntryCount() {
       int size = directory.getEntryCount();
       for (String excl : excludes) {
@@ -120,6 +126,7 @@ public class FilteringDirectoryNode impl
       return size;
    }
 
+   @Override
    public Set<String> getEntryNames() {
        Set<String> names = new HashSet<>();
        for (String name : directory.getEntryNames()) {
@@ -130,10 +137,12 @@ public class FilteringDirectoryNode impl
        return names;
    }
 
+   @Override
    public boolean isEmpty() {
       return (getEntryCount() == 0);
    }
 
+   @Override
    public boolean hasEntry(String name) {
       if (excludes.contains(name)) {
          return false;
@@ -141,6 +150,7 @@ public class FilteringDirectoryNode impl
       return directory.hasEntry(name);
    }
 
+   @Override
    public Entry getEntry(String name) throws FileNotFoundException {
       if (excludes.contains(name)) {
          throw new FileNotFoundException(name);
@@ -158,40 +168,48 @@ public class FilteringDirectoryNode impl
       return entry;
    }
 
+   @Override
    public ClassID getStorageClsid() {
       return directory.getStorageClsid();
    }
 
+   @Override
    public void setStorageClsid(ClassID clsidStorage) {
       directory.setStorageClsid(clsidStorage);
    }
 
+   @Override
    public boolean delete() {
       return directory.delete();
    }
 
+   @Override
    public boolean renameTo(String newName) {
       return directory.renameTo(newName);
    }
 
+   @Override
    public String getName() {
       return directory.getName();
    }
 
+   @Override
    public DirectoryEntry getParent() {
       return directory.getParent();
    }
 
+   @Override
    public boolean isDirectoryEntry() {
       return true;
    }
 
+   @Override
    public boolean isDocumentEntry() {
       return false;
    }
 
    private class FilteringIterator implements Iterator<Entry> {
-      private Iterator<Entry> parent;
+      private final Iterator<Entry> parent;
       private Entry next;
 
       private FilteringIterator() {
@@ -209,10 +227,12 @@ public class FilteringDirectoryNode impl
          }
       }
 
+      @Override
       public boolean hasNext() {
          return (next != null);
       }
 
+      @Override
       public Entry next() {
          if (!hasNext()) {
             throw new NoSuchElementException();
@@ -223,6 +243,7 @@ public class FilteringDirectoryNode impl
          return e;
       }
 
+      @Override
       public void remove() {
          throw new UnsupportedOperationException("Remove not supported");
       }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/sl/usermodel/Sheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/sl/usermodel/Sheet.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/sl/usermodel/Sheet.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/sl/usermodel/Sheet.java Fri May 21 21:22:40 2021
@@ -35,15 +35,13 @@ public interface Sheet<
      * check this setting in the sheet XML
      */
 	boolean getFollowMasterGraphics();
-	
+
 	MasterSheet<S,P> getMasterSheet();
 
 	Background<S,P> getBackground();
-	
+
 	/**
 	 * Convenience method to draw a sheet to a graphics context
-	 *
-	 * @param graphics
 	 */
 	void draw(Graphics2D graphics);
 

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/format/SimpleFraction.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/format/SimpleFraction.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/format/SimpleFraction.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/format/SimpleFraction.java Fri May 21 21:22:40 2021
@@ -59,14 +59,13 @@ public class SimpleFraction {
      * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
      * Continued Fraction</a> equations (11) and (22)-(26)</li>
      * </ul>
-     * </p>
      *
      *  Based on org.apache.commons.math.fraction.Fraction from Apache Commons-Math.
      *  YK: The only reason of having this class is to avoid dependency on the Commons-Math jar.
      *
      * @param value the double value to convert to a fraction.
      * @param epsilon maximum error allowed.  The resulting fraction is within
-     *        <code>epsilon</code> of <code>value</code>, in absolute terms.
+     *        {@code epsilon} of {@code value}, in absolute terms.
      * @param maxDenominator maximum denominator value allowed.
      * @param maxIterations maximum number of convergents
      * @throws RuntimeException if the continued fraction failed to
@@ -79,7 +78,7 @@ public class SimpleFraction {
         double r0 = value;
         long a0 = (long)Math.floor(r0);
         if (a0 > overflow) {
-            throw new IllegalArgumentException("Overflow trying to convert "+value+" to fraction ("+a0+"/"+1l+")");
+            throw new IllegalArgumentException("Overflow trying to convert "+value+" to fraction ("+a0+"/"+ 1L +")");
         }
 
         // check for (almost) integer arguments, which should not go
@@ -141,7 +140,7 @@ public class SimpleFraction {
 
     /**
      * Create a fraction given a numerator and denominator.
-     * @param numerator
+     * @param numerator the numerator
      * @param denominator maxDenominator The maximum allowed value for denominator
      */
     public SimpleFraction(int numerator, int denominator)

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java Fri May 21 21:22:40 2021
@@ -29,7 +29,7 @@ import org.apache.poi.ss.usermodel.Sheet
 import org.apache.poi.ss.usermodel.Workbook;
 
 /**
- * Common functionality across file formats for evaluating formula cells.<p>
+ * Common functionality across file formats for evaluating formula cells.
  */
 public abstract class BaseFormulaEvaluator implements FormulaEvaluator, WorkbookEvaluatorProvider {
     protected final WorkbookEvaluator _bookEvaluator;
@@ -91,8 +91,8 @@ public abstract class BaseFormulaEvaluat
      * evaluateInCell() when the call should not modify the contents of the
      * original cell.
      *
-     * @param cell may be <code>null</code> signifying that the cell is not present (or blank)
-     * @return <code>null</code> if the supplied cell is <code>null</code> or blank
+     * @param cell may be {@code null} signifying that the cell is not present (or blank)
+     * @return {@code null} if the supplied cell is {@code null} or blank
      */
     @Override
     public CellValue evaluate(Cell cell) {
@@ -281,13 +281,11 @@ public abstract class BaseFormulaEvaluat
         }
     }
 
-    /** {@inheritDoc} */
     @Override
     public void setIgnoreMissingWorkbooks(boolean ignore){
         _bookEvaluator.setIgnoreMissingWorkbooks(ignore);
     }
 
-    /** {@inheritDoc} */
     @Override
     public void setDebugEvaluationOutputForNextEval(boolean value){
         _bookEvaluator.setDebugEvaluationOutputForNextEval(value);

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/CacheAreaEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/CacheAreaEval.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/CacheAreaEval.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/CacheAreaEval.java Fri May 21 21:22:40 2021
@@ -31,47 +31,50 @@ import org.apache.poi.ss.util.CellRefere
  */
 
 public final class CacheAreaEval extends AreaEvalBase {
-    
+
     /* Value Containter */
     private final ValueEval[] _values;
-    
+
     public CacheAreaEval(AreaI ptg, ValueEval[] values) {
         super(ptg);
         _values = values;
     }
-    
+
     public CacheAreaEval(int firstRow, int firstColumn, int lastRow, int lastColumn, ValueEval[] values) {
         super(firstRow, firstColumn, lastRow, lastColumn);
         _values = values;
     }
-    
+
+    @Override
     public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
         return getRelativeValue(-1, relativeRowIndex, relativeColumnIndex);
     }
-    
+
+    @Override
     public ValueEval getRelativeValue(int sheetIndex, int relativeRowIndex, int relativeColumnIndex) {
         int oneDimensionalIndex = relativeRowIndex * getWidth() + relativeColumnIndex;
         return _values[oneDimensionalIndex];
     }
 
+    @Override
     public AreaEval offset(int relFirstRowIx, int relLastRowIx,
             int relFirstColIx, int relLastColIx) {
-        
+
         AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
                 relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
-        
+
         int height = area.getLastRow() - area.getFirstRow() + 1;
         int width = area.getLastColumn() - area.getFirstColumn() + 1;
 
         ValueEval[] newVals = new ValueEval[height * width];
-        
+
         int startRow = area.getFirstRow() - getFirstRow();
         int startCol = area.getFirstColumn() - getFirstColumn();
-        
+
         for (int j = 0; j < height; j++) {
             for (int i = 0; i < width; i++) {
                 ValueEval temp;
-                
+
                 /* CacheAreaEval is only temporary value representation, does not equal sheet selection
                  * so any attempts going beyond the selection results in BlankEval
                  */
@@ -81,13 +84,14 @@ public final class CacheAreaEval extends
                 else {
                     temp = _values[(startRow + j) * getWidth() + (startCol + i)];
                 }
-                newVals[j * width + i] = temp;     
+                newVals[j * width + i] = temp;
             }
         }
 
         return new CacheAreaEval(area, newVals);
     }
 
+    @Override
     public TwoDEval getRow(int rowIndex) {
         if (rowIndex >= getHeight()) {
             throw new IllegalArgumentException("Invalid rowIndex " + rowIndex
@@ -95,13 +99,14 @@ public final class CacheAreaEval extends
         }
         int absRowIndex = getFirstRow() + rowIndex;
         ValueEval[] values = new ValueEval[getWidth()];
-        
+
         for (int i = 0; i < values.length; i++) {
             values[i] = getRelativeValue(rowIndex, i);
         }
         return new CacheAreaEval(absRowIndex, getFirstColumn() , absRowIndex, getLastColumn(), values);
     }
 
+    @Override
     public TwoDEval getColumn(int columnIndex) {
         if (columnIndex >= getWidth()) {
             throw new IllegalArgumentException("Invalid columnIndex " + columnIndex
@@ -109,14 +114,14 @@ public final class CacheAreaEval extends
         }
         int absColIndex = getFirstColumn() + columnIndex;
         ValueEval[] values = new ValueEval[getHeight()];
-        
+
         for (int i = 0; i < values.length; i++) {
             values[i] = getRelativeValue(i, columnIndex);
         }
-        
+
         return new CacheAreaEval(getFirstRow(), absColIndex, getLastRow(), absColIndex, values);
     }
-    
+
     public String toString() {
         CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
         CellReference crB = new CellReference(getLastRow(), getLastColumn());

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ConditionalFormattingEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ConditionalFormattingEvaluator.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ConditionalFormattingEvaluator.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/ConditionalFormattingEvaluator.java Fri May 21 21:22:40 2021
@@ -38,17 +38,15 @@ import org.apache.poi.ss.util.CellRefere
 /**
  * Evaluates Conditional Formatting constraints.<p>
  *
- * For performance reasons, this class keeps a cache of all previously evaluated rules and cells.  
+ * For performance reasons, this class keeps a cache of all previously evaluated rules and cells.
  * Be sure to call {@link #clearAllCachedFormats()} if any conditional formats are modified, added, or deleted,
  * and {@link #clearAllCachedValues()} whenever cell values change.
- * <p>
- * 
  */
 public class ConditionalFormattingEvaluator {
 
     private final WorkbookEvaluator workbookEvaluator;
     private final Workbook workbook;
-    
+
     /**
      * All the underlying structures, for both HSSF and XSSF, repeatedly go to the raw bytes/XML for the
      * different pieces used in the ConditionalFormatting* structures.  That's highly inefficient,
@@ -56,12 +54,12 @@ public class ConditionalFormattingEvalua
      * <p>
      * Instead we need a cached version that is discarded when definitions change.
      * <p>
-     * Sheets don't implement equals, and since its an interface, 
+     * Sheets don't implement equals, and since its an interface,
      * there's no guarantee instances won't be recreated on the fly by some implementation.
      * So we use sheet name.
      */
     private final Map<String, List<EvaluationConditionalFormatRule>> formats = new HashMap<>();
-    
+
     /**
      * Evaluating rules for cells in their region(s) is expensive, so we want to cache them,
      * and empty/reevaluate the cache when values change.
@@ -76,21 +74,21 @@ public class ConditionalFormattingEvalua
         this.workbook = wb;
         this.workbookEvaluator = provider._getWorkbookEvaluator();
     }
-    
+
     protected WorkbookEvaluator getWorkbookEvaluator() {
         return workbookEvaluator;
     }
-    
+
     /**
-     * Call this whenever rules are added, reordered, or removed, or a rule formula is changed 
+     * Call this whenever rules are added, reordered, or removed, or a rule formula is changed
      * (not the formula inputs but the formula expression itself)
      */
     public void clearAllCachedFormats() {
         formats.clear();
     }
-    
+
     /**
-     * Call this whenever cell values change in the workbook, so condional formats are re-evaluated 
+     * Call this whenever cell values change in the workbook, so condional formats are re-evaluated
      * for all cells.
      * <p>
      * TODO: eventually this should work like {@link EvaluationCache#notifyUpdateCell(int, int, EvaluationCell)}
@@ -102,7 +100,7 @@ public class ConditionalFormattingEvalua
 
     /**
      * lazy load by sheet since reading can be expensive
-     * 
+     *
      * @param sheet The sheet to look at
      * @return unmodifiable list of rules
      */
@@ -131,43 +129,43 @@ public class ConditionalFormattingEvalua
         }
         return Collections.unmodifiableList(rules);
     }
-    
+
     /**
-     * This checks all applicable {@link ConditionalFormattingRule}s for the cell's sheet, 
+     * This checks all applicable {@link ConditionalFormattingRule}s for the cell's sheet,
      * in defined "priority" order, returning the matches if any.  This is a property currently
-     * not exposed from <code>CTCfRule</code> in <code>XSSFConditionalFormattingRule</code>.  
+     * not exposed from {@code CTCfRule} in {@code XSSFConditionalFormattingRule}.
      * <p>
      * Most cells will have zero or one applied rule, but it is possible to define multiple rules
      * that apply at the same time to the same cell, thus the List result.
      * <p>
-     * Note that to properly apply conditional rules, care must be taken to offset the base 
+     * Note that to properly apply conditional rules, care must be taken to offset the base
      * formula by the relative position of the current cell, or the wrong value is checked.
      * This is handled by {@link WorkbookEvaluator#evaluate(String, CellReference, CellRangeAddressBase)}.
      * <p>
      * If the cell exists and is a formula cell, its cached value may be used for rule evaluation, so
-     * make sure it is up to date.  If values have changed, it is best to call 
+     * make sure it is up to date.  If values have changed, it is best to call
      * {@link FormulaEvaluator#evaluateFormulaCell(Cell)} or {@link FormulaEvaluator#evaluateAll()} first,
-     * or the wrong conditional results may be returned. 
-     * 
+     * or the wrong conditional results may be returned.
+     *
      * @param cellRef NOTE: if no sheet name is specified, this uses the workbook active sheet
      * @return Unmodifiable List of {@link EvaluationConditionalFormatRule}s that apply to the current cell value,
-     *         in priority order, as evaluated by Excel (smallest priority # for XSSF, definition order for HSSF), 
+     *         in priority order, as evaluated by Excel (smallest priority # for XSSF, definition order for HSSF),
      *         or null if none apply
      */
     public List<EvaluationConditionalFormatRule> getConditionalFormattingForCell(final CellReference cellRef) {
         List<EvaluationConditionalFormatRule> rules = values.get(cellRef);
-        
+
         if (rules == null) {
             // compute and cache them
             rules = new ArrayList<>();
-            
+
             final Sheet sheet;
             if (cellRef.getSheetName() != null) {
                 sheet = workbook.getSheet(cellRef.getSheetName());
             } else {
                 sheet = workbook.getSheetAt(workbook.getActiveSheetIndex());
             }
-            
+
             /*
              * Per Excel help:
              * https://support.office.com/en-us/article/Manage-conditional-formatting-rule-precedence-e09711a3-48df-4bcb-b82c-9d8b8b22463d#__toc269129417
@@ -176,7 +174,7 @@ public class ConditionalFormattingEvalua
              */
             boolean stopIfTrue = false;
             for (EvaluationConditionalFormatRule rule : getRules(sheet)) {
-                
+
                 if (stopIfTrue) {
                     continue; // a previous rule matched and wants no more evaluations
                 }
@@ -189,40 +187,40 @@ public class ConditionalFormattingEvalua
             Collections.sort(rules);
             values.put(cellRef, rules);
         }
-        
+
         return Collections.unmodifiableList(rules);
     }
-    
+
     /**
-     * This checks all applicable {@link ConditionalFormattingRule}s for the cell's sheet, 
+     * This checks all applicable {@link ConditionalFormattingRule}s for the cell's sheet,
      * in defined "priority" order, returning the matches if any.  This is a property currently
-     * not exposed from <code>CTCfRule</code> in <code>XSSFConditionalFormattingRule</code>.  
+     * not exposed from {@code CTCfRule} in {@code XSSFConditionalFormattingRule}.
      * <p>
      * Most cells will have zero or one applied rule, but it is possible to define multiple rules
      * that apply at the same time to the same cell, thus the List result.
      * <p>
-     * Note that to properly apply conditional rules, care must be taken to offset the base 
+     * Note that to properly apply conditional rules, care must be taken to offset the base
      * formula by the relative position of the current cell, or the wrong value is checked.
      * This is handled by {@link WorkbookEvaluator#evaluate(String, CellReference, CellRangeAddressBase)}.
      * <p>
      * If the cell exists and is a formula cell, its cached value may be used for rule evaluation, so
-     * make sure it is up to date.  If values have changed, it is best to call 
+     * make sure it is up to date.  If values have changed, it is best to call
      * {@link FormulaEvaluator#evaluateFormulaCell(Cell)} or {@link FormulaEvaluator#evaluateAll()} first,
-     * or the wrong conditional results may be returned. 
-     * 
+     * or the wrong conditional results may be returned.
+     *
      * @param cell The cell to look for
      * @return Unmodifiable List of {@link EvaluationConditionalFormatRule}s that apply to the current cell value,
-     *         in priority order, as evaluated by Excel (smallest priority # for XSSF, definition order for HSSF), 
+     *         in priority order, as evaluated by Excel (smallest priority # for XSSF, definition order for HSSF),
      *         or null if none apply
      */
     public List<EvaluationConditionalFormatRule> getConditionalFormattingForCell(Cell cell) {
         return getConditionalFormattingForCell(getRef(cell));
     }
-    
+
     public static CellReference getRef(Cell cell) {
         return new CellReference(cell.getSheet().getSheetName(), cell.getRowIndex(), cell.getColumnIndex(), false, false);
     }
-    
+
     /**
      * Retrieve all formatting rules for the sheet with the given name.
      *
@@ -232,7 +230,7 @@ public class ConditionalFormattingEvalua
     public List<EvaluationConditionalFormatRule> getFormatRulesForSheet(String sheetName) {
         return getFormatRulesForSheet(workbook.getSheet(sheetName));
     }
-    
+
     /**
      * Retrieve all formatting rules for the given sheet.
      *
@@ -242,7 +240,7 @@ public class ConditionalFormattingEvalua
     public List<EvaluationConditionalFormatRule> getFormatRulesForSheet(Sheet sheet) {
         return getRules(sheet);
     }
-    
+
     /**
      * Conditional formatting rules can apply only to cells in the sheet to which they are attached.
      * The POI data model does not have a back-reference to the owning sheet, so it must be passed in separately.
@@ -262,7 +260,7 @@ public class ConditionalFormattingEvalua
         }
         return Collections.emptyList();
     }
-    
+
     /**
      * Retrieve all cells where the given formatting rule evaluates to true.
      *
@@ -272,7 +270,7 @@ public class ConditionalFormattingEvalua
     public List<Cell> getMatchingCells(EvaluationConditionalFormatRule rule) {
         final List<Cell> cells = new ArrayList<>();
         final Sheet sheet = rule.getSheet();
-        
+
         for (CellRangeAddress region : rule.getRegions()) {
             for (int r = region.getFirstRow(); r <= region.getLastRow(); r++) {
                 final Row row = sheet.getRow(r);
@@ -284,7 +282,7 @@ public class ConditionalFormattingEvalua
                     if (cell == null) {
                         continue;
                     }
-                    
+
                     List<EvaluationConditionalFormatRule> cellRules = getConditionalFormattingForCell(cell);
                     if (cellRules.contains(rule)) {
                         cells.add(cell);

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationConditionalFormatRule.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationConditionalFormatRule.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationConditionalFormatRule.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationConditionalFormatRule.java Fri May 21 21:22:40 2021
@@ -29,6 +29,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.Function;
 
 import org.apache.poi.ss.formula.eval.BlankEval;
 import org.apache.poi.ss.formula.eval.BoolEval;
@@ -102,12 +103,7 @@ public class EvaluationConditionalFormat
     private final DecimalFormat decimalTextFormat;
 
     /**
-     *
-     * @param workbookEvaluator
-     * @param sheet
-     * @param formatting
      * @param formattingIndex for priority, zero based
-     * @param rule
      * @param ruleIndex for priority, zero based, if this is an HSSF rule.  Unused for XSSF rules
      * @param regions could be read from formatting, but every call creates new objects in a new array.
      *                  this allows calling it once per formatting instance, and re-using the array.
@@ -241,7 +237,6 @@ public class EvaluationConditionalFormat
 
     /**
      * Defined as equal sheet name and formatting and rule indexes
-     * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
     public boolean equals(Object obj) {
@@ -263,7 +258,6 @@ public class EvaluationConditionalFormat
      * <p>
      * HSSF priority is based on definition/persistence order.
      *
-     * @param o
      * @return comparison based on sheet name, formatting index, and rule priority
      */
     @Override
@@ -294,7 +288,6 @@ public class EvaluationConditionalFormat
     }
 
     /**
-     * @param ref
      * @return true if this rule evaluates to true for the given cell
      */
     /* package */ boolean matches(CellReference ref) {
@@ -448,74 +441,15 @@ public class EvaluationConditionalFormat
                 return false;
             }
 
-            return getMeaningfulValues(region, false, new ValueFunction() {
-                @Override
-                public Set<ValueAndFormat> evaluate(List<ValueAndFormat> allValues) {
-                    final ConditionFilterData conf = rule.getFilterConfiguration();
-
-                    if (! conf.getBottom()) {
-                        allValues.sort(Collections.reverseOrder());
-                    } else {
-                        Collections.sort(allValues);
-                    }
-
-                    int limit = Math.toIntExact(conf.getRank());
-                    if (conf.getPercent()) {
-                        limit = allValues.size() * limit / 100;
-                    }
-                    if (allValues.size() <= limit) {
-                        return new HashSet<>(allValues);
-                    }
-
-                    return new HashSet<>(allValues.subList(0, limit));
-                }
-            }).contains(cv);
+            return getMeaningfulValues(region, false, this::evaluateTop10).contains(cv);
         case UNIQUE_VALUES:
             // Per Excel help, "duplicate" means matching value AND format
             // https://support.office.com/en-us/article/Filter-for-unique-values-or-remove-duplicate-values-ccf664b0-81d6-449b-bbe1-8daaec1e83c2
-            return getMeaningfulValues(region, true, new ValueFunction() {
-                @Override
-                public Set<ValueAndFormat> evaluate(List<ValueAndFormat> allValues) {
-                    Collections.sort(allValues);
-
-                    final Set<ValueAndFormat> unique = new HashSet<>();
-
-                    for (int i = 0; i < allValues.size(); i++) {
-                        final ValueAndFormat v = allValues.get(i);
-                        // skip this if the current value matches the next one, or is the last one and matches the previous one
-                        if ( (i < allValues.size()-1 && v.equals(allValues.get(i+1)) ) || ( i > 0 && i == allValues.size()-1 && v.equals(allValues.get(i-1)) ) ) {
-                            // current value matches next value, skip both
-                            i++;
-                            continue;
-                        }
-                        unique.add(v);
-                    }
-
-                    return unique;
-                }
-            }).contains(cv);
+            return getMeaningfulValues(region, true, this::evaluateUniqueValues).contains(cv);
         case DUPLICATE_VALUES:
             // Per Excel help, "duplicate" means matching value AND format
             // https://support.office.com/en-us/article/Filter-for-unique-values-or-remove-duplicate-values-ccf664b0-81d6-449b-bbe1-8daaec1e83c2
-            return getMeaningfulValues(region, true, new ValueFunction() {
-                @Override
-                public Set<ValueAndFormat> evaluate(List<ValueAndFormat> allValues) {
-                    Collections.sort(allValues);
-
-                    final Set<ValueAndFormat> dup = new HashSet<>();
-
-                    for (int i = 0; i < allValues.size(); i++) {
-                        final ValueAndFormat v = allValues.get(i);
-                        // skip this if the current value matches the next one, or is the last one and matches the previous one
-                        if ( (i < allValues.size()-1 && v.equals(allValues.get(i+1)) ) || ( i > 0 && i == allValues.size()-1 && v.equals(allValues.get(i-1)) ) ) {
-                            // current value matches next value, add one
-                            dup.add(v);
-                            i++;
-                        }
-                    }
-                    return dup;
-                }
-            }).contains(cv);
+            return getMeaningfulValues(region, true, this::evaluateDuplicateValues).contains(cv);
         case ABOVE_AVERAGE:
             // from testing, Excel only operates on numbers and dates (which are stored as numbers) in the range.
             // numbers stored as text are ignored, but numbers formatted as text are treated as numbers.
@@ -523,33 +457,15 @@ public class EvaluationConditionalFormat
             final ConditionFilterData conf = rule.getFilterConfiguration();
 
             // actually ordered, so iteration order is predictable
-            List<ValueAndFormat> values = new ArrayList<>(getMeaningfulValues(region, false, new ValueFunction() {
-                @Override
-                public Set<ValueAndFormat> evaluate(List<ValueAndFormat> allValues) {
-                    double total = 0;
-                    ValueEval[] pop = new ValueEval[allValues.size()];
-                    for (int i = 0; i < allValues.size(); i++) {
-                        ValueAndFormat v = allValues.get(i);
-                        total += v.value.doubleValue();
-                        pop[i] = new NumberEval(v.value.doubleValue());
-                    }
-
-                    final Set<ValueAndFormat> avgSet = new LinkedHashSet<>(1);
-                    avgSet.add(new ValueAndFormat(Double.valueOf(allValues.size() == 0 ? 0 : total / allValues.size()), null, decimalTextFormat));
-
-                    final double stdDev = allValues.size() <= 1 ? 0 : ((NumberEval) AggregateFunction.STDEV.evaluate(pop, 0, 0)).getNumberValue();
-                    avgSet.add(new ValueAndFormat(Double.valueOf(stdDev), null, decimalTextFormat));
-                    return avgSet;
-                }
-            }));
+            List<ValueAndFormat> values = new ArrayList<>(getMeaningfulValues(region, false, this::evaluateAboveAverage));
 
             Double val = cv.isNumber() ? cv.getValue() : null;
             if (val == null) {
                 return false;
             }
 
-            double avg = values.get(0).value.doubleValue();
-            double stdDev = values.get(1).value.doubleValue();
+            double avg = values.get(0).value;
+            double stdDev = values.get(1).value;
 
             /*
              * use StdDev, aboveAverage, equalAverage to find:
@@ -557,7 +473,7 @@ public class EvaluationConditionalFormat
              * operator type
              */
 
-            Double comp = Double.valueOf(conf.getStdDev() > 0 ? (avg + (conf.getAboveAverage() ? 1 : -1) * stdDev * conf.getStdDev()) : avg) ;
+            Double comp = conf.getStdDev() > 0 ? (avg + (conf.getAboveAverage() ? 1 : -1) * stdDev * conf.getStdDev()) : avg;
 
             final OperatorEnum op;
             if (conf.getAboveAverage()) {
@@ -576,10 +492,10 @@ public class EvaluationConditionalFormat
             return op.isValid(val, comp, null);
         case CONTAINS_TEXT:
             // implemented both by a cfRule "text" attribute and a formula.  Use the text.
-            return text == null ? false : cv.toString().toLowerCase(LocaleUtil.getUserLocale()).contains(lowerText);
+            return text != null && cv.toString().toLowerCase(LocaleUtil.getUserLocale()).contains(lowerText);
         case NOT_CONTAINS_TEXT:
             // implemented both by a cfRule "text" attribute and a formula.  Use the text.
-            return text == null ? true : ! cv.toString().toLowerCase(LocaleUtil.getUserLocale()).contains(lowerText);
+            return text == null || !cv.toString().toLowerCase(LocaleUtil.getUserLocale()).contains(lowerText);
         case BEGINS_WITH:
             // implemented both by a cfRule "text" attribute and a formula.  Use the text.
             return cv.toString().toLowerCase(LocaleUtil.getUserLocale()).startsWith(lowerText);
@@ -616,14 +532,88 @@ public class EvaluationConditionalFormat
         }
     }
 
+    private Set<ValueAndFormat> evaluateTop10(List<ValueAndFormat> allValues) {
+        final ConditionFilterData conf = rule.getFilterConfiguration();
+
+        if (! conf.getBottom()) {
+            allValues.sort(Collections.reverseOrder());
+        } else {
+            Collections.sort(allValues);
+        }
+
+        int limit = Math.toIntExact(conf.getRank());
+        if (conf.getPercent()) {
+            limit = allValues.size() * limit / 100;
+        }
+        if (allValues.size() <= limit) {
+            return new HashSet<>(allValues);
+        }
+
+        return new HashSet<>(allValues.subList(0, limit));
+    }
+
+    private Set<ValueAndFormat> evaluateUniqueValues(List<ValueAndFormat> allValues) {
+        Collections.sort(allValues);
+
+        final Set<ValueAndFormat> unique = new HashSet<>();
+
+        for (int i = 0; i < allValues.size(); i++) {
+            final ValueAndFormat v = allValues.get(i);
+            // skip this if the current value matches the next one, or is the last one and matches the previous one
+            if ( (i < allValues.size()-1 && v.equals(allValues.get(i+1)) ) || ( i > 0 && i == allValues.size()-1 && v.equals(allValues.get(i-1)) ) ) {
+                // current value matches next value, skip both
+                i++;
+                continue;
+            }
+            unique.add(v);
+        }
+
+        return unique;
+    }
+
+    public Set<ValueAndFormat> evaluateDuplicateValues(List<ValueAndFormat> allValues) {
+        Collections.sort(allValues);
+
+        final Set<ValueAndFormat> dup = new HashSet<>();
+
+        for (int i = 0; i < allValues.size(); i++) {
+            final ValueAndFormat v = allValues.get(i);
+            // skip this if the current value matches the next one, or is the last one and matches the previous one
+            if ( (i < allValues.size()-1 && v.equals(allValues.get(i+1)) ) || ( i > 0 && i == allValues.size()-1 && v.equals(allValues.get(i-1)) ) ) {
+                // current value matches next value, add one
+                dup.add(v);
+                i++;
+            }
+        }
+        return dup;
+    }
+
+    private Set<ValueAndFormat> evaluateAboveAverage(List<ValueAndFormat> allValues) {
+        double total = 0;
+        ValueEval[] pop = new ValueEval[allValues.size()];
+        for (int i = 0; i < allValues.size(); i++) {
+            ValueAndFormat v = allValues.get(i);
+            total += v.value;
+            pop[i] = new NumberEval(v.value);
+        }
+
+        final Set<ValueAndFormat> avgSet = new LinkedHashSet<>(1);
+        avgSet.add(new ValueAndFormat(allValues.size() == 0 ? 0 : total / allValues.size(), null, decimalTextFormat));
+
+        final double stdDev = allValues.size() <= 1 ? 0 : ((NumberEval) AggregateFunction.STDEV.evaluate(pop, 0, 0)).getNumberValue();
+        avgSet.add(new ValueAndFormat(stdDev, null, decimalTextFormat));
+        return avgSet;
+    }
+
     /**
      * from testing, Excel only operates on numbers and dates (which are stored as numbers) in the range.
      * numbers stored as text are ignored, but numbers formatted as text are treated as numbers.
      *
-     * @param region
+     * @param func instances evaluate the values for a region and return the positive matches for the function type.
+     *
      * @return the meaningful values in the range of cells specified
      */
-    private Set<ValueAndFormat> getMeaningfulValues(CellRangeAddress region, boolean withText, ValueFunction func) {
+    private Set<ValueAndFormat> getMeaningfulValues(CellRangeAddress region, boolean withText, Function<List<ValueAndFormat>,Set<ValueAndFormat>> func) {
         Set<ValueAndFormat> values = meaningfulRegionValues.get(region);
         if (values != null) {
             return values;
@@ -645,7 +635,7 @@ public class EvaluationConditionalFormat
             }
         }
 
-        values = func.evaluate(allValues);
+        values = func.apply(allValues);
         meaningfulRegionValues.put(region, values);
 
         return values;
@@ -660,7 +650,7 @@ public class EvaluationConditionalFormat
             }
             switch (type) {
                 case NUMERIC:
-                    return new ValueAndFormat(Double.valueOf(cell.getNumericCellValue()), format, decimalTextFormat);
+                    return new ValueAndFormat(cell.getNumericCellValue(), format, decimalTextFormat);
                 case STRING:
                 case BOOLEAN:
                     return new ValueAndFormat(cell.getStringCellValue(), format);
@@ -670,192 +660,6 @@ public class EvaluationConditionalFormat
         }
         return new ValueAndFormat("", "");
     }
-    /**
-     * instances evaluate the values for a region and return the positive matches for the function type.
-     * TODO: when we get to use Java 8, this is obviously a Lambda Function.
-     */
-    protected interface ValueFunction {
-
-        /**
-         *
-         * @param values
-         * @return the desired values for the rules implemented by the current instance
-         */
-        Set<ValueAndFormat> evaluate(List<ValueAndFormat> values);
-    }
-
-    /**
-     * Not calling it OperatorType to avoid confusion for now with other classes.
-     * Definition order matches OOXML type ID indexes.
-     * Note that this has NO_COMPARISON as the first item, unlike the similar
-     * DataValidation operator enum. Thanks, Microsoft.
-     */
-    public static enum OperatorEnum {
-        NO_COMPARISON {
-            /** always false/invalid */
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                return false;
-            }
-        },
-        BETWEEN {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    if (cellValue instanceof Number) {
-                        // use zero for null
-                        double n1 = 0;
-                        double n2 = v2 == null ? 0 : ((Number) v2).doubleValue();
-                        return Double.compare( ((Number) cellValue).doubleValue(), n1) >= 0 && Double.compare(((Number) cellValue).doubleValue(), n2) <= 0;
-                    } else if (cellValue instanceof String) {
-                        String n1 = "";
-                        String n2 = v2 == null ? "" : (String) v2;
-                        return ((String) cellValue).compareToIgnoreCase(n1) >= 0 && ((String) cellValue).compareToIgnoreCase(n2) <= 0;
-                    } else if (cellValue instanceof Boolean) return false;
-                    return false; // just in case - not a typical possibility
-                }
-                return cellValue.compareTo(v1) >= 0 && cellValue.compareTo(v2) <= 0;
-            }
-        },
-        NOT_BETWEEN {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    if (cellValue instanceof Number) {
-                        // use zero for null
-                        double n1 = 0;
-                        double n2 = v2 == null ? 0 : ((Number) v2).doubleValue();
-                        return Double.compare( ((Number) cellValue).doubleValue(), n1) < 0 || Double.compare(((Number) cellValue).doubleValue(), n2) > 0;
-                    } else if (cellValue instanceof String) {
-                        String n1 = "";
-                        String n2 = v2 == null ? "" : (String) v2;
-                        return ((String) cellValue).compareToIgnoreCase(n1) < 0 || ((String) cellValue).compareToIgnoreCase(n2) > 0;
-                    } else if (cellValue instanceof Boolean) return true;
-                    return false; // just in case - not a typical possibility
-                }
-                return cellValue.compareTo(v1) < 0 || cellValue.compareTo(v2) > 0;
-            }
-
-            public boolean isValidForIncompatibleTypes() {
-                return true;
-            }
-        },
-        EQUAL {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    if (cellValue instanceof Number) {
-                        // use zero for null
-                        return Double.compare( ((Number) cellValue).doubleValue(), 0) == 0;
-                    } else if (cellValue instanceof String) {
-                        return false; // even an empty string is not equal the empty cell, only another empty cell is, handled higher up
-                    } else if (cellValue instanceof Boolean) return false;
-                    return false; // just in case - not a typical possibility
-                }
-                // need to avoid instanceof, to work around a 1.6 compiler bug
-                if (cellValue.getClass() == String.class) {
-                    return cellValue.toString().compareToIgnoreCase(v1.toString()) == 0;
-                }
-                return cellValue.compareTo(v1) == 0;
-            }
-        },
-        NOT_EQUAL {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    return true; // non-null not equal null, returns true
-                }
-                // need to avoid instanceof, to work around a 1.6 compiler bug
-                if (cellValue.getClass() == String.class) {
-                    return cellValue.toString().compareToIgnoreCase(v1.toString()) == 0;
-                }
-                return cellValue.compareTo(v1) != 0;
-            }
-
-            public boolean isValidForIncompatibleTypes() {
-                return true;
-            }
-        },
-        GREATER_THAN {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    if (cellValue instanceof Number) {
-                        // use zero for null
-                        return Double.compare( ((Number) cellValue).doubleValue(), 0) > 0;
-                    } else if (cellValue instanceof String) {
-                        return true; // non-null string greater than empty cell
-                    } else if (cellValue instanceof Boolean) return true;
-                    return false; // just in case - not a typical possibility
-                }
-                return cellValue.compareTo(v1) > 0;
-            }
-        },
-        LESS_THAN {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    if (cellValue instanceof Number) {
-                        // use zero for null
-                        return Double.compare( ((Number) cellValue).doubleValue(), 0) < 0;
-                    } else if (cellValue instanceof String) {
-                        return false; // non-null string greater than empty cell
-                    } else if (cellValue instanceof Boolean) return false;
-                    return false; // just in case - not a typical possibility
-                }
-                return cellValue.compareTo(v1) < 0;
-            }
-        },
-        GREATER_OR_EQUAL {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    if (cellValue instanceof Number) {
-                        // use zero for null
-                        return Double.compare( ((Number) cellValue).doubleValue(), 0) >= 0;
-                    } else if (cellValue instanceof String) {
-                        return true; // non-null string greater than empty cell
-                    } else if (cellValue instanceof Boolean) return true;
-                    return false; // just in case - not a typical possibility
-                }
-                return cellValue.compareTo(v1) >= 0;
-            }
-        },
-        LESS_OR_EQUAL {
-            @Override
-            public <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
-                if (v1 == null) {
-                    if (cellValue instanceof Number) {
-                        // use zero for null
-                        return Double.compare( ((Number) cellValue).doubleValue(), 0) <= 0;
-                    } else if (cellValue instanceof String) {
-                        return false; // non-null string not less than empty cell
-                    } else if (cellValue instanceof Boolean) return false; // for completeness
-                    return false; // just in case - not a typical possibility
-                }
-                return cellValue.compareTo(v1) <= 0;
-            }
-        },
-        ;
-
-        /**
-         * Evaluates comparison using operator instance rules
-         * @param cellValue won't be null, assumption is previous checks handled that
-         * @param v1 if null, per Excel behavior various results depending on the type of cellValue and the specific enum instance
-         * @param v2 null if not needed.  If null when needed, various results, per Excel behavior
-         * @return true if the comparison is valid
-         */
-        public abstract <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2);
-
-        /**
-         * Called when the cell and comparison values are of different data types
-         * Needed for negation operators, which should return true.
-         * @return true if this comparison is true when the types to compare are different
-         */
-        public boolean isValidForIncompatibleTypes() {
-            return false;
-        }
-    }
 
     /**
      * Note: this class has a natural ordering that is inconsistent with equals.
@@ -914,7 +718,6 @@ public class EvaluationConditionalFormat
 
         /**
          * Note: this class has a natural ordering that is inconsistent with equals.
-         * @param o
          * @return value comparison
          */
         @Override

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationSheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationSheet.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationSheet.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/EvaluationSheet.java Fri May 21 21:22:40 2021
@@ -26,7 +26,7 @@ import org.apache.poi.util.Internal;
 public interface EvaluationSheet {
 
     /**
-     * @return <code>null</code> if there is no cell at the specified coordinates
+     * @return {@code null} if there is no cell at the specified coordinates
      */
     EvaluationCell getCell(int rowIndex, int columnIndex);
 
@@ -37,19 +37,18 @@ public interface EvaluationSheet {
      * @see EvaluationWorkbook#clearAllCachedResultValues()
      * @since POI 3.15 beta 3
      */
-    public void clearAllCachedResultValues();
+    void clearAllCachedResultValues();
 
     /**
      * @return last row index referenced on this sheet, for evaluation optimization
      * @since POI 4.0.0
      */
-    public int getLastRowNum();
+    int getLastRowNum();
 
     /**
      * Used by SUBTOTAL and similar functions that have options to ignore hidden rows
-     * @param rowIndex
      * @return true if the row is hidden, false if not
      * @since POI 4.1.0
      */
-    public boolean isRowHidden(int rowIndex);
+    boolean isRowHidden(int rowIndex);
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/FormulaType.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/FormulaType.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/FormulaType.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/FormulaType.java Fri May 21 21:22:40 2021
@@ -21,59 +21,58 @@ import org.apache.poi.util.Internal;
 
 /**
  * Enumeration of various formula types.
- * 
+ *
  * See Sections 3 and 4.8 of https://www.openoffice.org/sc/excelfileformat.pdf
  */
 @Internal
 public enum FormulaType {
     /** Regular cell formula */
     CELL(true),
-    
+
     /**
      * A Shared Formula ("{=SUM(A1:E1*{1,2,3,4,5}}")
-     * 
+     *
      * Similar to an array formula, but stored in a SHAREDFMLA instead of ARRAY record as a file size optimization.
      * See Section 4.8 of https://www.openoffice.org/sc/excelfileformat.pdf
      */
     SHARED(true),
-    
+
     /**
      * An Array formula ("{=SUM(A1:E1*{1,2,3,4,5}}")
      * https://support.office.com/en-us/article/Guidelines-and-examples-of-array-formulas-7D94A64E-3FF3-4686-9372-ECFD5CAA57C7
      */
     ARRAY(false),
-    
+
     /** Conditional formatting */
     CONDFORMAT(true),
-    
+
     /** Named range */
     NAMEDRANGE(false),
-    
+
     /**
      * This constant is currently very specific.  The exact differences from general data
      * validation formulas or conditional format formulas is not known yet
      */
     DATAVALIDATION_LIST(false);
-    
+
     /** formula is expected to return a single value vs. multiple values */
     private final boolean isSingleValue ;
     /**
      * @since POI 3.15 beta 3.
      */
-    private FormulaType(boolean singleValue) {
+    FormulaType(boolean singleValue) {
         this.isSingleValue = singleValue;
     }
-    
+
     /**
      * @return true if this formula type only returns single values, false if it can return multiple values (arrays, ranges, etc.)
      */
     public boolean isSingleValue() {
         return isSingleValue;
     }
-    
+
     /**
-     * Used to transition from <code>int</code>s (possibly stored in the Excel file) to <code>FormulaType</code>s.
-     * @param code 
+     * Used to transition from {@code int}s (possibly stored in the Excel file) to {@code FormulaType}s.
      * @return FormulaType
      * @throws IllegalArgumentException if code is out of range
      * @since POI 3.15 beta 3.

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/LazyAreaEval.java Fri May 21 21:22:40 2021
@@ -41,23 +41,27 @@ final class LazyAreaEval extends AreaEva
 		_evaluator = evaluator;
 	}
 
-    public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
+    @Override
+	public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
         return getRelativeValue(getFirstSheetIndex(), relativeRowIndex, relativeColumnIndex);
     }
-    public ValueEval getRelativeValue(int sheetIndex, int relativeRowIndex, int relativeColumnIndex) {
+    @Override
+	public ValueEval getRelativeValue(int sheetIndex, int relativeRowIndex, int relativeColumnIndex) {
 		int rowIx = (relativeRowIndex + getFirstRow() ) ;
 		int colIx = (relativeColumnIndex + getFirstColumn() ) ;
 
 		return _evaluator.getEvalForCell(sheetIndex, rowIx, colIx);
 	}
 
+	@Override
 	public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
 		AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
 				relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
 
 		return new LazyAreaEval(area, _evaluator);
 	}
-	public LazyAreaEval getRow(int rowIndex) {
+	@Override
+    public LazyAreaEval getRow(int rowIndex) {
 		if (rowIndex >= getHeight()) {
 			throw new IllegalArgumentException("Invalid rowIndex " + rowIndex
 					+ ".  Allowable range is (0.." + getHeight() + ").");
@@ -65,6 +69,7 @@ final class LazyAreaEval extends AreaEva
 		int absRowIx = getFirstRow() + rowIndex;
 		return new LazyAreaEval(absRowIx, getFirstColumn(), absRowIx, getLastColumn(), _evaluator);
 	}
+	@Override
 	public LazyAreaEval getColumn(int columnIndex) {
 		if (columnIndex >= getWidth()) {
 			throw new IllegalArgumentException("Invalid columnIndex " + columnIndex
@@ -89,17 +94,18 @@ final class LazyAreaEval extends AreaEva
     /**
      * @return  whether cell at rowIndex and columnIndex is a subtotal
     */
-    public boolean isSubTotal(int rowIndex, int columnIndex){
+    @Override
+	public boolean isSubTotal(int rowIndex, int columnIndex){
         // delegate the query to the sheet evaluator which has access to internal ptgs
         SheetRefEvaluator _sre = _evaluator.getSheetEvaluator(_evaluator.getFirstSheetIndex());
         return _sre.isSubTotal(getFirstRow() + rowIndex, getFirstColumn() + columnIndex);
     }
-    
+
     /**
      * @return whether the row at rowIndex is hidden
-     * @see org.apache.poi.ss.formula.eval.AreaEvalBase#isRowHidden(int)
      */
-    public boolean isRowHidden(int rowIndex) {
+    @Override
+	public boolean isRowHidden(int rowIndex) {
         // delegate the query to the sheet evaluator which has access to internal ptgs
         SheetRefEvaluator _sre = _evaluator.getSheetEvaluator(_evaluator.getFirstSheetIndex());
         return _sre.isRowHidden(getFirstRow() + rowIndex);

Added: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperatorEnum.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperatorEnum.java?rev=1890089&view=auto
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperatorEnum.java (added)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperatorEnum.java Fri May 21 21:22:40 2021
@@ -0,0 +1,198 @@
+/* ====================================================================
+   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;
+
+import org.apache.poi.util.Internal;
+
+/**
+ * Not calling it OperatorType to avoid confusion for now with other classes.
+ * Definition order matches OOXML type ID indexes.
+ * Note that this has NO_COMPARISON as the first item, unlike the similar
+ * DataValidation operator enum. Thanks, Microsoft.
+ */
+@Internal
+enum OperatorEnum {
+    // always false/invalid
+    NO_COMPARISON(OperatorEnum::noComp, false),
+    BETWEEN(OperatorEnum::between, false),
+    NOT_BETWEEN(OperatorEnum::notBetween, true),
+    EQUAL(OperatorEnum::equal, false),
+    NOT_EQUAL(OperatorEnum::notEqual, true),
+    GREATER_THAN(OperatorEnum::greaterThan, false),
+    LESS_THAN(OperatorEnum::lessThan, false),
+    GREATER_OR_EQUAL(OperatorEnum::greaterOrEqual, false),
+    LESS_OR_EQUAL(OperatorEnum::lessOrEqual, false)        ;
+
+    private interface CompareOp {
+        <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2);
+    }
+
+    private final CompareOp compareOp;
+    private final boolean validForIncompatibleTypes;
+
+    OperatorEnum(CompareOp compareOp, boolean validForIncompatibleTypes) {
+        this.compareOp = compareOp;
+        this.validForIncompatibleTypes = validForIncompatibleTypes;
+    }
+
+    /**
+     * Evaluates comparison using operator instance rules
+     * @param cellValue won't be null, assumption is previous checks handled that
+     * @param v1 if null, per Excel behavior various results depending on the type of cellValue and the specific enum instance
+     * @param v2 null if not needed.  If null when needed, various results, per Excel behavior
+     * @return true if the comparison is valid
+     */
+    <C extends Comparable<C>> boolean isValid(C cellValue, C v1, C v2) {
+        return compareOp.isValid(cellValue, v1, v2);
+    }
+
+    /**
+     * Called when the cell and comparison values are of different data types
+     * Needed for negation operators, which should return true.
+     * @return true if this comparison is true when the types to compare are different
+     */
+    boolean isValidForIncompatibleTypes() {
+        return validForIncompatibleTypes;
+    }
+
+    private static <C extends Comparable<C>> boolean noComp(C cellValue, C v1, C v2) {
+        return false;
+    }
+
+    private static <C extends Comparable<C>> boolean between(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            if (cellValue instanceof Number) {
+                // use zero for null
+                double n1 = 0;
+                double n2 = v2 == null ? 0 : ((Number) v2).doubleValue();
+                return Double.compare( ((Number) cellValue).doubleValue(), n1) >= 0 && Double.compare(((Number) cellValue).doubleValue(), n2) <= 0;
+            } else if (cellValue instanceof String) {
+                String n1 = "";
+                String n2 = v2 == null ? "" : (String) v2;
+                return ((String) cellValue).compareToIgnoreCase(n1) >= 0 && ((String) cellValue).compareToIgnoreCase(n2) <= 0;
+            } else if (cellValue instanceof Boolean) return false;
+            return false; // just in case - not a typical possibility
+        }
+        return cellValue.compareTo(v1) >= 0 && cellValue.compareTo(v2) <= 0;
+    }
+
+    private static <C extends Comparable<C>> boolean notBetween(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            if (cellValue instanceof Number) {
+                // use zero for null
+                double n1 = 0;
+                double n2 = v2 == null ? 0 : ((Number) v2).doubleValue();
+                return Double.compare( ((Number) cellValue).doubleValue(), n1) < 0 || Double.compare(((Number) cellValue).doubleValue(), n2) > 0;
+            } else if (cellValue instanceof String) {
+                String n1 = "";
+                String n2 = v2 == null ? "" : (String) v2;
+                return ((String) cellValue).compareToIgnoreCase(n1) < 0 || ((String) cellValue).compareToIgnoreCase(n2) > 0;
+            } else {
+                // just in case - not a typical possibility
+                return cellValue instanceof Boolean;
+            }
+        }
+        return cellValue.compareTo(v1) < 0 || cellValue.compareTo(v2) > 0;
+    }
+
+    private static <C extends Comparable<C>> boolean equal(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            if (cellValue instanceof Number) {
+                // use zero for null
+                return Double.compare( ((Number) cellValue).doubleValue(), 0) == 0;
+            } else if (cellValue instanceof String) {
+                return false; // even an empty string is not equal the empty cell, only another empty cell is, handled higher up
+            } else if (cellValue instanceof Boolean) return false;
+            return false; // just in case - not a typical possibility
+        }
+        // need to avoid instanceof, to work around a 1.6 compiler bug
+        if (cellValue.getClass() == String.class) {
+            return cellValue.toString().compareToIgnoreCase(v1.toString()) == 0;
+        }
+        return cellValue.compareTo(v1) == 0;
+    }
+
+    private static <C extends Comparable<C>> boolean notEqual(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            return true; // non-null not equal null, returns true
+        }
+        // need to avoid instanceof, to work around a 1.6 compiler bug
+        if (cellValue.getClass() == String.class) {
+            return cellValue.toString().compareToIgnoreCase(v1.toString()) == 0;
+        }
+        return cellValue.compareTo(v1) != 0;
+    }
+
+    private static <C extends Comparable<C>> boolean greaterThan(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            if (cellValue instanceof Number) {
+                // use zero for null
+                return Double.compare( ((Number) cellValue).doubleValue(), 0) > 0;
+            } else if (cellValue instanceof String) {
+                return true; // non-null string greater than empty cell
+            } else {
+                // just in case - not a typical possibility
+                return cellValue instanceof Boolean;
+            }
+        }
+        return cellValue.compareTo(v1) > 0;
+    }
+
+    private static <C extends Comparable<C>> boolean lessThan(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            if (cellValue instanceof Number) {
+                // use zero for null
+                return Double.compare( ((Number) cellValue).doubleValue(), 0) < 0;
+            } else if (cellValue instanceof String) {
+                return false; // non-null string greater than empty cell
+            } else if (cellValue instanceof Boolean) return false;
+            return false; // just in case - not a typical possibility
+        }
+        return cellValue.compareTo(v1) < 0;
+    }
+
+    private static <C extends Comparable<C>> boolean greaterOrEqual(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            if (cellValue instanceof Number) {
+                // use zero for null
+                return Double.compare( ((Number) cellValue).doubleValue(), 0) >= 0;
+            } else if (cellValue instanceof String) {
+                return true; // non-null string greater than empty cell
+            } else {
+                // just in case - not a typical possibility
+                return cellValue instanceof Boolean;
+            }
+        }
+        return cellValue.compareTo(v1) >= 0;
+    }
+
+    private static <C extends Comparable<C>> boolean lessOrEqual(C cellValue, C v1, C v2) {
+        if (v1 == null) {
+            if (cellValue instanceof Number) {
+                // use zero for null
+                return Double.compare( ((Number) cellValue).doubleValue(), 0) <= 0;
+            } else if (cellValue instanceof String) {
+                return false; // non-null string not less than empty cell
+            } else if (cellValue instanceof Boolean) return false; // for completeness
+            return false; // just in case - not a typical possibility
+        }
+        return cellValue.compareTo(v1) <= 0;
+    }
+
+}

Propchange: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/OperatorEnum.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SharedFormula.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SharedFormula.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SharedFormula.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/SharedFormula.java Fri May 21 21:22:40 2021
@@ -38,8 +38,6 @@ public class SharedFormula {
      * were it not shared.
      *
      * @param ptgs parsed tokens of the shared formula
-     * @param formulaRow
-     * @param formulaColumn
      */
     public Ptg[] convertSharedFormulas(Ptg[] ptgs, int formulaRow, int formulaColumn) {
 
@@ -72,9 +70,8 @@ public class SharedFormula {
             } else if (ptg instanceof OperandPtg) {
                 // Any subclass of OperandPtg is mutable, so it's safest to not share these instances.
                 ptg = ((OperandPtg) ptg).copy();
-            } else {
-            	// all other Ptgs are immutable and can be shared
             }
+            // all other Ptgs are immutable and can be shared
             newPtgStack[k] = ptg;
         }
         return newPtgStack;

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java?rev=1890089&r1=1890088&r2=1890089&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/formula/TwoDEval.java Fri May 21 21:22:40 2021
@@ -38,25 +38,27 @@ public interface TwoDEval extends ValueE
 	int getHeight();
 
 	/**
-	 * @return <code>true</code> if the area has just a single row, this also includes
+	 * @return {@code true} if the area has just a single row, this also includes
 	 * the trivial case when the area has just a single cell.
 	 */
-	boolean isRow();
+	default boolean isRow() {
+		return false;
+	}
 
 	/**
-	 * @return <code>true</code> if the area has just a single column, this also includes
+	 * @return {@code true} if the area has just a single column, this also includes
 	 * the trivial case when the area has just a single cell.
 	 */
 	boolean isColumn();
 
 	/**
 	 * @param rowIndex relative row index (zero based)
-	 * @return a single row {@link TwoDEval}
+	 * @return a single row TwoDEval
 	 */
 	TwoDEval getRow(int rowIndex);
 	/**
 	 * @param columnIndex relative column index (zero based)
-	 * @return a single column {@link TwoDEval}
+	 * @return a single column TwoDEval
 	 */
 	TwoDEval getColumn(int columnIndex);
 
@@ -65,10 +67,8 @@ public interface TwoDEval extends ValueE
      * @return true if the  cell at row and col is a subtotal
      */
     boolean isSubTotal(int rowIndex, int columnIndex);
-    
+
     /**
-     *
-     * @param rowIndex
      * @return true if the row is hidden
      * @see Subtotal
      */



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