blob: 671e619eef01b6ecd554c170cec7b094cdaa9282 [file] [log] [blame]
/**************************************************************
*
* 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.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula;
import java.io.*;
import java.util.Vector;
import java.util.Enumeration;
import org.openoffice.xmerge.util.Debug;
import org.openoffice.xmerge.util.EndianConverter;
import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook;
import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.DefinedName;
/**
* The TokenEncoder encodes a Token to an equivalent pexcel byte[]. The only
* public method apart from the default constructor is the getByte method.
* This method picks an encoder based onthe Token's type or id field and uses
* that encoder to return a byte[] which it returns. This Encoder supports
* Operands Floating point's, Cell references (absolute and relative),
* cell ranges
* Operators +,-,*,/,<,>.<=,>=,<>
* Functions All pexcel fixed and varaible argument functions
*
*/
public class TokenEncoder {
private FunctionLookup fl;
private String parseString;
private int index;
private Workbook wb;
/**
* Default Constructor
*/
public TokenEncoder() {
parseString = new String();
fl = new FunctionLookup();
}
/**
* Sets global workbook data needed for defined names
*/
public void setWorkbook(Workbook wb) {
this.wb = wb;
}
/**
* Return the byte[] equivalent of a <code>Token</code>. The various
* encoders return <code>Vector</code> of <code>Byte</code> instead
* of byte[] because the number of bytes returned varies with each
* <code>Token</code> encoded. After the encoding is finished the Vector
* in converted to a byte[].
*
* @param t The <code>Token</code> to be encoded
* @return An equivalent Pocket Excel byte[]
*/
public byte[] getByte(Token t) throws IOException {
Vector tmpByteArray = null; // we use this cause we don't know till after
// the encoding takes place how big the byte [] will be
//index=0; // This class is declared static in
// FormulaHelper so better make sure our index is 0
if(t.getTokenType()==ParseToken.TOKEN_OPERATOR) {
tmpByteArray = operatorEncoder(t);
} else if (t.getTokenType()==ParseToken.TOKEN_FUNCTION_VARIABLE || t.getTokenType()==ParseToken.TOKEN_FUNCTION_FIXED){
tmpByteArray = functionEncoder(t);
} else { // Operands and functions
switch(t.getTokenID()) {
case TokenConstants.TNAME :
tmpByteArray = nameDefinitionEncoder(t);
break;
case TokenConstants.TREF3D :
tmpByteArray = threeDCellRefEncoder(t);
break;
case TokenConstants.TAREA3D:
tmpByteArray = threeDAreaRefEncoder(t);
break;
case TokenConstants.TREF :
tmpByteArray = cellRefEncoder(t);
break;
case TokenConstants.TAREA :
tmpByteArray = areaRefEncoder(t);
break;
case TokenConstants.TNUM :
tmpByteArray = numEncoder(t);
break;
case TokenConstants.TSTRING :
tmpByteArray = stringEncoder(t);
break;
default :
Debug.log(Debug.ERROR, "Encoder found unrecognized Token");
}
}
byte cellRefArray[] = new byte[tmpByteArray.size()];
int i = 0;
String s = new String();
for(Enumeration e = tmpByteArray.elements();e.hasMoreElements();) {
Byte tmpByte = (Byte) e.nextElement();
s = s + tmpByte + " ";
cellRefArray[i] = tmpByte.byteValue();
i++;
}
Debug.log(Debug.TRACE, "Encoding Token " + t.getValue() + " as [" + s + "]");
return cellRefArray;
}
/**
* An Operator Encoder.
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector operatorEncoder(Token t) {
Vector tmpByteArray = new Vector();
tmpByteArray.add(new Byte((byte)t.getTokenID()));
return tmpByteArray;
}
/**
* A String Encoder.
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector stringEncoder(Token t) throws IOException{
Vector tmpByteArray = new Vector();
tmpByteArray.add(new Byte((byte)t.getTokenID()));
tmpByteArray.add(new Byte((byte)(t.getValue().length())));
tmpByteArray.add(new Byte((byte)0x01));
byte [] stringBytes = t.getValue().getBytes("UTF-16LE");
for (int i=0; i<stringBytes.length; i++) {
tmpByteArray.add(new Byte(stringBytes[i]));
}
return tmpByteArray;
}
/**
* An Integer Encoder.
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector numEncoder(Token t) {
Vector tmpByteArray = new Vector();
double cellLong = (double) Double.parseDouble(t.getValue());
tmpByteArray.add(new Byte((byte)t.getTokenID()));
byte[] tempByte = EndianConverter.writeDouble(cellLong);
for(int byteIter=0;byteIter<tempByte.length;byteIter++) {
tmpByteArray.add(new Byte(tempByte[byteIter]));
}
return tmpByteArray;
}
/**
* Converts a char to an int. It is zero based
* so a=0, b=1 etc.
*
* @param ch the character to be converted
* @return -1 if not a character otherwise a 0 based index
*/
private int char2int(char ch) {
if(!Character.isLetter(ch))
return -1;
ch = Character.toUpperCase(ch);
return ch-'A';
}
/**
* Identify letters
*
* @param c The character which is to be identified
* @return A boolean returning the result of the comparison
*/
private boolean isAlpha(char c) {
return(Character.isLetter(c));
}
/**
* Identify numbers
*
* @param c The character which is to be identified
* @return A boolean returning the result of the comparison
*/
private boolean isDigit(char c) {
return(Character.isDigit(c));
}
/**
* Identify letters or numbers
*
* @param c The character which is to be identified
* @return A boolean returning the result of the comparison
*/
private boolean isAlphaNum(char c) {
return(isAlpha(c) || isDigit(c));
}
/**
* Parses a column reference and returns it's integer equivalent. (eg.
* A=0, D=3, BA=27)
*
* @return an 0 based index to a column
*/
private int column() {
char ch = parseString.charAt(index);
String columnStr = new String();
int col = 0;
while(isAlpha(ch)) {
columnStr += ch;
index++;
ch = parseString.charAt(index);
}
if(columnStr.length()==1) {
col = char2int(columnStr.charAt(0));
} else if (columnStr.length()==2) {
col = char2int(columnStr.charAt(0)) + 1;
col = (col*26) + char2int(columnStr.charAt(1));
} else {
Debug.log(Debug.ERROR, "Invalid Column Reference " + columnStr );
}
return col;
}
/**
* Parses a column reference and returns it's integer equivalent. (eg.
* A=0, D=3, BA=27)
*
* @return an 0 based index to a column
*/
private int row() {
char ch = parseString.charAt(index);
String rowStr = new String();
int row = 0;
boolean status = true;
do {
rowStr += ch;
index++;
if(index>=parseString.length()) {
status = false;
} else {
ch = parseString.charAt(index);
}
} while(isDigit(ch) && status);
return Integer.parseInt(rowStr)-1; // Pexcel uses a 0 based index
}
/**
* A Cell Reference Encoder (It supports absolute and relative addressing)
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private byte[] encodeCellCoordinates(String cellCoordinates) {
int col = 0, row = 0;
int addressing = 0xC000;
index = 0;
parseString = cellCoordinates;
Debug.log(Debug.TRACE,"Encoding cell coordinates " + cellCoordinates);
if(cellCoordinates.charAt(index)=='$') {
addressing &= 0x8000;
index++;
}
col = column();
if(cellCoordinates.charAt(index)=='$') {
addressing &= 0x4000;
index++;
}
row = row(); // Pexcel uses a 0 based index
row |= addressing;
byte tokenBytes[] = new byte[3];
tokenBytes[0] = (byte)row;
tokenBytes[1] = (byte)(row>>8);
tokenBytes[2] = (byte)col;
return tokenBytes;
}
/**
* A name definition Encoder
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector nameDefinitionEncoder(Token t) {
Vector tmpByteArray = new Vector();
String nameString = t.getValue();
Debug.log(Debug.TRACE,"NameDefinitionEncoder : " + nameString);
tmpByteArray.add(new Byte((byte)t.getTokenID()));
Enumeration e = wb.getDefinedNames();
DefinedName dn;
String name;
int definedNameIndex = 0;
do {
dn = (DefinedName)e.nextElement();
name = dn.getName();
Debug.log(Debug.TRACE,"Name pulled from DefinedName : " + name);
definedNameIndex++;
} while(!nameString.equalsIgnoreCase(name) && e.hasMoreElements());
tmpByteArray.add(new Byte((byte)definedNameIndex));
tmpByteArray.add(new Byte((byte)0x00));
for(int i = 0;i < 12;i++) {
tmpByteArray.add(new Byte((byte)0x00));
}
return tmpByteArray;
}
/**
* A Cell Reference Encoder. It supports absolute and relative addressing
* but not sheetnames.
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector cellRefEncoder(Token t) {
Vector tmpByteArray = new Vector();
tmpByteArray.add(new Byte((byte)t.getTokenID()));
byte cellRefBytes[] = encodeCellCoordinates(t.getValue());
for(int i = 0;i < cellRefBytes.length;i++) {
tmpByteArray.add(new Byte(cellRefBytes[i]));
}
return tmpByteArray;
}
/**
* This function will find the sheetname index for a given String
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private short findSheetIndex(String s) {
short sheetIndex = 0;
String savedName;
String sheetName;
if (s.startsWith("$")) {
sheetName = s.substring(1,s.length()); // Remove $
} else {
sheetName = s.substring(0,s.length());
}
Debug.log(Debug.TRACE,"Searching for Worksheet : " + sheetName);
Vector names = wb.getWorksheetNames();
Enumeration e = names.elements();
do {
savedName = (String) e.nextElement();
sheetIndex++;
} while(!savedName.equalsIgnoreCase(sheetName) && e.hasMoreElements());
Debug.log(Debug.TRACE,"Setting sheetindex to " + sheetIndex);
return (short)(sheetIndex-1);
}
/**
* A 3D Cell reference encoder
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector threeDCellRefEncoder(Token t) {
Vector tmpByteArray = new Vector();
parseString = t.getValue();
Debug.log(Debug.TRACE,"Encoding 3D Cell reference " + t);
tmpByteArray.add(new Byte((byte)t.getTokenID()));
tmpByteArray.add(new Byte((byte)0xFF));
tmpByteArray.add(new Byte((byte)0xFF));
for(int i = 0;i < 8;i++) {
tmpByteArray.add(new Byte((byte)0x00));
}
String sheetRef = parseString.substring(0, parseString.indexOf('.') + 1);
if (sheetRef.indexOf(':')!=-1) {
sheetRef = parseString.substring(0, parseString.indexOf(':'));
short sheetNum1 = findSheetIndex(sheetRef);
sheetRef = parseString.substring(parseString.indexOf(':') + 1, parseString.length());
short sheetNum2 = findSheetIndex(sheetRef);
tmpByteArray.add(new Byte((byte)sheetNum1));
tmpByteArray.add(new Byte((byte)0x00));
tmpByteArray.add(new Byte((byte)sheetNum2));
tmpByteArray.add(new Byte((byte)0x00));
} else {
sheetRef = parseString.substring(0, parseString.indexOf('.'));
short sheetNum = findSheetIndex(sheetRef);
tmpByteArray.add(new Byte((byte)sheetNum));
tmpByteArray.add(new Byte((byte)0x00));
tmpByteArray.add(new Byte((byte)sheetNum));
tmpByteArray.add(new Byte((byte)0x00));
}
String s = parseString.substring(parseString.indexOf('.') + 1, parseString.length());
Debug.log(Debug.TRACE,"Parsing : " + s);
byte cellRefBytes[] = encodeCellCoordinates(s);
for(int i = 0;i < cellRefBytes.length;i++) {
tmpByteArray.add(new Byte(cellRefBytes[i]));
}
return tmpByteArray;
}
/**
* A 3D Area Reference Encoder.
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector threeDAreaRefEncoder(Token t) {
Vector tmpByteArray = new Vector();
parseString = t.getValue();
Debug.log(Debug.TRACE,"Encoding 3D Area reference " + t);
tmpByteArray.add(new Byte((byte)t.getTokenID()));
tmpByteArray.add(new Byte((byte)0xFF));
tmpByteArray.add(new Byte((byte)0xFF));
for(int i = 0;i < 8;i++) {
tmpByteArray.add(new Byte((byte)0x00));
}
String param1= parseString.substring(0, parseString.indexOf(':'));
String cellRef1 = param1.substring(parseString.indexOf('.') + 1, param1.length());
String sheetRef1 = param1.substring(0, param1.indexOf('.'));
short sheetNum1 = findSheetIndex(sheetRef1);
String param2 = parseString.substring(parseString.indexOf(':') + 1, parseString.length());
Debug.log(Debug.TRACE,"param2: " + param2);
String cellRef2 = param2.substring(param2.indexOf('.') + 1, param2.length());
Debug.log(Debug.TRACE,"cellRef2: " + cellRef2);
if(param2.indexOf('.')==-1) {
tmpByteArray.add(new Byte((byte)sheetNum1));
tmpByteArray.add(new Byte((byte)0x00));
tmpByteArray.add(new Byte((byte)sheetNum1));
tmpByteArray.add(new Byte((byte)0x00));
} else {
String sheetRef2 = param2.substring(0, param2.indexOf('.'));
short sheetNum2 = findSheetIndex(sheetRef2);
tmpByteArray.add(new Byte((byte)sheetNum1));
tmpByteArray.add(new Byte((byte)0x00));
tmpByteArray.add(new Byte((byte)sheetNum2));
tmpByteArray.add(new Byte((byte)0x00));
}
byte cellRefBytes1[] = encodeCellCoordinates(cellRef1);
byte cellRefBytes2[] = encodeCellCoordinates(cellRef2);
tmpByteArray.add(new Byte(cellRefBytes1[0]));
tmpByteArray.add(new Byte(cellRefBytes1[1]));
tmpByteArray.add(new Byte(cellRefBytes2[0]));
tmpByteArray.add(new Byte(cellRefBytes2[1]));
tmpByteArray.add(new Byte(cellRefBytes1[2]));
tmpByteArray.add(new Byte(cellRefBytes2[2]));
return tmpByteArray;
}
/**
* A Cell Range Encoder.
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector areaRefEncoder(Token t) {
Vector tmpByteArray = new Vector();
tmpByteArray.add(new Byte((byte)t.getTokenID()));
String param = t.getValue();
String cellRef1 = new String();
String cellRef2 = new String();
if(param.indexOf(':')==-1) {
Debug.log(Debug.ERROR, "Invalid Cell Range, could not find :");
} else {
cellRef1 = param.substring(0, param.indexOf(':'));
cellRef2 = param.substring(param.indexOf(':') + 1, param.length());
}
byte cellRefBytes1[] = encodeCellCoordinates(cellRef1);
byte cellRefBytes2[] = encodeCellCoordinates(cellRef2);
tmpByteArray.add(new Byte(cellRefBytes1[0]));
tmpByteArray.add(new Byte(cellRefBytes1[1]));
tmpByteArray.add(new Byte(cellRefBytes2[0]));
tmpByteArray.add(new Byte(cellRefBytes2[1]));
tmpByteArray.add(new Byte(cellRefBytes1[2]));
tmpByteArray.add(new Byte(cellRefBytes2[2]));
return tmpByteArray;
}
/**
* A Function Encoder.
*
* @param t <code>Token</code> to be encoded
* @return A <code>Vector</code> of pexcel <code>Byte</code>
*/
private Vector functionEncoder(Token t) {
Vector tmpByteArray = new Vector();
int id = t.getTokenID();
if(t.getTokenType()==ParseToken.TOKEN_FUNCTION_VARIABLE) {
tmpByteArray.add(new Byte((byte)TokenConstants.TFUNCVAR));
tmpByteArray.add(new Byte((byte)t.getNumArgs()));
} else {
tmpByteArray.add(new Byte((byte)TokenConstants.TFUNC));
}
tmpByteArray.add(new Byte((byte)id));
tmpByteArray.add(new Byte((byte)(id>>8)));
return tmpByteArray;
}
}