zhao
2021-07-19 8347f2fbddbd25369359dcb2da1233ac48a19fdc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
 
/* ====================================================================
   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.
==================================================================== */
 
namespace HH.WMS.Utils.NPOI.DDF
{
    using System;
    using System.Text;
    using System.Collections;
    using HH.WMS.Utils.NPOI.Util;
 
    /// <summary>
    /// Escher array properties are the most wierd construction ever invented
    /// with all sorts of special cases.  I'm hopeful I've got them all.
    /// @author Glen Stampoultzis (glens at superlinksoftware.com)
    /// </summary>
    public class EscherArrayProperty : EscherComplexProperty
    {
        /**
         * The size of the header that goes at the
         *  start of the array, before the data
         */
        private const int FIXED_SIZE = 3 * 2;
        /**
         * Normally, the size recorded in the simple data (for the complex
         *  data) includes the size of the header.
         * There are a few cases when it doesn't though...
         */
        private bool sizeIncludesHeaderSize = true;
 
        /**
         * When Reading a property from data stream remeber if the complex part is empty and Set this flag.
         */
        private bool emptyComplexPart = false;
 
        public EscherArrayProperty(short id, byte[] complexData)
            : base(id, CheckComplexData(complexData))
        {
 
            emptyComplexPart = complexData.Length == 0;
        }
 
        public EscherArrayProperty(short propertyNumber, bool isBlipId, byte[] complexData)
            : base(propertyNumber, isBlipId, CheckComplexData(complexData))
        {
 
        }
 
        private static byte[] CheckComplexData(byte[] complexData)
        {
            if (complexData == null || complexData.Length == 0)
                complexData = new byte[6];
 
            return complexData;
        }
 
        public int NumberOfElementsInArray
        {
            get
            {
                return LittleEndian.GetUShort(complexData, 0);
            }
            set 
            {
                int expectedArraySize = value * GetActualSizeOfElements(SizeOfElements) + FIXED_SIZE;
                if (expectedArraySize != complexData.Length)
                {
                    byte[] newArray = new byte[expectedArraySize];
                    Array.Copy(complexData, 0, newArray, 0, complexData.Length);
                    complexData = newArray;
                }
                LittleEndian.PutShort(complexData, 0, (short)value);           
            }
        }
        public int NumberOfElementsInMemory
        {
            get
            {
                return LittleEndian.GetUShort(complexData, 2);
            }
            set 
            {
                int expectedArraySize = value * GetActualSizeOfElements(this.SizeOfElements) + FIXED_SIZE;
                if (expectedArraySize != complexData.Length)
                {
                    byte[] newArray = new byte[expectedArraySize];
                    Array.Copy(complexData, 0, newArray, 0, expectedArraySize);
                    complexData = newArray;
                }
                LittleEndian.PutShort(complexData, 2, (short)value);           
            }
        }
 
        public short SizeOfElements
        {
            get
            {
                return LittleEndian.GetShort(complexData, 4);
            }
            set 
            {
                LittleEndian.PutShort(complexData, 4, (short)value);
 
                int expectedArraySize = NumberOfElementsInArray * GetActualSizeOfElements(SizeOfElements) + FIXED_SIZE;
                if (expectedArraySize != complexData.Length)
                {
                    // Keep just the first 6 bytes.  The rest is no good to us anyway.
                    byte[] newArray = new byte[expectedArraySize];
                    Array.Copy(complexData, 0, newArray, 0, 6);
                    complexData = newArray;
                }
            }
        }
 
        /// <summary>
        /// Gets the element.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        public byte[] GetElement(int index)
        {
            int actualSize = GetActualSizeOfElements(SizeOfElements);
            byte[] result = new byte[actualSize];
            Array.Copy(complexData, FIXED_SIZE + index * actualSize, result, 0, result.Length);
            return result;
        }
 
        /// <summary>
        /// Sets the element.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="element">The element.</param>
        public void SetElement(int index, byte[] element)
        {
            int actualSize = GetActualSizeOfElements(SizeOfElements);
            Array.Copy(element, 0, complexData, FIXED_SIZE + index * actualSize, actualSize);
        }
 
        /// <summary>
        /// Retrieves the string representation for this property.
        /// </summary>
        /// <returns></returns>
        public override String ToString()
        {
            String nl = Environment.NewLine;
 
            StringBuilder results = new StringBuilder();
            results.Append("    {EscherArrayProperty:" + nl);
            results.Append("     Num Elements: " + NumberOfElementsInArray + nl);
            results.Append("     Num Elements In Memory: " + NumberOfElementsInMemory + nl);
            results.Append("     Size of elements: " + SizeOfElements + nl);
            for (int i = 0; i < NumberOfElementsInArray; i++)
            {
                results.Append("     Element " + i + ": " + HexDump.ToHex(GetElement(i)) + nl);
            }
            results.Append("}" + nl);
 
            return "propNum: " + PropertyNumber
                    + ", propName: " + EscherProperties.GetPropertyName(PropertyNumber)
                    + ", complex: " + IsComplex
                    + ", blipId: " + IsBlipId
                    + ", data: " + nl + results.ToString();
        }
 
        /// <summary>
        /// We have this method because the way in which arrays in escher works
        /// is screwed for seemly arbitary reasons.  While most properties are
        /// fairly consistent and have a predictable array size, escher arrays
        /// have special cases.
        /// </summary>
        /// <param name="data">The data array containing the escher array information</param>
        /// <param name="offset">The offset into the array to start Reading from.</param>
        /// <returns>the number of bytes used by this complex property.</returns>
        public int SetArrayData(byte[] data, int offset)
        {
            if (emptyComplexPart)
            {
                complexData = new byte[0];
            }
            else
            {
                short numElements = LittleEndian.GetShort(data, offset);
                short numReserved = LittleEndian.GetShort(data, offset + 2);
                short sizeOfElements = LittleEndian.GetShort(data, offset + 4);
 
                int arraySize = GetActualSizeOfElements(sizeOfElements) * numElements;
                if (arraySize == complexData.Length)
                {
                    // The stored data size in the simple block excludes the header size
                    complexData = new byte[arraySize + 6];
                    sizeIncludesHeaderSize = false;
                }
                Array.Copy(data, offset, complexData, 0, complexData.Length);
            }
            return complexData.Length;
        }
 
        /// <summary>
        /// Serializes the simple part of this property.  ie the first 6 bytes.
        /// Needs special code to handle the case when the size doesn't
        /// include the size of the header block
        /// </summary>
        /// <param name="data"></param>
        /// <param name="pos"></param>
        /// <returns></returns>
        public override int SerializeSimplePart(byte[] data, int pos)
        {
            LittleEndian.PutShort(data, pos, Id);
            int recordSize = complexData.Length;
            if (!sizeIncludesHeaderSize)
            {
                recordSize -= 6;
            }
            LittleEndian.PutInt(data, pos + 2, recordSize);
            return 6;
        }
 
        /// <summary>
        /// Sometimes the element size is stored as a negative number.  We
        /// negate it and shift it to Get the real value.
        /// </summary>
        /// <param name="sizeOfElements">The size of elements.</param>
        /// <returns></returns>
        public static int GetActualSizeOfElements(short sizeOfElements)
        {
            if (sizeOfElements < 0)
                return (short)((-sizeOfElements) >> 2);
            else
                return sizeOfElements;
        }
 
    }
}