123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- /* ====================================================================
- 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.
- ==================================================================== */
-
- package org.apache.poi.ss.formula;
-
- import java.util.Arrays;
-
- import org.apache.poi.ss.formula.ptg.ExpPtg;
- import org.apache.poi.ss.formula.ptg.Ptg;
- import org.apache.poi.ss.formula.ptg.TblPtg;
- import org.apache.poi.ss.util.CellReference;
- import org.apache.poi.util.IOUtils;
- import org.apache.poi.util.LittleEndian;
- import org.apache.poi.util.LittleEndianByteArrayInputStream;
- import org.apache.poi.util.LittleEndianInput;
- import org.apache.poi.util.LittleEndianOutput;
-
- /**
- * Encapsulates an encoded formula token array.
- *
- * @author Josh Micich
- */
- public class Formula {
-
- //Arbitrarily set. May need to increase.
- private static final int MAX_ENCODED_LEN = 100000;
-
- private static final Formula EMPTY = new Formula(new byte[0], 0);
-
- /** immutable */
- private final byte[] _byteEncoding;
- private final int _encodedTokenLen;
-
- private Formula(byte[] byteEncoding, int encodedTokenLen) {
- _byteEncoding = byteEncoding.clone();
- _encodedTokenLen = encodedTokenLen;
-
- // TODO - this seems to occur when IntersectionPtg is present
- // This example file "IntersectionPtg.xls"
- // used by test: TestIntersectionPtg.testReading()
- // has 10 bytes unused at the end of the formula
- // 10 extra bytes are just 0x01 and 0x00
- // LittleEndianByteArrayInputStream in = new LittleEndianByteArrayInputStream(byteEncoding);
- // Ptg.readTokens(encodedTokenLen, in);
- // int nUnusedBytes = _byteEncoding.length - in.getReadIndex();
- }
- /**
- * Convenience method for {@link #read(int, LittleEndianInput, int)}
- */
- public static Formula read(int encodedTokenLen, LittleEndianInput in) {
- return read(encodedTokenLen, in, encodedTokenLen);
- }
- /**
- * When there are no array constants present, <tt>encodedTokenLen</tt>==<tt>totalEncodedLen</tt>
- * @param encodedTokenLen number of bytes in the stream taken by the plain formula tokens
- * @param totalEncodedLen the total number of bytes in the formula (includes trailing encoding
- * for array constants, but does not include 2 bytes for initial <tt>ushort encodedTokenLen</tt> field.
- * @return A new formula object as read from the stream. Possibly empty, never <code>null</code>.
- */
- public static Formula read(int encodedTokenLen, LittleEndianInput in, int totalEncodedLen) {
- byte[] byteEncoding = IOUtils.safelyAllocate(totalEncodedLen, MAX_ENCODED_LEN);
- in.readFully(byteEncoding);
- return new Formula(byteEncoding, encodedTokenLen);
- }
-
- public Ptg[] getTokens() {
- LittleEndianInput in = new LittleEndianByteArrayInputStream(_byteEncoding);
- return Ptg.readTokens(_encodedTokenLen, in);
- }
- /**
- * Writes The formula encoding is includes:
- * <ul>
- * <li>ushort tokenDataLen</li>
- * <li>tokenData</li>
- * <li>arrayConstantData (if present)</li>
- * </ul>
- */
- public void serialize(LittleEndianOutput out) {
- out.writeShort(_encodedTokenLen);
- out.write(_byteEncoding);
- }
-
- public void serializeTokens(LittleEndianOutput out) {
- out.write(_byteEncoding, 0, _encodedTokenLen);
- }
- public void serializeArrayConstantData(LittleEndianOutput out) {
- int len = _byteEncoding.length-_encodedTokenLen;
- out.write(_byteEncoding, _encodedTokenLen, len);
- }
-
-
- /**
- * @return total formula encoding length. The formula encoding includes:
- * <ul>
- * <li>ushort tokenDataLen</li>
- * <li>tokenData</li>
- * <li>arrayConstantData (optional)</li>
- * </ul>
- * Note - this value is different to <tt>tokenDataLength</tt>
- */
- public int getEncodedSize() {
- return 2 + _byteEncoding.length;
- }
- /**
- * This method is often used when the formula length does not appear immediately before
- * the encoded token data.
- *
- * @return the encoded length of the plain formula tokens. This does <em>not</em> include
- * the leading ushort field, nor any trailing array constant data.
- */
- public int getEncodedTokenSize() {
- return _encodedTokenLen;
- }
-
- /**
- * Creates a {@link Formula} object from a supplied {@link Ptg} array.
- * Handles <code>null</code>s OK.
- * @param ptgs may be <code>null</code>
- * @return Never <code>null</code> (Possibly empty if the supplied <tt>ptgs</tt> is <code>null</code>)
- */
- public static Formula create(Ptg[] ptgs) {
- if (ptgs == null || ptgs.length < 1) {
- return EMPTY;
- }
- int totalSize = Ptg.getEncodedSize(ptgs);
- byte[] encodedData = new byte[totalSize];
- Ptg.serializePtgs(ptgs, encodedData, 0);
- int encodedTokenLen = Ptg.getEncodedSizeWithoutArrayData(ptgs);
- return new Formula(encodedData, encodedTokenLen);
- }
- /**
- * Gets the {@link Ptg} array from the supplied {@link Formula}.
- * Handles <code>null</code>s OK.
- *
- * @param formula may be <code>null</code>
- * @return possibly <code>null</code> (if the supplied <tt>formula</tt> is <code>null</code>)
- */
- public static Ptg[] getTokens(Formula formula) {
- if (formula == null) {
- return null;
- }
- return formula.getTokens();
- }
-
- public Formula copy() {
- // OK to return this because immutable
- return this;
- }
-
- /**
- * Gets the locator for the corresponding {@link org.apache.poi.hssf.record.SharedFormulaRecord},
- * {@link org.apache.poi.hssf.record.ArrayRecord} or {@link org.apache.poi.hssf.record.TableRecord}
- * if this formula belongs to such a grouping. The {@link CellReference}
- * returned by this method will match the top left corner of the range of that grouping.
- * The return value is usually not the same as the location of the cell containing this formula.
- *
- * @return the firstRow & firstColumn of an array formula or shared formula that this formula
- * belongs to. <code>null</code> if this formula is not part of an array or shared formula.
- */
- public CellReference getExpReference() {
- byte[] data = _byteEncoding;
- if (data.length != 5) {
- // tExp and tTbl are always 5 bytes long, and the only ptg in the formula
- return null;
- }
- switch (data[0]) {
- case ExpPtg.sid:
- break;
- case TblPtg.sid:
- break;
- default:
- return null;
- }
- int firstRow = LittleEndian.getUShort(data, 1);
- int firstColumn = LittleEndian.getUShort(data, 3);
- return new CellReference(firstRow, firstColumn);
- }
- public boolean isSame(Formula other) {
- return Arrays.equals(_byteEncoding, other._byteEncoding);
- }
- }
|