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 2019/12/22 21:44:48 UTC

svn commit: r1871911 [11/15] - in /poi/trunk/src: integrationtest/org/apache/poi/ integrationtest/org/apache/poi/hssf/usermodel/ java/org/apache/poi/common/ java/org/apache/poi/ddf/ java/org/apache/poi/hssf/eventusermodel/dummyrecord/ java/org/apache/p...

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/common/UnicodeString.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/common/UnicodeString.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/common/UnicodeString.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/common/UnicodeString.java Sun Dec 22 21:44:45 2019
@@ -18,368 +18,91 @@
 package org.apache.poi.hssf.record.common;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.stream.Collectors;
 
+import org.apache.poi.common.Duplicatable;
 import org.apache.poi.hssf.record.RecordInputStream;
 import org.apache.poi.hssf.record.cont.ContinuableRecordInput;
 import org.apache.poi.hssf.record.cont.ContinuableRecordOutput;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndianInput;
-import org.apache.poi.util.LittleEndianOutput;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
-import org.apache.poi.util.StringUtil;
+import org.apache.poi.util.Removal;
 
 /**
- * Title: Unicode String<p>
- * Description:  Unicode String - just standard fields that are in several records.
- *               It is considered more desirable then repeating it in all of them.<p>
- *               This is often called a XLUnicodeRichExtendedString in MS documentation.<p>
- * REFERENCE:  PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<p>
- * REFERENCE:  PG 951 Excel Binary File Format (.xls) Structure Specification v20091214 
+ * Unicode String - just standard fields that are in several records.
+ * It is considered more desirable then repeating it in all of them.<p>
+ * This is often called a XLUnicodeRichExtendedString in MS documentation.<p>
  */
