001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.ddf;
019:
020: import org.apache.poi.util.LittleEndian;
021: import org.apache.poi.util.HexDump;
022:
023: /**
024: * Escher array properties are the most wierd construction ever invented
025: * with all sorts of special cases. I'm hopeful I've got them all.
026: *
027: * @author Glen Stampoultzis (glens at superlinksoftware.com)
028: */
029: public class EscherArrayProperty extends EscherComplexProperty {
030: /**
031: * The size of the header that goes at the
032: * start of the array, before the data
033: */
034: private static final int FIXED_SIZE = 3 * 2;
035: /**
036: * Normally, the size recorded in the simple data (for the complex
037: * data) includes the size of the header.
038: * There are a few cases when it doesn't though...
039: */
040: private boolean sizeIncludesHeaderSize = true;
041:
042: /**
043: * When reading a property from data stream remeber if the complex part is empty and set this flag.
044: */
045: private boolean emptyComplexPart = false;
046:
047: public EscherArrayProperty(short id, byte[] complexData) {
048: super (id, checkComplexData(complexData));
049: emptyComplexPart = complexData.length == 0;
050: }
051:
052: public EscherArrayProperty(short propertyNumber, boolean isBlipId,
053: byte[] complexData) {
054: super (propertyNumber, isBlipId, checkComplexData(complexData));
055: }
056:
057: private static byte[] checkComplexData(byte[] complexData) {
058: if (complexData == null || complexData.length == 0)
059: complexData = new byte[6];
060:
061: return complexData;
062: }
063:
064: public int getNumberOfElementsInArray() {
065: return LittleEndian.getUShort(complexData, 0);
066: }
067:
068: public void setNumberOfElementsInArray(int numberOfElements) {
069: int expectedArraySize = numberOfElements
070: * getActualSizeOfElements(getSizeOfElements())
071: + FIXED_SIZE;
072: if (expectedArraySize != complexData.length) {
073: byte[] newArray = new byte[expectedArraySize];
074: System.arraycopy(complexData, 0, newArray, 0,
075: complexData.length);
076: complexData = newArray;
077: }
078: LittleEndian.putShort(complexData, 0, (short) numberOfElements);
079: }
080:
081: public int getNumberOfElementsInMemory() {
082: return LittleEndian.getUShort(complexData, 2);
083: }
084:
085: public void setNumberOfElementsInMemory(int numberOfElements) {
086: int expectedArraySize = numberOfElements
087: * getActualSizeOfElements(getSizeOfElements())
088: + FIXED_SIZE;
089: if (expectedArraySize != complexData.length) {
090: byte[] newArray = new byte[expectedArraySize];
091: System.arraycopy(complexData, 0, newArray, 0,
092: expectedArraySize);
093: complexData = newArray;
094: }
095: LittleEndian.putShort(complexData, 2, (short) numberOfElements);
096: }
097:
098: public short getSizeOfElements() {
099: return LittleEndian.getShort(complexData, 4);
100: }
101:
102: public void setSizeOfElements(int sizeOfElements) {
103: LittleEndian.putShort(complexData, 4, (short) sizeOfElements);
104:
105: int expectedArraySize = getNumberOfElementsInArray()
106: * getActualSizeOfElements(getSizeOfElements())
107: + FIXED_SIZE;
108: if (expectedArraySize != complexData.length) {
109: // Keep just the first 6 bytes. The rest is no good to us anyway.
110: byte[] newArray = new byte[expectedArraySize];
111: System.arraycopy(complexData, 0, newArray, 0, 6);
112: complexData = newArray;
113: }
114: }
115:
116: public byte[] getElement(int index) {
117: int actualSize = getActualSizeOfElements(getSizeOfElements());
118: byte[] result = new byte[actualSize];
119: System.arraycopy(complexData, FIXED_SIZE + index * actualSize,
120: result, 0, result.length);
121: return result;
122: }
123:
124: public void setElement(int index, byte[] element) {
125: int actualSize = getActualSizeOfElements(getSizeOfElements());
126: System.arraycopy(element, 0, complexData, FIXED_SIZE + index
127: * actualSize, actualSize);
128: }
129:
130: public String toString() {
131: String nl = System.getProperty("line.separator");
132:
133: StringBuffer results = new StringBuffer();
134: results.append(" {EscherArrayProperty:" + nl);
135: results.append(" Num Elements: "
136: + getNumberOfElementsInArray() + nl);
137: results.append(" Num Elements In Memory: "
138: + getNumberOfElementsInMemory() + nl);
139: results.append(" Size of elements: " + getSizeOfElements()
140: + nl);
141: for (int i = 0; i < getNumberOfElementsInArray(); i++) {
142: results.append(" Element " + i + ": "
143: + HexDump.toHex(getElement(i)) + nl);
144: }
145: results.append("}" + nl);
146:
147: return "propNum: " + getPropertyNumber() + ", propName: "
148: + EscherProperties.getPropertyName(getPropertyNumber())
149: + ", complex: " + isComplex() + ", blipId: "
150: + isBlipId() + ", data: " + nl + results.toString();
151: }
152:
153: /**
154: * We have this method because the way in which arrays in escher works
155: * is screwed for seemly arbitary reasons. While most properties are
156: * fairly consistent and have a predictable array size, escher arrays
157: * have special cases.
158: *
159: * @param data The data array containing the escher array information
160: * @param offset The offset into the array to start reading from.
161: * @return the number of bytes used by this complex property.
162: */
163: public int setArrayData(byte[] data, int offset) {
164: if (emptyComplexPart) {
165: complexData = new byte[0];
166: } else {
167: short numElements = LittleEndian.getShort(data, offset);
168: short numReserved = LittleEndian.getShort(data, offset + 2);
169: short sizeOfElements = LittleEndian.getShort(data,
170: offset + 4);
171:
172: int arraySize = getActualSizeOfElements(sizeOfElements)
173: * numElements;
174: if (arraySize == complexData.length) {
175: // The stored data size in the simple block excludes the header size
176: complexData = new byte[arraySize + 6];
177: sizeIncludesHeaderSize = false;
178: }
179: System.arraycopy(data, offset, complexData, 0,
180: complexData.length);
181: }
182: return complexData.length;
183: }
184:
185: /**
186: * Serializes the simple part of this property. ie the first 6 bytes.
187: *
188: * Needs special code to handle the case when the size doesn't
189: * include the size of the header block
190: */
191: public int serializeSimplePart(byte[] data, int pos) {
192: LittleEndian.putShort(data, pos, getId());
193: int recordSize = complexData.length;
194: if (!sizeIncludesHeaderSize) {
195: recordSize -= 6;
196: }
197: LittleEndian.putInt(data, pos + 2, recordSize);
198: return 6;
199: }
200:
201: /**
202: * Sometimes the element size is stored as a negative number. We
203: * negate it and shift it to get the real value.
204: */
205: public static int getActualSizeOfElements(short sizeOfElements) {
206: if (sizeOfElements < 0)
207: return (short) ((-sizeOfElements) >> 2);
208: else
209: return sizeOfElements;
210: }
211:
212: }
|