package org.apache.poi.ddf;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
public final class EscherArrayProperty extends EscherComplexProperty implements Iterable<byte[]> {
private static final int MAX_RECORD_LENGTH = 100_000;
private static final int FIXED_SIZE = 3 * 2;
private boolean = true;
private boolean emptyComplexPart;
public EscherArrayProperty(short id, byte[] complexData) {
super(id, checkComplexData(complexData));
emptyComplexPart = (complexData == null || complexData.length == 0);
}
public EscherArrayProperty(short propertyNumber, boolean isBlipId, byte[] complexData) {
super(propertyNumber, isBlipId, checkComplexData(complexData));
}
private static byte[] checkComplexData(byte[] complexData) {
if (complexData == null || complexData.length == 0) {
return new byte[6];
}
return complexData;
}
public int getNumberOfElementsInArray() {
return (emptyComplexPart) ? 0 : LittleEndian.getUShort(getComplexData(), 0);
}
public void setNumberOfElementsInArray(int numberOfElements) {
int expectedArraySize = numberOfElements * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
if (expectedArraySize != getComplexData().length) {
byte[] newArray = IOUtils.safelyAllocate(expectedArraySize, MAX_RECORD_LENGTH);
System.arraycopy(getComplexData(), 0, newArray, 0, getComplexData().length);
setComplexData(newArray);
}
LittleEndian.putShort(getComplexData(), 0, (short) numberOfElements);
}
public int getNumberOfElementsInMemory() {
return (emptyComplexPart) ? 0 : LittleEndian.getUShort(getComplexData(), 2);
}
public void setNumberOfElementsInMemory(int numberOfElements) {
int expectedArraySize = numberOfElements * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
if (expectedArraySize != getComplexData().length) {
byte[] newArray = IOUtils.safelyAllocate(expectedArraySize, MAX_RECORD_LENGTH);
System.arraycopy(getComplexData(), 0, newArray, 0, expectedArraySize);
setComplexData(newArray);
}
LittleEndian.putShort(getComplexData(), 2, (short) numberOfElements);
}
public short getSizeOfElements() {
return (emptyComplexPart) ? 0 : LittleEndian.getShort( getComplexData(), 4 );
}
public void setSizeOfElements(int sizeOfElements) {
LittleEndian.putShort( getComplexData(), 4, (short) sizeOfElements );
int expectedArraySize = getNumberOfElementsInArray() * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
if (expectedArraySize != getComplexData().length) {
byte[] newArray = IOUtils.safelyAllocate(expectedArraySize, MAX_RECORD_LENGTH);
System.arraycopy( getComplexData(), 0, newArray, 0, 6 );
setComplexData(newArray);
}
}
public byte[] getElement(int index) {
int actualSize = getActualSizeOfElements(getSizeOfElements());
byte[] result = IOUtils.safelyAllocate(actualSize, MAX_RECORD_LENGTH);
System.arraycopy(getComplexData(), FIXED_SIZE + index * actualSize, result, 0, result.length );
return result;
}
public void setElement(int index, byte[] element) {
int actualSize = getActualSizeOfElements(getSizeOfElements());
System.arraycopy( element, 0, getComplexData(), FIXED_SIZE + index * actualSize, actualSize);
}
@Override
public String toString() {
StringBuilder results = new StringBuilder();
results.append("propNum: ").append(getPropertyNumber());
results.append(", propName: ").append(EscherProperties.getPropertyName( getPropertyNumber() ));
results.append(", complex: ").append(isComplex());
results.append(", blipId: ").append(isBlipId());
results.append(", data: \n");
results.append(" {EscherArrayProperty:" + '\n');
results.append(" Num Elements: ").append(getNumberOfElementsInArray()).append('\n');
results.append(" Num Elements In Memory: ").append(getNumberOfElementsInMemory()).append('\n');
results.append(" Size of elements: ").append(getSizeOfElements()).append('\n');
for (int i = 0; i < getNumberOfElementsInArray(); i++) {
results.append(" Element ").append(i).append(": ").append(HexDump.toHex(getElement(i))).append('\n');
}
results.append("}" + '\n');
return results.toString();
}
@Override
public String toXml(String tab){
StringBuilder builder = new StringBuilder();
builder.append(tab).append("<").append(getClass().getSimpleName()).append(" id=\"0x").append(HexDump.toHex(getId()))
.append("\" name=\"").append(getName()).append("\" blipId=\"")
.append(isBlipId()).append("\">\n");
for (int i = 0; i < getNumberOfElementsInArray(); i++) {
builder.append("\t").append(tab).append("<Element>").append(HexDump.toHex(getElement(i))).append("</Element>\n");
}
builder.append(tab).append("</").append(getClass().getSimpleName()).append(">");
return builder.toString();
}
public int setArrayData(byte[] data, int offset) {
if (emptyComplexPart){
setComplexData(new byte[0]);
} else {
short numElements = LittleEndian.getShort(data, offset);
short sizeOfElements = LittleEndian.getShort(data, offset + 4);
int arraySize = getActualSizeOfElements(sizeOfElements) * numElements;
if (arraySize == getComplexData().length) {
setComplexData(new byte[arraySize + 6]);
sizeIncludesHeaderSize = false;
}
System.arraycopy(data, offset, getComplexData(), 0, getComplexData().length );
}
return getComplexData().length;
}
@Override
public int serializeSimplePart(byte[] data, int pos) {
LittleEndian.putShort(data, pos, getId());
int recordSize = getComplexData().length;
if(!sizeIncludesHeaderSize) {
recordSize -= 6;
}
LittleEndian.putInt(data, pos + 2, recordSize);
return 6;
}
private static int getActualSizeOfElements(short sizeOfElements) {
if (sizeOfElements < 0) {
return (short) ( ( -sizeOfElements ) >> 2 );
}
return sizeOfElements;
}
@Override
public Iterator<byte[]> iterator() {
return new Iterator<byte[]>(){
int idx;
@Override
public boolean hasNext() {
return (idx < getNumberOfElementsInArray());
}
@Override
public byte[] next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return getElement(idx++);
}
@Override
public void remove() {
throw new UnsupportedOperationException("not yet implemented");
}
};
}
}