-public class UnicodeString implements Comparable<UnicodeString> {
+public class UnicodeString implements Comparable<UnicodeString>, Duplicatable {
     private static final POILogger _logger = POILogFactory.getLogger(UnicodeString.class);
 
-    //arbitrarily selected; may need to increase
-    private static final int MAX_RECORD_LENGTH = 100_000;
-
+    private static final BitField highByte  = BitFieldFactory.getInstance(0x1);
+    // 0x2 is reserved
+    private static final BitField extBit    = BitFieldFactory.getInstance(0x4);
+    private static final BitField richText  = BitFieldFactory.getInstance(0x8);
 
     private short             field_1_charCount;
     private byte              field_2_optionflags;
     private String            field_3_string;
     private List<FormatRun>   field_4_format_runs;
     private ExtRst            field_5_ext_rst;
-    private static final BitField   highByte  = BitFieldFactory.getInstance(0x1);
-    // 0x2 is reserved
-    private static final BitField   extBit    = BitFieldFactory.getInstance(0x4);
-    private static final BitField   richText  = BitFieldFactory.getInstance(0x8);
-
-    public static class FormatRun implements Comparable<FormatRun> {
-        final short _character;
-        short _fontIndex;
-
-        public FormatRun(short character, short fontIndex) {
-            this._character = character;
-            this._fontIndex = fontIndex;
-        }
-
-        public FormatRun(LittleEndianInput in) {
-            this(in.readShort(), in.readShort());
-        }
 
-        public short getCharacterPos() {
-            return _character;
-        }
+    private UnicodeString(UnicodeString other) {
+        field_1_charCount = other.field_1_charCount;
+        field_2_optionflags = other.field_2_optionflags;
+        field_3_string = other.field_3_string;
+        field_4_format_runs =  (other.field_4_format_runs == null) ? null :
+            other.field_4_format_runs.stream().map(FormatRun::new).collect(Collectors.toList());
+        field_5_ext_rst = (other.field_5_ext_rst == null) ? null : other.field_5_ext_rst.copy();
+    }
 
-        public short getFontIndex() {
-            return _fontIndex;
-        }
+    public UnicodeString(String str) {
+      setString(str);
+    }
 
-        public boolean equals(Object o) {
-            if (!(o instanceof FormatRun)) {
-                return false;
-            }
-            FormatRun other = ( FormatRun ) o;
+    /**
+     * construct a unicode string record and fill its fields, ID is ignored
+     * @param in the RecordInputstream to read the record from
+     */
+    public UnicodeString(RecordInputStream in) {
+        field_1_charCount   = in.readShort();
+        field_2_optionflags = in.readByte();
 
-            return _character == other._character && _fontIndex == other._fontIndex;
+        int runCount = 0;
+        int extensionLength = 0;
+        //Read the number of rich runs if rich text.
+        if (isRichText()) {
+            runCount = in.readShort();
         }
-
-        public int compareTo(FormatRun r) {
-            if (_character == r._character && _fontIndex == r._fontIndex) {
-                return 0;
-            }
-            if (_character == r._character) {
-                return _fontIndex - r._fontIndex;
-            }
-            return _character - r._character;
+        //Read the size of extended data if present.
+        if (isExtendedText()) {
+            extensionLength = in.readInt();
         }
 
-        @Override
-        public int hashCode() {
-            assert false : "hashCode not designed";
-            return 42; // any arbitrary constant will do
-        }
-
-        public String toString() {
-            return "character="+_character+",fontIndex="+_fontIndex;
-        }
-
-        public void serialize(LittleEndianOutput out) {
-            out.writeShort(_character);
-            out.writeShort(_fontIndex);
-        }
-    }
-    
-    // See page 681
-    public static class ExtRst implements Comparable<ExtRst> {
-       private short reserved;
-       
-       // This is a Phs (see page 881)
-       private short formattingFontIndex;
-       private short formattingOptions;
-       
-       // This is a RPHSSub (see page 894)
-       private int numberOfRuns;
-       private String phoneticText;
-       
-       // This is an array of PhRuns (see page 881)
-       private PhRun[] phRuns;
-       // Sometimes there's some cruft at the end
-       private byte[] extraData;
-
-       private void populateEmpty() {
-          reserved = 1;
-          phoneticText = "";
-          phRuns = new PhRun[0];
-          extraData = new byte[0];
-       }
-       
-       protected ExtRst() {
-          populateEmpty();
-       }
-       protected ExtRst(LittleEndianInput in, int expectedLength) {
-          reserved = in.readShort();
-          
-          // Old style detection (Reserved = 0xFF)
-          if(reserved == -1) {
-             populateEmpty();
-             return;
-          }
-          
-          // Spot corrupt records
-          if(reserved != 1) {
-             _logger.log(POILogger.WARN, "Warning - ExtRst has wrong magic marker, expecting 1 but found " + reserved + " - ignoring");
-             // Grab all the remaining data, and ignore it
-             for(int i=0; i<expectedLength-2; i++) {
-                in.readByte();
-             }
-             // And make us be empty
-             populateEmpty();
-             return;
-          }
-
-          // Carry on reading in as normal
-          short stringDataSize = in.readShort();
-          
-          formattingFontIndex = in.readShort();
-          formattingOptions   = in.readShort();
-          
-          // RPHSSub
-          numberOfRuns = in.readUShort();
-          short length1 = in.readShort();
-          // No really. Someone clearly forgot to read
-          //  the docs on their datastructure...
-          short length2 = in.readShort();
-          // And sometimes they write out garbage :(
-          if(length1 == 0 && length2 > 0) {
-             length2 = 0;
-          }
-          if(length1 != length2) {
-             throw new IllegalStateException(
-                   "The two length fields of the Phonetic Text don't agree! " +
-                   length1 + " vs " + length2
-             );
-          }
-          phoneticText = StringUtil.readUnicodeLE(in, length1);
-          
-          int runData = stringDataSize - 4 - 6 - (2*phoneticText.length());
-          int numRuns = (runData / 6);
-          phRuns = new PhRun[numRuns];
-          for(int i=0; i<phRuns.length; i++) {
-             phRuns[i] = new PhRun(in);
-          }
+        boolean isCompressed = ((field_2_optionflags & 1) == 0);
+        int cc = getCharCount();
+        field_3_string = (isCompressed) ? in.readCompressedUnicode(cc) : in.readUnicodeLEString(cc);
 
-          int extraDataLength = runData - (numRuns*6);
-          if(extraDataLength < 0) {
-        	 _logger.log( POILogger.WARN, "Warning - ExtRst overran by " + (0-extraDataLength) + " bytes");
-             extraDataLength = 0;
-          }
-          extraData = IOUtils.safelyAllocate(extraDataLength, MAX_RECORD_LENGTH);
-          for(int i=0; i<extraData.length; i++) {
-             extraData[i] = in.readByte();
-          }
-       }
-       /**
-        * Returns our size, excluding our 
-        *  4 byte header
-        */
-       protected int getDataSize() {
-          return 4 + 6 + (2*phoneticText.length()) + 
-             (6*phRuns.length) + extraData.length;
-       }
-       protected void serialize(ContinuableRecordOutput out) {
-          int dataSize = getDataSize();
-          
-          out.writeContinueIfRequired(8);
-          out.writeShort(reserved);
-          out.writeShort(dataSize);
-          out.writeShort(formattingFontIndex);
-          out.writeShort(formattingOptions);
-          
-          out.writeContinueIfRequired(6);
-          out.writeShort(numberOfRuns);
-          out.writeShort(phoneticText.length());
-          out.writeShort(phoneticText.length());
-          
-          out.writeContinueIfRequired(phoneticText.length()*2);
-          StringUtil.putUnicodeLE(phoneticText, out);
-          
-          for(int i=0; i<phRuns.length; i++) {
-             phRuns[i].serialize(out);
-          }
-          
-          out.write(extraData);
-       }
-
-       public boolean equals(Object obj) {
-          if(! (obj instanceof ExtRst)) {
-             return false;
-          }
-          ExtRst other = (ExtRst)obj;
-          return (compareTo(other) == 0);
-       }
-       public int compareTo(ExtRst o) {
-          int result;
-          
-          result = reserved - o.reserved;
-          if (result != 0) {
-              return result;
-          }
-          result = formattingFontIndex - o.formattingFontIndex;
-          if (result != 0) {
-              return result;
-          }
-          result = formattingOptions - o.formattingOptions;
-          if (result != 0) {
-              return result;
-          }
-          result = numberOfRuns - o.numberOfRuns;
-          if (result != 0) {
-              return result;
-          }
-          
-          result = phoneticText.compareTo(o.phoneticText);
-          if (result != 0) {
-              return result;
-          }
-          
-          result = phRuns.length - o.phRuns.length;
-          if (result != 0) {
-              return result;
-          }
-          for(int i=0; i<phRuns.length; i++) {
-             result = phRuns[i].phoneticTextFirstCharacterOffset - o.phRuns[i].phoneticTextFirstCharacterOffset;
-             if (result != 0) {
-                 return result;
-             }
-             result = phRuns[i].realTextFirstCharacterOffset - o.phRuns[i].realTextFirstCharacterOffset;
-             if (result != 0) {
-                 return result;
-             }
-             result = phRuns[i].realTextLength - o.phRuns[i].realTextLength;
-             if (result != 0) {
-                 return result;
-             }
+        if (isRichText() && (runCount > 0)) {
+          field_4_format_runs = new ArrayList<>(runCount);
+          for (int i=0;i<runCount;i++) {
+            field_4_format_runs.add(new FormatRun(in));
           }
-          
-          result = Arrays.hashCode(extraData)-Arrays.hashCode(o.extraData);
-          
-          return result;
-       }
+        }
 
-       @Override
-       public int hashCode() {
-           int hash = reserved;
-           hash = 31*hash+formattingFontIndex;
-           hash = 31*hash+formattingOptions;
-           hash = 31*hash+numberOfRuns;
-           hash = 31*hash+phoneticText.hashCode();
-
-           if (phRuns != null) {
-               for (PhRun ph : phRuns) {
-                   hash = 31*hash+ph.phoneticTextFirstCharacterOffset;
-                   hash = 31*hash+ph.realTextFirstCharacterOffset;
-                   hash = 31*hash+ph.realTextLength;
-               }
-           }
-           return hash;
-       }
-
-       protected ExtRst clone() {
-          ExtRst ext = new ExtRst();
-          ext.reserved = reserved;
-          ext.formattingFontIndex = formattingFontIndex;
-          ext.formattingOptions = formattingOptions;
-          ext.numberOfRuns = numberOfRuns;
-          ext.phoneticText = phoneticText;
-          ext.phRuns = new PhRun[phRuns.length];
-          for(int i=0; i<ext.phRuns.length; i++) {
-             ext.phRuns[i] = new PhRun(
-                   phRuns[i].phoneticTextFirstCharacterOffset,
-                   phRuns[i].realTextFirstCharacterOffset,
-                   phRuns[i].realTextLength
-             );
+        if (isExtendedText() && (extensionLength > 0)) {
+          field_5_ext_rst = new ExtRst(new ContinuableRecordInput(in), extensionLength);
+          if(field_5_ext_rst.getDataSize()+4 != extensionLength) {
+             _logger.log(POILogger.WARN, "ExtRst was supposed to be " + extensionLength + " bytes long, but seems to actually be " + (field_5_ext_rst.getDataSize() + 4));
           }
-          return ext;
-       }
-       
-       public short getFormattingFontIndex() {
-         return formattingFontIndex;
-       }
-       public short getFormattingOptions() {
-         return formattingOptions;
-       }
-       public int getNumberOfRuns() {
-         return numberOfRuns;
-       }
-       public String getPhoneticText() {
-         return phoneticText;
-       }
-       public PhRun[] getPhRuns() {
-         return phRuns;
-       }
-    }
-    public static class PhRun {
-       private int phoneticTextFirstCharacterOffset;
-       private int realTextFirstCharacterOffset;
-       private int realTextLength;
-       
-       public PhRun(int phoneticTextFirstCharacterOffset,
-            int realTextFirstCharacterOffset, int realTextLength) {
-         this.phoneticTextFirstCharacterOffset = phoneticTextFirstCharacterOffset;
-         this.realTextFirstCharacterOffset = realTextFirstCharacterOffset;
-         this.realTextLength = realTextLength;
-      }
-      private PhRun(LittleEndianInput in) {
-          phoneticTextFirstCharacterOffset = in.readUShort();
-          realTextFirstCharacterOffset = in.readUShort();
-          realTextLength = in.readUShort();
-       }
-       private void serialize(ContinuableRecordOutput out) {
-          out.writeContinueIfRequired(6);
-          out.writeShort(phoneticTextFirstCharacterOffset);
-          out.writeShort(realTextFirstCharacterOffset);
-          out.writeShort(realTextLength);
-       }
-    }
-
-    private UnicodeString() {
-     //Used for clone method.
-    }
-
-    public UnicodeString(String str)
-    {
-      setString(str);
+        }
     }
 
-
-
     public int hashCode()
     {
         int stringHash = 0;
@@ -405,8 +128,8 @@ public class UnicodeString implements Co
 
         //OK lets do this in stages to return a quickly, first check the actual string
         if (field_1_charCount != other.field_1_charCount
-            || field_2_optionflags != other.field_2_optionflags
-            || !field_3_string.equals(other.field_3_string)) {
+                || field_2_optionflags != other.field_2_optionflags
+                || !field_3_string.equals(other.field_3_string)) {
             return false;
         }
 
@@ -422,16 +145,16 @@ public class UnicodeString implements Co
         //Strings are equal, so now compare formatting runs.
         int size = field_4_format_runs.size();
         if (size != other.field_4_format_runs.size()) {
-          return false;
+            return false;
         }
 
         for (int i=0;i<size;i++) {
-          FormatRun run1 = field_4_format_runs.get(i);
-          FormatRun run2 = other.field_4_format_runs.get(i);
+            FormatRun run1 = field_4_format_runs.get(i);
+            FormatRun run2 = other.field_4_format_runs.get(i);
 
-          if (!run1.equals(run2)) {
-            return false;
-          }
+            if (!run1.equals(run2)) {
+                return false;
+            }
         }
 
         // Well the format runs are equal as well!, better check the ExtRst data
@@ -440,46 +163,8 @@ public class UnicodeString implements Co
         } else if (other.field_5_ext_rst == null) {
             return false;
         }
-            
-       return field_5_ext_rst.equals(other.field_5_ext_rst);
-    }
-
-    /**
-     * construct a unicode string record and fill its fields, ID is ignored
-     * @param in the RecordInputstream to read the record from
-     */
-    public UnicodeString(RecordInputStream in) {
-        field_1_charCount   = in.readShort();
-        field_2_optionflags = in.readByte();
-
-        int runCount = 0;
-        int extensionLength = 0;
-        //Read the number of rich runs if rich text.
-        if (isRichText()) {
-            runCount = in.readShort();
-        }
-        //Read the size of extended data if present.
-        if (isExtendedText()) {
-            extensionLength = in.readInt();
-        }
-
-        boolean isCompressed = ((field_2_optionflags & 1) == 0);
-        int cc = getCharCount();
-        field_3_string = (isCompressed) ? in.readCompressedUnicode(cc) : in.readUnicodeLEString(cc);
-
-        if (isRichText() && (runCount > 0)) {
-          field_4_format_runs = new ArrayList<>(runCount);
-          for (int i=0;i<runCount;i++) {
-            field_4_format_runs.add(new FormatRun(in));
-          }
-        }
 
-        if (isExtendedText() && (extensionLength > 0)) {
-          field_5_ext_rst = new ExtRst(new ContinuableRecordInput(in), extensionLength);
-          if(field_5_ext_rst.getDataSize()+4 != extensionLength) {
-             _logger.log(POILogger.WARN, "ExtRst was supposed to be " + extensionLength + " bytes long, but seems to actually be " + (field_5_ext_rst.getDataSize() + 4));
-          }
-        }
+        return field_5_ext_rst.equals(other.field_5_ext_rst);
     }
 
 
@@ -737,7 +422,7 @@ public class UnicodeString implements Co
         if (isExtendedText() && field_5_ext_rst != null) {
             extendedDataSize = 4 + field_5_ext_rst.getDataSize();
         }
-       
+
         // Serialise the bulk of the String
         // The writeString handles tricky continue stuff for us
         out.writeString(field_3_string, numberOfRichTextRuns, extendedDataSize);
@@ -812,21 +497,16 @@ public class UnicodeString implements Co
         return extBit.isSet(getOptionFlags());
     }
 
-    public Object clone() {
-        UnicodeString str = new UnicodeString();
-        str.field_1_charCount = field_1_charCount;
-        str.field_2_optionflags = field_2_optionflags;
-        str.field_3_string = field_3_string;
-        if (field_4_format_runs != null) {
-          str.field_4_format_runs = new ArrayList<>();
-          for (FormatRun r : field_4_format_runs) {
-            str.field_4_format_runs.add(new FormatRun(r._character, r._fontIndex));
-          }
-        }
-        if (field_5_ext_rst != null) {
-           str.field_5_ext_rst = field_5_ext_rst.clone();
-        }
+    @Override
+    @SuppressWarnings("squid:S2975")
+    @Deprecated
+    @Removal(version = "5.0.0")
+    public UnicodeString clone() {
+        return copy();
+    }
 
-        return str;
+    @Override
+    public UnicodeString copy() {
+        return new UnicodeString(this);
     }
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/cont/ContinuableRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/cont/ContinuableRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/cont/ContinuableRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/cont/ContinuableRecord.java Sun Dec 22 21:44:45 2019
@@ -29,9 +29,12 @@ import org.apache.poi.util.LittleEndianO
  */
 public abstract class ContinuableRecord extends Record {
 
-	protected ContinuableRecord() {
-		// no fields to initialise
+	protected ContinuableRecord() {}
+
+	protected ContinuableRecord(ContinuableRecord other) {
+		super(other);
 	}
+
 	/**
 	 * Serializes this record's content to the supplied data output.<p>
 	 * The standard BIFF header (ushort sid, ushort size) has been handled by the superclass, so

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/DataItemRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/DataItemRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/DataItemRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/DataItemRecord.java Sun Dec 22 21:44:45 2019
@@ -24,9 +24,7 @@ import org.apache.poi.util.LittleEndianO
 import org.apache.poi.util.StringUtil;
 
 /**
- * SXDI - Data Item (0x00C5)<br>
- * 
- * @author Patrick Cheng
+ * SXDI - Data Item (0x00C5)
  */
 public final class DataItemRecord extends StandardRecord {
 	public static final short sid = 0x00C5;
@@ -38,7 +36,18 @@ public final class DataItemRecord extend
 	private int isxvi;
 	private int ifmt;
 	private String name;
-	
+
+	public DataItemRecord(DataItemRecord other) {
+		super(other);
+		isxvdData = other.isxvdData;
+		iiftab = other.iiftab;
+		df = other.df;
+		isxvd = other.isxvd;
+		isxvi = other.isxvi;
+		ifmt = other.ifmt;
+		name = other.name;
+	}
+
 	public DataItemRecord(RecordInputStream in) {
 		isxvdData = in.readUShort();
 		iiftab = in.readUShort();
@@ -46,20 +55,20 @@ public final class DataItemRecord extend
 		isxvd = in.readUShort();
 		isxvi = in.readUShort();
 		ifmt = in.readUShort();
-		
+
 		name = in.readString();
 	}
-	
+
 	@Override
 	protected void serialize(LittleEndianOutput out) {
-		
+
 		out.writeShort(isxvdData);
 		out.writeShort(iiftab);
 		out.writeShort(df);
 		out.writeShort(isxvd);
 		out.writeShort(isxvi);
 		out.writeShort(ifmt);
-		
+
 		StringUtil.writeUnicodeString(out, name);
 	}
 
@@ -76,7 +85,7 @@ public final class DataItemRecord extend
 	@Override
 	public String toString() {
 		StringBuilder buffer = new StringBuilder();
-		
+
 		buffer.append("[SXDI]\n");
 		buffer.append("  .isxvdData = ").append(HexDump.shortToHex(isxvdData)).append("\n");
 		buffer.append("  .iiftab = ").append(HexDump.shortToHex(iiftab)).append("\n");
@@ -87,4 +96,9 @@ public final class DataItemRecord extend
 		buffer.append("[/SXDI]\n");
 		return buffer.toString();
 	}
+
+	@Override
+	public DataItemRecord copy() {
+		return new DataItemRecord(this);
+	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ExtendedPivotTableViewFieldsRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ExtendedPivotTableViewFieldsRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ExtendedPivotTableViewFieldsRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ExtendedPivotTableViewFieldsRecord.java Sun Dec 22 21:44:45 2019
@@ -25,9 +25,7 @@ import org.apache.poi.util.RecordFormatE
 import org.apache.poi.util.StringUtil;
 
 /**
- * SXVDEX - Extended PivotTable View Fields (0x0100)<br>
- * 
- * @author Patrick Cheng
+ * SXVDEX - Extended PivotTable View Fields (0x0100)
  */
 public final class ExtendedPivotTableViewFieldsRecord extends StandardRecord {
 	public static final short sid = 0x0100;
@@ -45,8 +43,19 @@ public final class ExtendedPivotTableVie
 	/** custom sub-total name */
 	private String _subtotalName;
 
-	public ExtendedPivotTableViewFieldsRecord(RecordInputStream in) {
+	public ExtendedPivotTableViewFieldsRecord(ExtendedPivotTableViewFieldsRecord other) {
+		super(other);
+		_grbit1 = other._grbit1;
+		_grbit2 = other._grbit2;
+		_citmShow = other._citmShow;
+		_isxdiSort = other._isxdiSort;
+		_isxdiShow = other._isxdiShow;
+		_reserved1 = other._reserved1;
+		_reserved2 = other._reserved2;
+		_subtotalName = other._subtotalName;
+	}
 
+	public ExtendedPivotTableViewFieldsRecord(RecordInputStream in) {
 		_grbit1 = in.readInt();
 		_grbit2 = in.readUByte();
 		_citmShow = in.readUByte();
@@ -99,7 +108,7 @@ public final class ExtendedPivotTableVie
 
 	@Override
 	protected int getDataSize() {
-		
+
 		return 4 + 1 + 1 + 2 + 2 + 2 +  4 + 4 +
 					(_subtotalName == null ? 0 : (2*_subtotalName.length())); // in unicode
 	}
@@ -124,4 +133,9 @@ public final class ExtendedPivotTableVie
 		buffer.append("[/SXVDEX]\n");
 		return buffer.toString();
 	}
+
+	@Override
+	public ExtendedPivotTableViewFieldsRecord copy() {
+		return new ExtendedPivotTableViewFieldsRecord(this);
+	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/PageItemRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/PageItemRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/PageItemRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/PageItemRecord.java Sun Dec 22 21:44:45 2019
@@ -17,6 +17,8 @@
 
 package org.apache.poi.hssf.record.pivottable;
 
+import java.util.stream.Stream;
+
 import org.apache.poi.hssf.record.RecordInputStream;
 import org.apache.poi.hssf.record.StandardRecord;
 import org.apache.poi.util.HexDump;
@@ -24,9 +26,7 @@ import org.apache.poi.util.LittleEndianO
 import org.apache.poi.util.RecordFormatException;
 
 /**
- * SXPI - Page Item (0x00B6)<br>
- * 
- * @author Patrick Cheng
+ * SXPI - Page Item (0x00B6)
  */
 public final class PageItemRecord extends StandardRecord {
 	public static final short sid = 0x00B6;
@@ -40,6 +40,12 @@ public final class PageItemRecord extend
 		/** Object ID for the drop-down arrow */
 		private int _idObj;
 
+		public FieldInfo(FieldInfo other) {
+			_isxvi = other._isxvi;
+			_isxvd = other._isxvd;
+			_idObj = other._idObj;
+		}
+
 		public FieldInfo(RecordInputStream in) {
 			_isxvi = in.readShort();
 			_isxvd = in.readShort();
@@ -63,6 +69,11 @@ public final class PageItemRecord extend
 
 	private final FieldInfo[] _fieldInfos;
 
+	public PageItemRecord(PageItemRecord other) {
+		super(other);
+		_fieldInfos = Stream.of(other._fieldInfos).map(FieldInfo::new).toArray(FieldInfo[]::new);
+	}
+
 	public PageItemRecord(RecordInputStream in) {
 		int dataSize = in.remaining();
 		if (dataSize % FieldInfo.ENCODED_SIZE != 0) {
@@ -80,8 +91,8 @@ public final class PageItemRecord extend
 
 	@Override
 	protected void serialize(LittleEndianOutput out) {
-		for (int i = 0; i < _fieldInfos.length; i++) {
-			_fieldInfos[i].serialize(out);
+		for (FieldInfo fieldInfo : _fieldInfos) {
+			fieldInfo.serialize(out);
 		}
 	}
 
@@ -108,4 +119,9 @@ public final class PageItemRecord extend
 		sb.append("[/SXPI]\n");
 		return sb.toString();
 	}
+
+	@Override
+	public PageItemRecord copy() {
+		return new PageItemRecord(this);
+	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/StreamIDRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/StreamIDRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/StreamIDRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/StreamIDRecord.java Sun Dec 22 21:44:45 2019
@@ -23,19 +23,22 @@ import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndianOutput;
 
 /**
- * SXIDSTM - Stream ID (0x00D5)<br>
- * 
- * @author Patrick Cheng
+ * SXIDSTM - Stream ID (0x00D5)
  */
 public final class StreamIDRecord extends StandardRecord {
 	public static final short sid = 0x00D5;
 
 	private int idstm;
-	
+
+	public StreamIDRecord(StreamIDRecord other) {
+		super(other);
+		idstm = other.idstm;
+	}
+
 	public StreamIDRecord(RecordInputStream in) {
 		idstm = in.readShort();
 	}
-	
+
 	@Override
 	protected void serialize(LittleEndianOutput out) {
 		out.writeShort(idstm);
@@ -61,4 +64,9 @@ public final class StreamIDRecord extend
 		buffer.append("[/SXIDSTM]\n");
 		return buffer.toString();
 	}
+
+	@Override
+	public StreamIDRecord copy() {
+		return new StreamIDRecord(this);
+	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewDefinitionRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewDefinitionRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewDefinitionRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewDefinitionRecord.java Sun Dec 22 21:44:45 2019
@@ -38,26 +38,52 @@ public final class ViewDefinitionRecord
 	private int colFirstData;
 	private int iCache;
 	private int reserved;
-	
+
 	private int sxaxis4Data;
 	private int ipos4Data;
 	private int cDim;
-	
+
 	private int cDimRw;
-	
+
 	private int cDimCol;
 	private int cDimPg;
-	
+
 	private int cDimData;
 	private int cRw;
 	private int cCol;
 	private int grbit;
 	private int itblAutoFmt;
-	
+
 	private String dataField;
 	private String name;
 
-	
+
+	public ViewDefinitionRecord(ViewDefinitionRecord other) {
+		super(other);
+		rwFirst = other.rwFirst;
+		rwLast = other.rwLast;
+		colFirst = other.colFirst;
+		colLast = other.colLast;
+		rwFirstHead = other.rwFirstHead;
+		rwFirstData = other.rwFirstData;
+		colFirstData = other.colFirstData;
+		iCache = other.iCache;
+		reserved = other.reserved;
+		sxaxis4Data = other.sxaxis4Data;
+		ipos4Data = other.ipos4Data;
+		cDim = other.cDim;
+		cDimRw = other.cDimRw;
+		cDimCol = other.cDimCol;
+		cDimPg = other.cDimPg;
+		cDimData = other.cDimData;
+		cRw = other.cRw;
+		cCol = other.cCol;
+		grbit = other.grbit;
+		itblAutoFmt = other.itblAutoFmt;
+		name = other.name;
+		dataField = other.dataField;
+	}
+
 	public ViewDefinitionRecord(RecordInputStream in) {
 		rwFirst = in.readUShort();
 		rwLast = in.readUShort();
@@ -85,7 +111,7 @@ public final class ViewDefinitionRecord
 		name = StringUtil.readUnicodeString(in, cchName);
 		dataField = StringUtil.readUnicodeString(in, cchData);
 	}
-	
+
 	@Override
 	protected void serialize(LittleEndianOutput out) {
 		out.writeShort(rwFirst);
@@ -112,7 +138,7 @@ public final class ViewDefinitionRecord
 		out.writeShort(dataField.length());
 
 		StringUtil.writeUnicodeStringFlagAndData(out, name);
-		StringUtil.writeUnicodeStringFlagAndData(out, dataField);		
+		StringUtil.writeUnicodeStringFlagAndData(out, dataField);
 	}
 
 	@Override
@@ -157,4 +183,9 @@ public final class ViewDefinitionRecord
 		buffer.append("[/SXVIEW]\n");
 		return buffer.toString();
 	}
+
+	@Override
+	public ViewDefinitionRecord copy() {
+		return new ViewDefinitionRecord(this);
+	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewFieldsRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewFieldsRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewFieldsRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewFieldsRecord.java Sun Dec 22 21:44:45 2019
@@ -24,9 +24,7 @@ import org.apache.poi.util.LittleEndianO
 import org.apache.poi.util.StringUtil;
 
 /**
- * SXVD - View Fields (0x00B1)<br>
- * 
- * @author Patrick Cheng
+ * SXVD - View Fields (0x00B1)
  */
 public final class ViewFieldsRecord extends StandardRecord {
 	public static final short sid = 0x00B1;
@@ -40,18 +38,31 @@ public final class ViewFieldsRecord exte
 	private int _cSub;
 	private int _grbitSub;
 	private int _cItm;
-	
+
 	private String _name;
-	
+
 	/**
 	 * values for the {@link ViewFieldsRecord#_sxaxis} field
 	 */
-	private static final class Axis {
-		public static final int NO_AXIS = 0;
-		public static final int ROW = 1;
-		public static final int COLUMN = 2;
-		public static final int PAGE = 4;
-		public static final int DATA = 8;
+	private enum Axis {
+		NO_AXIS(0),
+		ROW(1),
+		COLUMN(2),
+		PAGE(4),
+		DATA(8);
+		int id;
+		Axis(int id) {
+			this.id = id;
+		}
+	}
+
+	public ViewFieldsRecord(ViewFieldsRecord other) {
+		super(other);
+		_sxaxis = other._sxaxis;
+		_cSub = other._cSub;
+		_grbitSub = other._grbitSub;
+		_cItm = other._cItm;
+		_name = other._name;
 	}
 
 	public ViewFieldsRecord(RecordInputStream in) {
@@ -59,7 +70,7 @@ public final class ViewFieldsRecord exte
 		_cSub = in.readShort();
 		_grbitSub = in.readShort();
 		_cItm = in.readShort();
-		
+
 		int cchName = in.readUShort();
 		if (cchName != STRING_NOT_PRESENT_LEN) {
 			int flag = in.readByte();
@@ -70,15 +81,15 @@ public final class ViewFieldsRecord exte
 			}
 		}
 	}
-	
+
 	@Override
 	protected void serialize(LittleEndianOutput out) {
-		
+
 		out.writeShort(_sxaxis);
 		out.writeShort(_cSub);
 		out.writeShort(_grbitSub);
 		out.writeShort(_cItm);
-		
+
 		if (_name != null) {
 			StringUtil.writeUnicodeString(out, _name);
 		} else {
@@ -91,8 +102,8 @@ public final class ViewFieldsRecord exte
 		if (_name == null) {
 			return BASE_SIZE;
 		}
-		return BASE_SIZE 
-			+ 1 // unicode flag 
+		return BASE_SIZE
+			+ 1 // unicode flag
 			+ _name.length() * (StringUtil.hasMultibyte(_name) ? 2 : 1);
 	}
 
@@ -110,8 +121,13 @@ public final class ViewFieldsRecord exte
 		buffer.append("    .grbitSub  = ").append(HexDump.shortToHex(_grbitSub)).append('\n');
 		buffer.append("    .cItm      = ").append(HexDump.shortToHex(_cItm)).append('\n');
 		buffer.append("    .name      = ").append(_name).append('\n');
-		
+
 		buffer.append("[/SXVD]\n");
 		return buffer.toString();
 	}
+
+	@Override
+	public ViewFieldsRecord copy() {
+		return new ViewFieldsRecord(this);
+	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewSourceRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewSourceRecord.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewSourceRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/pivottable/ViewSourceRecord.java Sun Dec 22 21:44:45 2019
@@ -24,18 +24,21 @@ import org.apache.poi.util.LittleEndianO
 
 /**
  * SXVS - View Source (0x00E3)<br>
- * 
- * @author Patrick Cheng
  */
 public final class ViewSourceRecord extends StandardRecord {
 	public static final short sid = 0x00E3;
 
 	private int vs;
-	
+
+	public ViewSourceRecord(ViewSourceRecord other) {
+		super(other);
+		vs = other.vs;
+	}
+
 	public ViewSourceRecord(RecordInputStream in) {
 		vs = in.readShort();
 	}
-	
+
 	@Override
 	protected void serialize(LittleEndianOutput out) {
 		out.writeShort(vs);
@@ -61,4 +64,9 @@ public final class ViewSourceRecord exte
 		buffer.append("[/SXVS]\n");
 		return buffer.toString();
 	}
+
+	@Override
+	public ViewSourceRecord copy() {
+		return new ViewSourceRecord(this);
+	}
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java Sun Dec 22 21:44:45 2019
@@ -19,7 +19,9 @@
 package org.apache.poi.hssf.usermodel;
 
 import java.util.List;
+import java.util.Objects;
 
+import org.apache.poi.common.Duplicatable;
 import org.apache.poi.hssf.model.InternalWorkbook;
 import org.apache.poi.hssf.record.ExtendedFormatRecord;
 import org.apache.poi.hssf.record.FontRecord;
@@ -41,7 +43,7 @@ import org.apache.poi.ss.usermodel.Verti
  * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#getCellStyleAt(int)
  * @see org.apache.poi.hssf.usermodel.HSSFCell#setCellStyle(HSSFCellStyle)
  */
-public final class HSSFCellStyle implements CellStyle {
+public final class HSSFCellStyle implements CellStyle, Duplicatable {
     private final ExtendedFormatRecord _format;
     private final short                _index;
     private final InternalWorkbook     _workbook;
@@ -59,6 +61,13 @@ public final class HSSFCellStyle impleme
         _format     = rec;
     }
 
+    protected HSSFCellStyle(HSSFCellStyle other) {
+        _workbook = other._workbook;
+        _index = other._index;
+        _format = other._format;
+    }
+
+
     /**
      * get the index within the HSSFWorkbook (sequence within the collection of ExtnededFormat objects)
      * @return unique index number of the underlying record this style represents (probably you don't care
@@ -863,11 +872,7 @@ public final class HSSFCellStyle impleme
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((_format == null) ? 0 : _format.hashCode());
-        result = prime * result + _index;
-        return result;
+        return Objects.hash(_format, _index);
     }
 
     @Override
@@ -895,4 +900,8 @@ public final class HSSFCellStyle impleme
         return false;
     }
 
+    @Override
+    public HSSFCellStyle copy() {
+        return new HSSFCellStyle(this);
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFChart.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFChart.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFChart.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFChart.java Sun Dec 22 21:44:45 2019
@@ -34,38 +34,7 @@ import org.apache.poi.hssf.record.Record
 import org.apache.poi.hssf.record.SCLRecord;
 import org.apache.poi.hssf.record.UnknownRecord;
 import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.chart.AreaFormatRecord;
-import org.apache.poi.hssf.record.chart.AxisLineFormatRecord;
-import org.apache.poi.hssf.record.chart.AxisOptionsRecord;
-import org.apache.poi.hssf.record.chart.AxisParentRecord;
-import org.apache.poi.hssf.record.chart.AxisRecord;
-import org.apache.poi.hssf.record.chart.AxisUsedRecord;
-import org.apache.poi.hssf.record.chart.BarRecord;
-import org.apache.poi.hssf.record.chart.BeginRecord;
-import org.apache.poi.hssf.record.chart.CategorySeriesAxisRecord;
-import org.apache.poi.hssf.record.chart.ChartFormatRecord;
-import org.apache.poi.hssf.record.chart.ChartRecord;
-import org.apache.poi.hssf.record.chart.ChartTitleFormatRecord;
-import org.apache.poi.hssf.record.chart.DataFormatRecord;
-import org.apache.poi.hssf.record.chart.DefaultDataLabelTextPropertiesRecord;
-import org.apache.poi.hssf.record.chart.EndRecord;
-import org.apache.poi.hssf.record.chart.FontBasisRecord;
-import org.apache.poi.hssf.record.chart.FontIndexRecord;
-import org.apache.poi.hssf.record.chart.FrameRecord;
-import org.apache.poi.hssf.record.chart.LegendRecord;
-import org.apache.poi.hssf.record.chart.LineFormatRecord;
-import org.apache.poi.hssf.record.chart.LinkedDataRecord;
-import org.apache.poi.hssf.record.chart.PlotAreaRecord;
-import org.apache.poi.hssf.record.chart.PlotGrowthRecord;
-import org.apache.poi.hssf.record.chart.SeriesIndexRecord;
-import org.apache.poi.hssf.record.chart.SeriesRecord;
-import org.apache.poi.hssf.record.chart.SeriesTextRecord;
-import org.apache.poi.hssf.record.chart.SeriesToChartGroupRecord;
-import org.apache.poi.hssf.record.chart.SheetPropertiesRecord;
-import org.apache.poi.hssf.record.chart.TextRecord;
-import org.apache.poi.hssf.record.chart.TickRecord;
-import org.apache.poi.hssf.record.chart.UnitsRecord;
-import org.apache.poi.hssf.record.chart.ValueRangeRecord;
+import org.apache.poi.hssf.record.chart.*;
 import org.apache.poi.ss.formula.ptg.Area3DPtg;
 import org.apache.poi.ss.formula.ptg.AreaPtgBase;
 import org.apache.poi.ss.formula.ptg.Ptg;
@@ -74,8 +43,6 @@ import org.apache.poi.ss.util.CellRangeA
 
 /**
  * Has methods for construction of a chart object.
- *
- * @author Glen Stampoultzis (glens at apache.org)
  */
 public final class HSSFChart {
     private HSSFSheet sheet;
@@ -235,11 +202,11 @@ public final class HSSFChart {
                     lastSeries.insertData(linkedDataRecord);
                 }
             }
-            
+
             if (lastChart == null) {
                 continue;
             }
-            
+
             if (r instanceof LegendRecord) {
                 lastChart.legendRecord = (LegendRecord)r;
             } else if(r instanceof SeriesRecord) {
@@ -1254,30 +1221,30 @@ public final class HSSFChart {
             } else if (record instanceof EndRecord) {
                 newRecord = new EndRecord();
             } else if (record instanceof SeriesRecord) {
-                SeriesRecord seriesRecord = (SeriesRecord) ((SeriesRecord)record).clone();
+                SeriesRecord seriesRecord = (SeriesRecord) ((SeriesRecord)record).copy();
                 newSeries = new HSSFSeries(seriesRecord);
                 newRecord = seriesRecord;
             } else if (record instanceof LinkedDataRecord) {
-                LinkedDataRecord linkedDataRecord = ((LinkedDataRecord)record).clone();
+                LinkedDataRecord linkedDataRecord = ((LinkedDataRecord)record).copy();
                 if (newSeries != null) {
                     newSeries.insertData(linkedDataRecord);
                 }
                 newRecord = linkedDataRecord;
             } else if (record instanceof DataFormatRecord) {
-                DataFormatRecord dataFormatRecord = ((DataFormatRecord)record).clone();
+                DataFormatRecord dataFormatRecord = ((DataFormatRecord)record).copy();
 
                 dataFormatRecord.setSeriesIndex((short)seriesIdx) ;
                 dataFormatRecord.setSeriesNumber((short)seriesIdx) ;
 
                 newRecord = dataFormatRecord;
             } else if (record instanceof SeriesTextRecord) {
-                SeriesTextRecord seriesTextRecord = (SeriesTextRecord) ((SeriesTextRecord)record).clone();
+                SeriesTextRecord seriesTextRecord = (SeriesTextRecord) ((SeriesTextRecord)record).copy();
                 if (newSeries != null) {
                     newSeries.setSeriesTitleText(seriesTextRecord);
                 }
                 newRecord = seriesTextRecord;
             } else if (record instanceof Record) {
-                newRecord = (Record) ((Record)record).clone();
+                newRecord = (Record) ((Record)record).copy();
             }
 
             if (newRecord != null)

Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java Sun Dec 22 21:44:45 2019
@@ -39,7 +39,7 @@ public class HSSFHyperlink implements Hy
 
     /**
      * Construct a new hyperlink
-     * 
+     *
      * This method is internal to be used only by
      * {@link HSSFCreationHelper#createHyperlink(HyperlinkType)}.
      *
@@ -76,7 +76,7 @@ public class HSSFHyperlink implements Hy
         this.record = record;
         link_type = getType(record);
     }
-    
+
     private static HyperlinkType getType(HyperlinkRecord record) {
         HyperlinkType link_type;
         // Figure out the type
@@ -94,11 +94,11 @@ public class HSSFHyperlink implements Hy
         }
         return link_type;
     }
-    
+
     protected HSSFHyperlink(Hyperlink other) {
         if (other instanceof HSSFHyperlink) {
             HSSFHyperlink hlink = (HSSFHyperlink) other;
-            record = hlink.record.clone();
+            record = hlink.record.copy();
             link_type = getType(record);
         }
         else {
@@ -264,7 +264,7 @@ public class HSSFHyperlink implements Hy
     public HyperlinkType getType() {
         return link_type;
     }
-    
+
     /**
      * Return the type of this hyperlink
      *
@@ -277,7 +277,7 @@ public class HSSFHyperlink implements Hy
     public HyperlinkType getTypeEnum() {
         return getType();
     }
-    
+
     /**
      * @return whether the objects have the same HyperlinkRecord
      */
@@ -288,7 +288,7 @@ public class HSSFHyperlink implements Hy
         HSSFHyperlink otherLink = (HSSFHyperlink) other;
         return record == otherLink.record;
     }
-    
+
     @Override
     public int hashCode() {
         return record.hashCode();

Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java Sun Dec 22 21:44:45 2019
@@ -21,8 +21,8 @@ import java.util.Iterator;
 
 import org.apache.poi.hssf.model.InternalWorkbook;
 import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.common.FormatRun;
 import org.apache.poi.hssf.record.common.UnicodeString;
-import org.apache.poi.hssf.record.common.UnicodeString.FormatRun;
 import org.apache.poi.ss.usermodel.Font;
 import org.apache.poi.ss.usermodel.RichTextString;
 /**
@@ -105,9 +105,7 @@ public final class HSSFRichTextString im
      *  be affected by changes that we make to this string.
      */
     private UnicodeString cloneStringIfRequired() {
-      if (_book == null)
-        return _string;
-        return (UnicodeString)_string.clone();
+        return (_book == null) ? _string : _string.copy();
     }
 
     private void addToSSTIfRequired() {
@@ -148,16 +146,16 @@ public final class HSSFRichTextString im
         Iterator<FormatRun> formatting = _string.formatIterator();
         if (formatting != null) {
           while (formatting.hasNext()) {
-            UnicodeString.FormatRun r = formatting.next();
+            FormatRun r = formatting.next();
             if ((r.getCharacterPos() >= startIndex) && (r.getCharacterPos() < endIndex))
               formatting.remove();
           }
         }
 
 
-        _string.addFormatRun(new UnicodeString.FormatRun((short)startIndex, fontIndex));
+        _string.addFormatRun(new FormatRun((short)startIndex, fontIndex));
         if (endIndex != length())
-          _string.addFormatRun(new UnicodeString.FormatRun((short)endIndex, currentFont));
+          _string.addFormatRun(new FormatRun((short)endIndex, currentFont));
 
         addToSSTIfRequired();
     }
@@ -240,9 +238,9 @@ public final class HSSFRichTextString im
      */
     public short getFontAtIndex( int index ) {
       int size = _string.getFormatRunCount();
-      UnicodeString.FormatRun currentRun = null;
+      FormatRun currentRun = null;
       for (int i=0;i<size;i++) {
-        UnicodeString.FormatRun r = _string.getFormatRun(i);
+        FormatRun r = _string.getFormatRun(i);
         if (r.getCharacterPos() > index) {
             break;
         }
@@ -270,7 +268,7 @@ public final class HSSFRichTextString im
      * @return  the index within the string.
      */
     public int getIndexOfFormattingRun(int index) {
-        UnicodeString.FormatRun r = _string.getFormatRun(index);
+        FormatRun r = _string.getFormatRun(index);
         return r.getCharacterPos();
     }
 
@@ -281,7 +279,7 @@ public final class HSSFRichTextString im
      * @return  the font number used.
      */
     public short getFontOfFormattingRun(int index) {
-      UnicodeString.FormatRun r = _string.getFormatRun(index);
+      FormatRun r = _string.getFormatRun(index);
       return r.getFontIndex();
     }
 
@@ -307,7 +305,7 @@ public final class HSSFRichTextString im
         return 42; // any arbitrary constant will do
     }
 
-    
+
     /**
      * @return  the plain text representation of this string.
      */

Modified: poi/trunk/src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java Sun Dec 22 21:44:45 2019
@@ -53,6 +53,7 @@ public final class CellRangeAddress8Bit
 		out.writeByte(getLastColumn());
 	}
 
+	@Override
 	public CellRangeAddress8Bit copy() {
 		return new CellRangeAddress8Bit(getFirstRow(), getLastRow(), getFirstColumn(), getLastColumn());
 	}

Modified: poi/trunk/src/java/org/apache/poi/hssf/util/LazilyConcatenatedByteArray.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/util/LazilyConcatenatedByteArray.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/util/LazilyConcatenatedByteArray.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/util/LazilyConcatenatedByteArray.java Sun Dec 22 21:44:45 2019
@@ -49,6 +49,10 @@ public class LazilyConcatenatedByteArray
         arrays.add(array);
     }
 
+    public void concatenate(LazilyConcatenatedByteArray other) {
+        arrays.addAll(other.arrays);
+    }
+
     /**
      * Gets the concatenated contents as a single byte array.
      *

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java Sun Dec 22 21:44:45 2019
@@ -24,7 +24,6 @@ import java.util.function.Supplier;
 
 import javax.crypto.Cipher;
 import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.common.usermodel.GenericRecord;
@@ -32,18 +31,26 @@ import org.apache.poi.poifs.filesystem.D
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.GenericRecordUtil;
 
-public abstract class Decryptor implements Cloneable, GenericRecord {
+public abstract class Decryptor implements GenericRecord {
     @SuppressWarnings({"squid:S2068"})
     public static final String DEFAULT_PASSWORD="VelvetSweatshop";
     public static final String DEFAULT_POIFS_ENTRY="EncryptedPackage";
-    
+
     protected EncryptionInfo encryptionInfo;
     private SecretKey secretKey;
     private byte[] verifier, integrityHmacKey, integrityHmacValue;
 
-    protected Decryptor() {
+    protected Decryptor() {}
+
+    protected Decryptor(Decryptor other) {
+        encryptionInfo = other.encryptionInfo;
+        // secretKey is immutable
+        secretKey = other.secretKey;
+        verifier = (other.verifier == null) ? null : other.verifier.clone();
+        integrityHmacKey = (other.integrityHmacKey == null) ? null : other.integrityHmacKey.clone();
+        integrityHmacValue = (other.integrityHmacValue == null) ? null : other.integrityHmacValue.clone();
     }
-    
+
     /**
      * Return a stream with decrypted data.
      * <p>
@@ -60,7 +67,7 @@ public abstract class Decryptor implemen
 
     /**
      * Wraps a stream for decryption<p>
-     * 
+     *
      * As we are handling streams and don't know the total length beforehand,
      * it's the callers duty to care for the length of the entries.
      *
@@ -96,7 +103,7 @@ public abstract class Decryptor implemen
     throws GeneralSecurityException {
         throw new EncryptedDocumentException("this decryptor doesn't support initCipherForBlock");
     }
-    
+
     public abstract boolean verifyPassword(String password)
         throws GeneralSecurityException;
 
@@ -137,7 +144,7 @@ public abstract class Decryptor implemen
     public SecretKey getSecretKey() {
         return secretKey;
     }
-    
+
     public byte[] getIntegrityHmacKey() {
         return integrityHmacKey;
     }
@@ -167,11 +174,11 @@ public abstract class Decryptor implemen
     protected int getBlockSizeInBytes() {
         return encryptionInfo.getHeader().getBlockSize();
     }
-    
+
     protected int getKeySizeInBytes() {
         return encryptionInfo.getHeader().getKeySize()/8;
     }
-    
+
     public EncryptionInfo getEncryptionInfo() {
         return encryptionInfo;
     }
@@ -180,16 +187,7 @@ public abstract class Decryptor implemen
         this.encryptionInfo = encryptionInfo;
     }
 
-    @Override
-    public Decryptor clone() throws CloneNotSupportedException {
-        Decryptor other = (Decryptor)super.clone();
-        other.integrityHmacKey = integrityHmacKey.clone();
-        other.integrityHmacValue = integrityHmacValue.clone();
-        other.verifier = verifier.clone();
-        other.secretKey = new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm());
-        // encryptionInfo is set from outside
-        return other;
-    }
+    public abstract Decryptor copy();
 
     @Override
     public Map<String, Supplier<?>> getGenericProperties() {

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java Sun Dec 22 21:44:45 2019
@@ -22,18 +22,19 @@ import java.util.Map;
 import java.util.function.Supplier;
 
 import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.common.Duplicatable;
 import org.apache.poi.common.usermodel.GenericRecord;
 
 /**
  * Reads and processes OOXML Encryption Headers
  * The constants are largely based on ZIP constants.
  */
-public abstract class EncryptionHeader implements Cloneable, GenericRecord {
+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;
@@ -46,7 +47,7 @@ public abstract class EncryptionHeader i
     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;
@@ -57,13 +58,26 @@ public abstract class EncryptionHeader i
     private ChainingMode chainingMode;
     private byte[] keySalt;
     private String cspName;
-    
+
     protected EncryptionHeader() {}
 
+    protected EncryptionHeader(EncryptionHeader other) {
+        flags = other.flags;
+        sizeExtra = other.sizeExtra;
+        cipherAlgorithm = other.cipherAlgorithm;
+        hashAlgorithm = other.hashAlgorithm;
+        keyBits = other.keyBits;
+        blockSize = other.blockSize;
+        providerType = other.providerType;
+        chainingMode = other.chainingMode;
+        keySalt = (other.keySalt == null) ? null : other.keySalt.clone();
+        cspName = other.cspName;
+    }
+
     public ChainingMode getChainingMode() {
         return chainingMode;
     }
-    
+
     protected void setChainingMode(ChainingMode chainingMode) {
         this.chainingMode = chainingMode;
     }
@@ -71,7 +85,7 @@ public abstract class EncryptionHeader i
     public int getFlags() {
         return flags;
     }
-    
+
     protected void setFlags(int flags) {
         this.flags = flags;
     }
@@ -79,7 +93,7 @@ public abstract class EncryptionHeader i
     public int getSizeExtra() {
         return sizeExtra;
     }
-    
+
     protected void setSizeExtra(int sizeExtra) {
         this.sizeExtra = sizeExtra;
     }
@@ -87,7 +101,7 @@ public abstract class EncryptionHeader i
     public CipherAlgorithm getCipherAlgorithm() {
         return cipherAlgorithm;
     }
-    
+
     protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
         this.cipherAlgorithm = cipherAlgorithm;
         if (cipherAlgorithm.allowedKeySize.length == 1) {
@@ -106,7 +120,7 @@ public abstract class EncryptionHeader i
     public int getKeySize() {
         return keyBits;
     }
-    
+
     /**
      * Sets the keySize (in bits). Before calling this method, make sure
      * to set the cipherAlgorithm, as the amount of keyBits gets validated against
@@ -127,41 +141,36 @@ public abstract class EncryptionHeader i
     public int getBlockSize() {
     	return blockSize;
     }
-    
+
     protected void setBlockSize(int blockSize) {
         this.blockSize = blockSize;
     }
-    
+
     public byte[] getKeySalt() {
         return keySalt;
     }
-    
+
     protected void setKeySalt(byte[] salt) {
         this.keySalt = (salt == null) ? null : salt.clone();
     }
 
     public CipherProvider getCipherProvider() {
         return providerType;
-    }    
+    }
 
     protected void setCipherProvider(CipherProvider providerType) {
         this.providerType = providerType;
     }
-    
+
     public String getCspName() {
         return cspName;
     }
-    
+
     protected void setCspName(String cspName) {
         this.cspName = cspName;
     }
 
-    @Override
-    public EncryptionHeader clone() throws CloneNotSupportedException {
-        EncryptionHeader other = (EncryptionHeader)super.clone();
-        other.keySalt = (keySalt == null) ? null : keySalt.clone();
-        return other;
-    }
+    public abstract EncryptionHeader copy();
 
     @Override
     public Map<String, Supplier<?>> getGenericProperties() {

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java Sun Dec 22 21:44:45 2019
@@ -42,17 +42,7 @@ import org.apache.poi.util.LittleEndianI
  * some {@link EncryptionMode}s.
  * @see #getBuilder(EncryptionMode)
  */
-public class EncryptionInfo implements Cloneable, GenericRecord {
-    private final EncryptionMode encryptionMode;
-    private final int versionMajor;
-    private final int versionMinor;
-    private final int encryptionFlags;
-    
-    private EncryptionHeader header;
-    private EncryptionVerifier verifier;
-    private Decryptor decryptor;
-    private Encryptor encryptor;
-
+public class EncryptionInfo implements GenericRecord {
     /**
      * A flag that specifies whether CryptoAPI RC4 or ECMA-376 encryption
      * ECMA-376 is used. It MUST be 1 unless flagExternal is 1. If flagExternal is 1, it MUST be 0.
@@ -65,14 +55,14 @@ public class EncryptionInfo implements C
      */
     @SuppressWarnings("WeakerAccess")
     public static final BitField flagDocProps = BitFieldFactory.getInstance(0x08);
-    
+
     /**
      * A value that MUST be 1 if extensible encryption is used. If this value is 1,
      * the value of every other field in this structure MUST be 0.
      */
     @SuppressWarnings("WeakerAccess")
     public static final BitField flagExternal = BitFieldFactory.getInstance(0x10);
-    
+
     /**
      * A value that MUST be 1 if the protected content is an ECMA-376 document
      * ECMA-376. If the fAES bit is 1, the fCryptoAPI bit MUST also be 1.
@@ -87,13 +77,23 @@ public class EncryptionInfo implements C
         "CRYPTO_API", "DOC_PROPS", "EXTERNAL", "AES"
     };
 
+    private final EncryptionMode encryptionMode;
+    private final int versionMajor;
+    private final int versionMinor;
+    private final int encryptionFlags;
+
+    private EncryptionHeader header;
+    private EncryptionVerifier verifier;
+    private Decryptor decryptor;
+    private Encryptor encryptor;
+
     /**
      * Opens for decryption
      */
     public EncryptionInfo(POIFSFileSystem fs) throws IOException {
        this(fs.getRoot());
     }
-    
+
     /**
      * Opens for decryption
      */
@@ -141,7 +141,7 @@ public class EncryptionInfo implements C
                 " / fDocProps: "+flagDocProps.isSet(encryptionFlags)+
                 " / fAES: "+flagAES.isSet(encryptionFlags));
         }
-        
+
         EncryptionInfoBuilder eib;
         try {
             eib = getBuilder(encryptionMode);
@@ -151,7 +151,7 @@ public class EncryptionInfo implements C
 
         eib.initialize(this, dis);
     }
-    
+
     /**
      * Prepares for encryption, using the given Encryption Mode, and
      *  all other parameters as default.
@@ -160,7 +160,7 @@ public class EncryptionInfo implements C
     public EncryptionInfo(EncryptionMode encryptionMode) {
         this(encryptionMode, null, null, -1, -1, null);
     }
-    
+
     /**
      * Constructs an EncryptionInfo from scratch
      *
@@ -171,7 +171,7 @@ public class EncryptionInfo implements C
      * @param keyBits the bit count of the key
      * @param blockSize the size of a cipher block
      * @param chainingMode the chaining mode
-     * 
+     *
      * @throws EncryptedDocumentException if the given parameters mismatch, e.g. only certain combinations
      *   of keyBits, blockSize are allowed for a given {@link CipherAlgorithm}
      */
@@ -183,7 +183,7 @@ public class EncryptionInfo implements C
           , int blockSize
           , ChainingMode chainingMode
       ) {
-        this.encryptionMode = encryptionMode; 
+        this.encryptionMode = encryptionMode;
         versionMajor = encryptionMode.versionMajor;
         versionMinor = encryptionMode.versionMinor;
         encryptionFlags = encryptionMode.encryptionFlags;
@@ -194,10 +194,28 @@ public class EncryptionInfo implements C
         } catch (Exception e) {
             throw new EncryptedDocumentException(e);
         }
-        
+
         eib.initialize(this, cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
     }
 
+    public EncryptionInfo(EncryptionInfo other) {
+        encryptionMode = other.encryptionMode;
+        versionMajor = other.versionMajor;
+        versionMinor = other.versionMinor;
+        encryptionFlags = other.encryptionFlags;
+
+        header = (other.header == null) ? null : other.header.copy();
+        verifier = (other.verifier == null) ? null : other.verifier.copy();
+        if (other.decryptor != null) {
+            decryptor = other.decryptor.copy();
+            decryptor.setEncryptionInfo(this);
+        }
+        if (other.encryptor != null) {
+            encryptor = other.encryptor.copy();
+            encryptor.setEncryptionInfo(this);
+        }
+    }
+
     /**
      * This method loads the builder class with reflection, which may generate
      * a {@code ClassNotFoundException} if the class is not on the classpath.
@@ -222,7 +240,7 @@ public class EncryptionInfo implements C
         eib = (EncryptionInfoBuilder)cl.loadClass(encryptionMode.builder).newInstance();
         return eib;
     }
-    
+
     public int getVersionMajor() {
         return versionMajor;
     }
@@ -242,7 +260,7 @@ public class EncryptionInfo implements C
     public EncryptionVerifier getVerifier() {
         return verifier;
     }
-    
+
     public Decryptor getDecryptor() {
         return decryptor;
     }
@@ -270,7 +288,7 @@ public class EncryptionInfo implements C
     public EncryptionMode getEncryptionMode() {
         return encryptionMode;
     }
-    
+
     /**
      * @return true, if Document Summary / Summary are encrypted and stored in the {@code EncryptedStream} stream,
      * otherwise the Summaries aren't encrypted and located in their usual streams
@@ -278,17 +296,9 @@ public class EncryptionInfo implements C
     public boolean isDocPropsEncrypted() {
         return !flagDocProps.isSet(getEncryptionFlags());
     }
-    
-    @Override
-    public EncryptionInfo clone() throws CloneNotSupportedException {
-        EncryptionInfo other = (EncryptionInfo)super.clone();
-        other.header = header.clone();
-        other.verifier = verifier.clone();
-        other.decryptor = decryptor.clone();
-        other.decryptor.setEncryptionInfo(other);
-        other.encryptor = encryptor.clone();
-        other.encryptor.setEncryptionInfo(other);
-        return other;
+
+    public EncryptionInfo copy()  {
+        return new EncryptionInfo(this);
     }
 
     @Override

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java Sun Dec 22 21:44:45 2019
@@ -21,12 +21,13 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.function.Supplier;
 
+import org.apache.poi.common.Duplicatable;
 import org.apache.poi.common.usermodel.GenericRecord;
 
 /**
- * Used when checking if a key is valid for a document 
+ * Used when checking if a key is valid for a document
  */
-public abstract class EncryptionVerifier implements Cloneable, GenericRecord {
+public abstract class EncryptionVerifier implements GenericRecord, Duplicatable {
     private byte[] salt;
     private byte[] encryptedVerifier;
     private byte[] encryptedVerifierHash;
@@ -36,21 +37,32 @@ public abstract class EncryptionVerifier
     private CipherAlgorithm cipherAlgorithm;
     private ChainingMode chainingMode;
     private HashAlgorithm hashAlgorithm;
-    
+
     protected EncryptionVerifier() {}
 
+    protected EncryptionVerifier(EncryptionVerifier other) {
+        salt = (other.salt == null) ? null : other.salt.clone();
+        encryptedVerifier = (other.encryptedVerifier == null) ? null : other.encryptedVerifier.clone();
+        encryptedVerifierHash = (other.encryptedVerifierHash == null) ? null : other.encryptedVerifierHash.clone();
+        encryptedKey = (other.encryptedKey == null) ? null : other.encryptedKey.clone();
+        spinCount = other.spinCount;
+        cipherAlgorithm = other.cipherAlgorithm;
+        chainingMode = other.chainingMode;
+        hashAlgorithm = other.hashAlgorithm;
+    }
+
     public byte[] getSalt() {
         return salt;
     }
 
     public byte[] getEncryptedVerifier() {
         return encryptedVerifier;
-    }    
-    
+    }
+
     public byte[] getEncryptedVerifierHash() {
         return encryptedVerifierHash;
-    }    
-    
+    }
+
     public int getSpinCount() {
         return spinCount;
     }
@@ -58,15 +70,15 @@ public abstract class EncryptionVerifier
     public byte[] getEncryptedKey() {
         return encryptedKey;
     }
-    
+
     public CipherAlgorithm getCipherAlgorithm() {
         return cipherAlgorithm;
     }
-    
+
     public HashAlgorithm getHashAlgorithm() {
         return hashAlgorithm;
     }
-    
+
     public ChainingMode getChainingMode() {
         return chainingMode;
     }
@@ -102,16 +114,8 @@ public abstract class EncryptionVerifier
     protected void setHashAlgorithm(HashAlgorithm hashAlgorithm) {
         this.hashAlgorithm = hashAlgorithm;
     }
-    
-    @Override
-    public EncryptionVerifier clone() throws CloneNotSupportedException {
-        EncryptionVerifier other = (EncryptionVerifier)super.clone();
-        other.salt = (salt == null) ? null : salt.clone();
-        other.encryptedVerifier = (encryptedVerifier == null) ? null : encryptedVerifier.clone();
-        other.encryptedVerifierHash = (encryptedVerifierHash == null) ? null : encryptedVerifierHash.clone();
-        other.encryptedKey = (encryptedKey == null) ? null : encryptedKey.clone();
-        return other;
-    }
+
+    public abstract EncryptionVerifier copy();
 
     @Override
     public Map<String, Supplier<?>> getGenericProperties() {

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java Sun Dec 22 21:44:45 2019
@@ -23,7 +23,6 @@ import java.util.Map;
 import java.util.function.Supplier;
 
 import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.common.usermodel.GenericRecord;
@@ -31,11 +30,19 @@ import org.apache.poi.poifs.filesystem.D
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.GenericRecordUtil;
 
-public abstract class Encryptor implements Cloneable, GenericRecord {
+public abstract class Encryptor implements GenericRecord {
     protected static final String DEFAULT_POIFS_ENTRY = Decryptor.DEFAULT_POIFS_ENTRY;
     private EncryptionInfo encryptionInfo;
     private SecretKey secretKey;
-    
+
+    protected Encryptor() {}
+
+    protected Encryptor(Encryptor other) {
+        encryptionInfo = other.encryptionInfo;
+        // secretKey is immutable
+        secretKey = other.secretKey;
+    }
+
     /**
      * Return a output stream for encrypted data.
      *
@@ -47,9 +54,9 @@ public abstract class Encryptor implemen
 
     // for tests
     public abstract void confirmPassword(String password, byte[] keySpec, byte[] keySalt, byte[] verifier, byte[] verifierSalt, byte[] integritySalt);
-    
+
     public abstract void confirmPassword(String password);
-	
+
 	public static Encryptor getInstance(EncryptionInfo info) {
 	    return info.getEncryptor();
     }
@@ -62,7 +69,7 @@ public abstract class Encryptor implemen
     throws IOException, GeneralSecurityException {
         throw new EncryptedDocumentException("this decryptor doesn't support writing directly to a stream");
     }
-    
+
     public SecretKey getSecretKey() {
         return secretKey;
     }
@@ -89,14 +96,8 @@ public abstract class Encryptor implemen
     public void setChunkSize(int chunkSize) {
         throw new EncryptedDocumentException("this decryptor doesn't support changing the chunk size");
     }
-    
-    @Override
-    public Encryptor clone() throws CloneNotSupportedException {
-        Encryptor other = (Encryptor)super.clone();
-        other.secretKey = new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm());
-        // encryptionInfo is set from outside
-        return other;
-    }
+
+    public abstract Encryptor copy();
 
     @Override
     public Map<String, Supplier<?>> getGenericProperties() {

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java Sun Dec 22 21:44:45 2019
@@ -28,16 +28,22 @@ import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.*;
+import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.Decryptor;
+import org.apache.poi.poifs.crypt.EncryptionHeader;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionVerifier;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
 
-public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
+public class BinaryRC4Decryptor extends Decryptor {
     private long length = -1L;
     private int chunkSize = 512;
-    
+
     private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream {
 
         @Override
@@ -54,12 +60,18 @@ public class BinaryRC4Decryptor extends
         public BinaryRC4CipherInputStream(InputStream stream, int size, int initialPos)
                 throws GeneralSecurityException {
             super(stream, size, chunkSize, initialPos);
-        }    
+        }
     }
 
     protected BinaryRC4Decryptor() {
     }
 
+    protected BinaryRC4Decryptor(BinaryRC4Decryptor other) {
+        super(other);
+        length = other.length;
+        chunkSize = other.chunkSize;
+    }
+
     @Override
     public boolean verifyPassword(String password) {
         EncryptionVerifier ver = getEncryptionInfo().getVerifier();
@@ -89,8 +101,8 @@ public class BinaryRC4Decryptor extends
     public Cipher initCipherForBlock(Cipher cipher, int block)
     throws GeneralSecurityException {
         return initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.DECRYPT_MODE);
-    }    
-    
+    }
+
     protected static Cipher initCipherForBlock(Cipher cipher, int block,
         EncryptionInfo encryptionInfo, SecretKey skey, int encryptMode)
     throws GeneralSecurityException {
@@ -136,20 +148,20 @@ public class BinaryRC4Decryptor extends
         length = dis.readLong();
         return new BinaryRC4CipherInputStream(dis, length);
     }
-    
+
     @Override
     public InputStream getDataStream(InputStream stream, int size, int initialPos)
             throws IOException, GeneralSecurityException {
         return new BinaryRC4CipherInputStream(stream, size, initialPos);
     }
-    
+
 
     @Override
     public long getLength() {
         if (length == -1L) {
             throw new IllegalStateException("Decryptor.getDataStream() was not called");
         }
-        
+
         return length;
     }
 
@@ -157,9 +169,9 @@ public class BinaryRC4Decryptor extends
     public void setChunkSize(int chunkSize) {
         this.chunkSize = chunkSize;
     }
-    
+
     @Override
-    public BinaryRC4Decryptor clone() throws CloneNotSupportedException {
-        return (BinaryRC4Decryptor)super.clone();
+    public BinaryRC4Decryptor copy() {
+        return new BinaryRC4Decryptor(this);
     }
 }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java Sun Dec 22 21:44:45 2019
@@ -24,7 +24,7 @@ import org.apache.poi.poifs.crypt.HashAl
 import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 
-public class BinaryRC4EncryptionHeader extends EncryptionHeader implements EncryptionRecord, Cloneable {
+public class BinaryRC4EncryptionHeader extends EncryptionHeader implements EncryptionRecord {
 
     protected BinaryRC4EncryptionHeader() {
         setCipherAlgorithm(CipherAlgorithm.rc4);
@@ -38,14 +38,16 @@ public class BinaryRC4EncryptionHeader e
         setChainingMode(null);
     }
 
+    protected BinaryRC4EncryptionHeader(BinaryRC4EncryptionHeader other) {
+        super(other);
+    }
+
     @Override
     public void write(LittleEndianByteArrayOutputStream littleendianbytearrayoutputstream) {
     }
 
     @Override
-    public BinaryRC4EncryptionHeader clone() throws CloneNotSupportedException {
-        return (BinaryRC4EncryptionHeader)super.clone();
+    public BinaryRC4EncryptionHeader copy() {
+        return new BinaryRC4EncryptionHeader(this);
     }
-    
-    
 }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java Sun Dec 22 21:44:45 2019
@@ -18,12 +18,14 @@
 package org.apache.poi.poifs.crypt.binaryrc4;
 
 import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.*;
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.EncryptionVerifier;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
 import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 import org.apache.poi.util.LittleEndianInput;
 
-public class BinaryRC4EncryptionVerifier extends EncryptionVerifier implements EncryptionRecord, Cloneable {
+public class BinaryRC4EncryptionVerifier extends EncryptionVerifier implements EncryptionRecord {
 
     protected BinaryRC4EncryptionVerifier() {
         setSpinCount(-1);
@@ -50,12 +52,16 @@ public class BinaryRC4EncryptionVerifier
         setHashAlgorithm(HashAlgorithm.md5);
     }
 
+    protected BinaryRC4EncryptionVerifier(BinaryRC4EncryptionVerifier other) {
+        super(other);
+    }
+
     @Override
     protected void setSalt(byte[] salt) {
         if (salt == null || salt.length != 16) {
             throw new EncryptedDocumentException("invalid verifier salt");
         }
-        
+
         super.setSalt(salt);
     }
 
@@ -83,7 +89,7 @@ public class BinaryRC4EncryptionVerifier
     }
 
     @Override
-    public BinaryRC4EncryptionVerifier clone() throws CloneNotSupportedException {
-        return (BinaryRC4EncryptionVerifier)super.clone();
+    public BinaryRC4EncryptionVerifier copy() {
+        return new BinaryRC4EncryptionVerifier(this);
     }
 }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java Sun Dec 22 21:44:45 2019
@@ -39,11 +39,15 @@ import org.apache.poi.poifs.crypt.standa
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 
-public class BinaryRC4Encryptor extends Encryptor implements Cloneable {
+public class BinaryRC4Encryptor extends Encryptor {
 
     private int chunkSize = 512;
-    
-    protected BinaryRC4Encryptor() {
+
+    protected BinaryRC4Encryptor() {}
+
+    protected BinaryRC4Encryptor(BinaryRC4Encryptor other) {
+        super(other);
+        chunkSize = other.chunkSize;
     }
 
     @Override
@@ -90,7 +94,7 @@ public class BinaryRC4Encryptor extends
     throws IOException, GeneralSecurityException {
         return new BinaryRC4CipherOutputStream(stream);
     }
-    
+
     protected int getKeySizeInBytes() {
         return getEncryptionInfo().getHeader().getKeySize() / 8;
     }
@@ -116,10 +120,10 @@ public class BinaryRC4Encryptor extends
     public void setChunkSize(int chunkSize) {
         this.chunkSize = chunkSize;
     }
-    
+
     @Override
-    public BinaryRC4Encryptor clone() throws CloneNotSupportedException {
-        return (BinaryRC4Encryptor)super.clone();
+    public BinaryRC4Encryptor copy() {
+        return new BinaryRC4Encryptor(this);
     }
 
     protected class BinaryRC4CipherOutputStream extends ChunkedCipherOutputStream {
@@ -139,7 +143,7 @@ public class BinaryRC4Encryptor extends
         throws GeneralSecurityException {
             return BinaryRC4Decryptor.initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.ENCRYPT_MODE);
         }
-        
+
         @Override
         protected void calculateChecksum(File file, int i) {
         }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java Sun Dec 22 21:44:45 2019
@@ -49,13 +49,13 @@ import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianInputStream;
 import org.apache.poi.util.StringUtil;
 
-public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
+public class CryptoAPIDecryptor extends Decryptor {
 
     private long length = -1L;
     private int chunkSize = -1;
 
     static class StreamDescriptorEntry {
-        static BitField flagStream = BitFieldFactory.getInstance(1);
+        static final BitField flagStream = BitFieldFactory.getInstance(1);
 
         int streamOffset;
         int streamSize;
@@ -65,7 +65,12 @@ public class CryptoAPIDecryptor extends
         String streamName;
     }
 
-    protected CryptoAPIDecryptor() {
+    protected CryptoAPIDecryptor() {}
+
+    protected CryptoAPIDecryptor(CryptoAPIDecryptor other) {
+        super(other);
+        length = other.length;
+        chunkSize = other.chunkSize;
     }
 
     @Override
@@ -153,13 +158,13 @@ public class CryptoAPIDecryptor extends
      * Decrypt the Document-/SummaryInformation and other optionally streams.
      * Opposed to other crypto modes, cryptoapi is record based and can't be used
      * to stream-decrypt a whole file.<p>
-     * 
+     *
      * Summary entries are only encrypted within cryptoapi encrypted files.
      * Binary RC4 encrypted files use non-encrypted/default property sets
-     * 
+     *
      * @param root root directory node of the OLE file containing the encrypted properties
      * @param encryptedStream name of the encrypted stream -
-     *      "encryption" for HSSF/HWPF, "encryptedStream" (or encryptedSummary?) for HSLF 
+     *      "encryption" for HSSF/HWPF, "encryptedStream" (or encryptedSummary?) for HSLF
      *
      * @see <a href="http://msdn.microsoft.com/en-us/library/dd943321(v=office.12).aspx">2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream</a>
      */
@@ -239,8 +244,8 @@ public class CryptoAPIDecryptor extends
     }
 
     @Override
-    public CryptoAPIDecryptor clone() throws CloneNotSupportedException {
-        return (CryptoAPIDecryptor)super.clone();
+    public CryptoAPIDecryptor copy() {
+        return new CryptoAPIDecryptor(this);
     }
 
     private class CryptoAPICipherInputStream extends ChunkedCipherInputStream {

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java Sun Dec 22 21:44:45 2019
@@ -27,12 +27,16 @@ import org.apache.poi.poifs.crypt.HashAl
 import org.apache.poi.poifs.crypt.standard.StandardEncryptionHeader;
 import org.apache.poi.util.LittleEndianInput;
 
-public class CryptoAPIEncryptionHeader extends StandardEncryptionHeader implements Cloneable {
+public class CryptoAPIEncryptionHeader extends StandardEncryptionHeader {
 
     public CryptoAPIEncryptionHeader(LittleEndianInput is) throws IOException {
         super(is);
     }
 
+    protected CryptoAPIEncryptionHeader(CryptoAPIEncryptionHeader other) {
+        super(other);
+    }
+
     protected CryptoAPIEncryptionHeader(CipherAlgorithm cipherAlgorithm,
             HashAlgorithm hashAlgorithm, int keyBits, int blockSize,
             ChainingMode chainingMode) {
@@ -62,7 +66,7 @@ public class CryptoAPIEncryptionHeader e
     }
 
     @Override
-    public CryptoAPIEncryptionHeader clone() throws CloneNotSupportedException {
-        return (CryptoAPIEncryptionHeader)super.clone();
+    public CryptoAPIEncryptionHeader copy() {
+        return new CryptoAPIEncryptionHeader(this);
     }
 }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java?rev=1871911&r1=1871910&r2=1871911&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java Sun Dec 22 21:44:45 2019
@@ -23,7 +23,7 @@ import org.apache.poi.poifs.crypt.HashAl
 import org.apache.poi.poifs.crypt.standard.StandardEncryptionVerifier;
 import org.apache.poi.util.LittleEndianInput;
 
-public class CryptoAPIEncryptionVerifier extends StandardEncryptionVerifier implements Cloneable {
+public class CryptoAPIEncryptionVerifier extends StandardEncryptionVerifier {
 
     protected CryptoAPIEncryptionVerifier(LittleEndianInput is,
             CryptoAPIEncryptionHeader header) {
@@ -36,6 +36,10 @@ public class CryptoAPIEncryptionVerifier
         super(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
     }
 
+    protected CryptoAPIEncryptionVerifier(CryptoAPIEncryptionVerifier other) {
+        super(other);
+    }
+
     @Override
     protected void setSalt(byte[] salt) {
         super.setSalt(salt);
@@ -52,7 +56,7 @@ public class CryptoAPIEncryptionVerifier
     }
 
     @Override
-    public CryptoAPIEncryptionVerifier clone() throws CloneNotSupportedException {
-        return (CryptoAPIEncryptionVerifier)super.clone();
+    public CryptoAPIEncryptionVerifier copy() {
+        return new CryptoAPIEncryptionVerifier(this);
     }
 }



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