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;
019:
020: import java.util.Stack;
021: import java.util.List;
022:
023: import org.apache.poi.hssf.record.formula.*;
024: import org.apache.poi.util.LittleEndian;
025:
026: /**
027: * Title: SharedFormulaRecord
028: * Description: Primarily used as an excel optimization so that multiple similar formulas
029: * are not written out too many times. We should recognize this record and
030: * serialize as is since this is used when reading templates.
031: * <p>
032: * Note: the documentation says that the SID is BC where biffviewer reports 4BC. The hex dump shows
033: * that the two byte sid representation to be 'BC 04' that is consistent with the other high byte
034: * record types.
035: * @author Danny Mui at apache dot org
036: */
037:
038: public class SharedFormulaRecord extends Record {
039: public final static short sid = 0x4BC;
040:
041: private int field_1_first_row;
042: private int field_2_last_row;
043: private short field_3_first_column;
044: private short field_4_last_column;
045: private int field_5_reserved;
046: private short field_6_expression_len;
047: private Stack field_7_parsed_expr;
048:
049: public SharedFormulaRecord() {
050: }
051:
052: /**
053: * @param in the RecordInputstream to read the record from
054: */
055:
056: public SharedFormulaRecord(RecordInputStream in) {
057: super (in);
058: }
059:
060: protected void validateSid(short id) {
061: if (id != this .sid) {
062: throw new RecordFormatException("Not a valid SharedFormula");
063: }
064: }
065:
066: public int getFirstRow() {
067: return field_1_first_row;
068: }
069:
070: public int getLastRow() {
071: return field_2_last_row;
072: }
073:
074: public short getFirstColumn() {
075: return field_3_first_column;
076: }
077:
078: public short getLastColumn() {
079: return field_4_last_column;
080: }
081:
082: public short getExpressionLength() {
083: return field_6_expression_len;
084: }
085:
086: /**
087: * spit the record out AS IS. no interperatation or identification
088: */
089:
090: public int serialize(int offset, byte[] data) {
091: //Because this record is converted to individual Formula records, this method is not required.
092: throw new UnsupportedOperationException(
093: "Cannot serialize a SharedFormulaRecord");
094: }
095:
096: public int getRecordSize() {
097: //Because this record is converted to individual Formula records, this method is not required.
098: throw new UnsupportedOperationException(
099: "Cannot get the size for a SharedFormulaRecord");
100:
101: }
102:
103: /**
104: * print a sort of string representation ([SHARED FORMULA RECORD] id = x [/SHARED FORMULA RECORD])
105: */
106:
107: public String toString() {
108: StringBuffer buffer = new StringBuffer();
109:
110: buffer.append("[SHARED FORMULA RECORD:"
111: + Integer.toHexString(sid) + "]\n");
112: buffer.append(" .id = ").append(
113: Integer.toHexString(sid)).append("\n");
114: buffer.append(" .first_row = ").append(
115: Integer.toHexString(getFirstRow())).append("\n");
116: buffer.append(" .last_row = ").append(
117: Integer.toHexString(getLastRow())).append("\n");
118: buffer.append(" .first_column = ").append(
119: Integer.toHexString(getFirstColumn())).append("\n");
120: buffer.append(" .last_column = ").append(
121: Integer.toHexString(getLastColumn())).append("\n");
122: buffer.append(" .reserved = ").append(
123: Integer.toHexString(field_5_reserved)).append("\n");
124: buffer.append(" .expressionlength= ").append(
125: getExpressionLength()).append("\n");
126:
127: buffer.append(" .numptgsinarray = ").append(
128: field_7_parsed_expr.size()).append("\n");
129:
130: for (int k = 0; k < field_7_parsed_expr.size(); k++) {
131: buffer.append("Formula ").append(k).append("\n").append(
132: field_7_parsed_expr.get(k).toString()).append("\n");
133: }
134:
135: buffer.append("[/SHARED FORMULA RECORD]\n");
136: return buffer.toString();
137: }
138:
139: public short getSid() {
140: return sid;
141: }
142:
143: /**
144: * Shared formulas are to treated like unknown records, and as a result d
145: */
146: protected void fillFields(RecordInputStream in) {
147: field_1_first_row = in.readShort();
148: field_2_last_row = in.readShort();
149: field_3_first_column = in.readByte();
150: field_4_last_column = in.readByte();
151: field_5_reserved = in.readShort();
152: field_6_expression_len = in.readShort();
153: field_7_parsed_expr = getParsedExpressionTokens(in);
154: }
155:
156: private Stack getParsedExpressionTokens(RecordInputStream in) {
157: Stack stack = new Stack();
158:
159: while (in.remaining() != 0) {
160: Ptg ptg = Ptg.createPtg(in);
161: stack.push(ptg);
162: }
163: return stack;
164: }
165:
166: public boolean isFormulaInShared(FormulaRecord formula) {
167: final int formulaRow = formula.getRow();
168: final int formulaColumn = formula.getColumn();
169: return ((getFirstRow() <= formulaRow)
170: && (getLastRow() >= formulaRow)
171: && (getFirstColumn() <= formulaColumn) && (getLastColumn() >= formulaColumn));
172: }
173:
174: /** Creates a non shared formula from the shared formula counter part*/
175: public void convertSharedFormulaRecord(FormulaRecord formula) {
176: //Sanity checks
177: final int formulaRow = formula.getRow();
178: final int formulaColumn = formula.getColumn();
179: if (isFormulaInShared(formula)) {
180: formula.setExpressionLength(getExpressionLength());
181: Stack newPtgStack = new Stack();
182:
183: if (field_7_parsed_expr != null)
184: for (int k = 0; k < field_7_parsed_expr.size(); k++) {
185: Ptg ptg = (Ptg) field_7_parsed_expr.get(k);
186: if (ptg instanceof RefNPtg) {
187: RefNPtg refNPtg = (RefNPtg) ptg;
188: ptg = new ReferencePtg(
189: (short) (formulaRow + refNPtg.getRow()),
190: (byte) (formulaColumn + refNPtg
191: .getColumn()), refNPtg
192: .isRowRelative(), refNPtg
193: .isColRelative());
194: } else if (ptg instanceof RefNVPtg) {
195: RefNVPtg refNVPtg = (RefNVPtg) ptg;
196: ptg = new RefVPtg(
197: (short) (formulaRow + refNVPtg.getRow()),
198: (byte) (formulaColumn + refNVPtg
199: .getColumn()), refNVPtg
200: .isRowRelative(), refNVPtg
201: .isColRelative());
202: } else if (ptg instanceof RefNAPtg) {
203: RefNAPtg refNAPtg = (RefNAPtg) ptg;
204: ptg = new RefAPtg(
205: (short) (formulaRow + refNAPtg.getRow()),
206: (byte) (formulaColumn + refNAPtg
207: .getColumn()), refNAPtg
208: .isRowRelative(), refNAPtg
209: .isColRelative());
210: } else if (ptg instanceof AreaNPtg) {
211: AreaNPtg areaNPtg = (AreaNPtg) ptg;
212: ptg = new AreaPtg(
213: (short) (formulaRow + areaNPtg
214: .getFirstRow()),
215: (short) (formulaRow + areaNPtg
216: .getLastRow()),
217: (short) (formulaColumn + areaNPtg
218: .getFirstColumn()),
219: (short) (formulaColumn + areaNPtg
220: .getLastColumn()), areaNPtg
221: .isFirstRowRelative(), areaNPtg
222: .isLastRowRelative(), areaNPtg
223: .isFirstColRelative(), areaNPtg
224: .isLastColRelative());
225: } else if (ptg instanceof AreaNVPtg) {
226: AreaNVPtg areaNVPtg = (AreaNVPtg) ptg;
227: ptg = new AreaVPtg(
228: (short) (formulaRow + areaNVPtg
229: .getFirstRow()),
230: (short) (formulaRow + areaNVPtg
231: .getLastRow()),
232: (short) (formulaColumn + areaNVPtg
233: .getFirstColumn()),
234: (short) (formulaColumn + areaNVPtg
235: .getLastColumn()), areaNVPtg
236: .isFirstRowRelative(),
237: areaNVPtg.isLastRowRelative(),
238: areaNVPtg.isFirstColRelative(),
239: areaNVPtg.isLastColRelative());
240: } else if (ptg instanceof AreaNAPtg) {
241: AreaNAPtg areaNAPtg = (AreaNAPtg) ptg;
242: ptg = new AreaAPtg(
243: (short) (formulaRow + areaNAPtg
244: .getFirstRow()),
245: (short) (formulaRow + areaNAPtg
246: .getLastRow()),
247: (short) (formulaColumn + areaNAPtg
248: .getFirstColumn()),
249: (short) (formulaColumn + areaNAPtg
250: .getLastColumn()), areaNAPtg
251: .isFirstRowRelative(),
252: areaNAPtg.isLastRowRelative(),
253: areaNAPtg.isFirstColRelative(),
254: areaNAPtg.isLastColRelative());
255: }
256: newPtgStack.add(ptg);
257: }
258: formula.setParsedExpression(newPtgStack);
259: //Now its not shared!
260: formula.setSharedFormula(false);
261: } else {
262: throw new RuntimeException(
263: "Shared Formula Conversion: Coding Error");
264: }
265: }
266:
267: /**
268: * Mirroring formula records so it is registered in the ValueRecordsAggregate
269: */
270: public boolean isInValueSection() {
271: return true;
272: }
273:
274: /**
275: * Register it in the ValueRecordsAggregate so it can go into the FormulaRecordAggregate
276: */
277: public boolean isValue() {
278: return true;
279: }
280:
281: public Object clone() {
282: //Because this record is converted to individual Formula records, this method is not required.
283: throw new UnsupportedOperationException(
284: "Cannot clone a SharedFormulaRecord");
285: }
286: }
|