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.hssf.record.formula;
019:
020: import org.apache.poi.util.LittleEndian;
021: import org.apache.poi.util.BitField;
022: import org.apache.poi.util.BitFieldFactory;
023: import org.apache.poi.util.StringUtil;
024:
025: import org.apache.poi.hssf.util.CellReference;
026: import org.apache.poi.hssf.model.Workbook;
027: import org.apache.poi.hssf.record.RecordFormatException;
028: import org.apache.poi.hssf.record.RecordInputStream;
029: import org.apache.poi.hssf.record.SSTRecord;
030: import org.apache.poi.hssf.record.UnicodeString;
031:
032: /**
033: * ArrayPtg - handles arrays
034: *
035: * The ArrayPtg is a little wierd, the size of the Ptg when parsing initially only
036: * includes the Ptg sid and the reserved bytes. The next Ptg in the expression then follows.
037: * It is only after the "size" of all the Ptgs is met, that the ArrayPtg data is actually
038: * held after this. So Ptg.createParsedExpression keeps track of the number of
039: * ArrayPtg elements and need to parse the data upto the FORMULA record size.
040: *
041: * @author Jason Height (jheight at chariot dot net dot au)
042: */
043:
044: public class ArrayPtg extends Ptg {
045: public final static byte sid = 0x20;
046: protected byte field_1_reserved;
047: protected byte field_2_reserved;
048: protected byte field_3_reserved;
049: protected byte field_4_reserved;
050: protected byte field_5_reserved;
051: protected byte field_6_reserved;
052: protected byte field_7_reserved;
053:
054: protected short token_1_columns;
055: protected short token_2_rows;
056: protected Object[][] token_3_arrayValues;
057:
058: protected ArrayPtg() {
059: //Required for clone methods
060: }
061:
062: public ArrayPtg(RecordInputStream in) {
063: field_1_reserved = in.readByte();
064: field_2_reserved = in.readByte();
065: field_3_reserved = in.readByte();
066: field_4_reserved = in.readByte();
067: field_5_reserved = in.readByte();
068: field_6_reserved = in.readByte();
069: field_7_reserved = in.readByte();
070: }
071:
072: /** Read in the actual token (array) values. This occurs AFTER the last
073: * Ptg in the expression.
074: */
075: public void readTokenValues(RecordInputStream in) {
076: token_1_columns = (short) (0x00ff & in.readByte());
077: token_2_rows = in.readShort();
078:
079: //The token_1_columns and token_2_rows do not follow the documentation.
080: //The number of physical rows and columns is actually +1 of these values.
081: //Which is not explicitly documented.
082: token_1_columns++;
083: token_2_rows++;
084:
085: token_3_arrayValues = new Object[token_1_columns][token_2_rows];
086:
087: for (int x = 0; x < token_1_columns; x++) {
088: for (int y = 0; y < token_2_rows; y++) {
089: byte grbit = in.readByte();
090: if (grbit == 0x01) {
091: token_3_arrayValues[x][y] = new Double(in
092: .readDouble());
093: } else if (grbit == 0x02) {
094: //Ignore the doco, it is actually a unicode string with all the
095: //trimmings ie 16 bit size, option byte etc
096: token_3_arrayValues[x][y] = in.readUnicodeString();
097: } else
098: throw new RecordFormatException("Unknown grbit '"
099: + grbit + "'");
100: }
101: }
102:
103: }
104:
105: public String toString() {
106: StringBuffer buffer = new StringBuffer("[ArrayPtg]\n");
107:
108: buffer.append("columns = ").append(getColumnCount()).append(
109: "\n");
110: buffer.append("rows = ").append(getRowCount()).append("\n");
111: for (int x = 0; x < getColumnCount(); x++) {
112: for (int y = 0; y < getRowCount(); y++) {
113: Object o = token_3_arrayValues[x][y];
114: buffer.append("[").append(x).append("][").append(y)
115: .append("] = ").append(o).append("\n");
116: }
117: }
118: return buffer.toString();
119: }
120:
121: public void writeBytes(byte[] array, int offset) {
122: array[offset++] = (byte) (sid + ptgClass);
123: array[offset++] = field_1_reserved;
124: array[offset++] = field_2_reserved;
125: array[offset++] = field_3_reserved;
126: array[offset++] = field_4_reserved;
127: array[offset++] = field_5_reserved;
128: array[offset++] = field_6_reserved;
129: array[offset++] = field_7_reserved;
130:
131: }
132:
133: public int writeTokenValueBytes(byte[] array, int offset) {
134: int pos = 0;
135: array[pos + offset] = (byte) (token_1_columns - 1);
136: pos++;
137: LittleEndian.putShort(array, pos + offset,
138: (short) (token_2_rows - 1));
139: pos += 2;
140: for (int x = 0; x < getColumnCount(); x++) {
141: for (int y = 0; y < getRowCount(); y++) {
142: Object o = token_3_arrayValues[x][y];
143: if (o instanceof Double) {
144: array[pos + offset] = 0x01;
145: pos++;
146: LittleEndian.putDouble(array, pos + offset,
147: ((Double) o).doubleValue());
148: pos += 8;
149: } else if (o instanceof UnicodeString) {
150: array[pos + offset] = 0x02;
151: pos++;
152: UnicodeString s = (UnicodeString) o;
153: //JMH TBD Handle string continuation. Id do it now but its 4am.
154: UnicodeString.UnicodeRecordStats stats = new UnicodeString.UnicodeRecordStats();
155: s.serialize(stats, pos + offset, array);
156: pos += stats.recordSize;
157: } else
158: throw new RuntimeException("Coding error");
159: }
160: }
161: return pos;
162: }
163:
164: public void setRowCount(short row) {
165: token_2_rows = row;
166: }
167:
168: public short getRowCount() {
169: return token_2_rows;
170: }
171:
172: public void setColumnCount(short col) {
173: token_1_columns = (byte) col;
174: }
175:
176: public short getColumnCount() {
177: return token_1_columns;
178: }
179:
180: /** This size includes the size of the array Ptg plus the Array Ptg Token value size*/
181: public int getSize() {
182: int size = 1 + 7 + 1 + 2;
183: for (int x = 0; x < getColumnCount(); x++) {
184: for (int y = 0; y < getRowCount(); y++) {
185: Object o = token_3_arrayValues[x][y];
186: if (o instanceof UnicodeString) {
187: size++;
188: UnicodeString.UnicodeRecordStats rs = new UnicodeString.UnicodeRecordStats();
189: ((UnicodeString) o).getRecordSize(rs);
190: size += rs.recordSize;
191: } else if (o instanceof Double) {
192: size += 9;
193: }
194: }
195: }
196: return size;
197: }
198:
199: public String toFormulaString(Workbook book) {
200: StringBuffer b = new StringBuffer();
201: b.append("{");
202: for (int x = 0; x < getColumnCount(); x++) {
203: for (int y = 0; y < getRowCount(); y++) {
204: Object o = token_3_arrayValues[x][y];
205: if (o instanceof String) {
206: b.append((String) o);
207: } else if (o instanceof Double) {
208: b.append(((Double) o).doubleValue());
209: }
210: if (y != getRowCount())
211: b.append(",");
212: }
213: if (x != getColumnCount())
214: b.append(";");
215: }
216: b.append("}");
217: return b.toString();
218: }
219:
220: public byte getDefaultOperandClass() {
221: return Ptg.CLASS_ARRAY;
222: }
223:
224: public Object clone() {
225: ArrayPtg ptg = new ArrayPtg();
226: ptg.field_1_reserved = field_1_reserved;
227: ptg.field_2_reserved = field_2_reserved;
228: ptg.field_3_reserved = field_3_reserved;
229: ptg.field_4_reserved = field_4_reserved;
230: ptg.field_5_reserved = field_5_reserved;
231: ptg.field_6_reserved = field_6_reserved;
232: ptg.field_7_reserved = field_7_reserved;
233:
234: ptg.token_1_columns = token_1_columns;
235: ptg.token_2_rows = token_2_rows;
236: ptg.token_3_arrayValues = new Object[getColumnCount()][getRowCount()];
237: for (int x = 0; x < getColumnCount(); x++) {
238: for (int y = 0; y < getRowCount(); y++) {
239: ptg.token_3_arrayValues[x][y] = token_3_arrayValues[x][y];
240: }
241: }
242: ptg.setClass(ptgClass);
243: return ptg;
244: }
245: }
|