1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; 25 26 import java.io.*; 27 import java.util.Vector; 28 import java.util.Enumeration; 29 30 import org.openoffice.xmerge.util.Debug; 31 import org.openoffice.xmerge.util.EndianConverter; 32 import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; 33 import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.DefinedName; 34 35 /** 36 * The TokenEncoder encodes a Token to an equivalent pexcel byte[]. The only 37 * public method apart from the default constructor is the getByte method. 38 * This method picks an encoder based on the Token's type or id field and uses 39 * that encoder to return a byte[] which it returns. This Encoder supports 40 * Operands Floating point's, Cell references (absolute and relative), 41 * cell ranges 42 * Operators +,-,*,/,<,>.<=,>=,<> 43 * Functions All pexcel fixed and variable argument functions 44 * 45 */ 46 public class TokenEncoder { 47 48 private FunctionLookup fl; 49 private String parseString; 50 private int index; 51 private Workbook wb; 52 53 /** 54 * Default Constructor 55 */ TokenEncoder()56 public TokenEncoder() { 57 58 parseString = new String(); 59 fl = new FunctionLookup(); 60 } 61 62 /** 63 * Sets global workbook data needed for defined names 64 */ setWorkbook(Workbook wb)65 public void setWorkbook(Workbook wb) { 66 67 this.wb = wb; 68 } 69 70 71 /** 72 * Return the byte[] equivalent of a <code>Token</code>. The various 73 * encoders return <code>Vector</code> of <code>Byte</code> instead 74 * of byte[] because the number of bytes returned varies with each 75 * <code>Token</code> encoded. After the encoding is finished the Vector 76 * in converted to a byte[]. 77 * 78 * @param t The <code>Token</code> to be encoded 79 * @return An equivalent Pocket Excel byte[] 80 */ getByte(Token t)81 public byte[] getByte(Token t) throws IOException { 82 83 Vector tmpByteArray = null; // we use this cause we don't know till after 84 // the encoding takes place how big the byte [] will be 85 //index=0; // This class is declared static in 86 // FormulaHelper so better make sure our index is 0 87 if(t.getTokenType()==ParseToken.TOKEN_OPERATOR) { 88 tmpByteArray = operatorEncoder(t); 89 } else if (t.getTokenType()==ParseToken.TOKEN_FUNCTION_VARIABLE || t.getTokenType()==ParseToken.TOKEN_FUNCTION_FIXED){ 90 tmpByteArray = functionEncoder(t); 91 } else { // Operands and functions 92 switch(t.getTokenID()) { 93 case TokenConstants.TNAME : 94 tmpByteArray = nameDefinitionEncoder(t); 95 break; 96 case TokenConstants.TREF3D : 97 tmpByteArray = threeDCellRefEncoder(t); 98 break; 99 case TokenConstants.TAREA3D: 100 tmpByteArray = threeDAreaRefEncoder(t); 101 break; 102 case TokenConstants.TREF : 103 tmpByteArray = cellRefEncoder(t); 104 break; 105 case TokenConstants.TAREA : 106 tmpByteArray = areaRefEncoder(t); 107 break; 108 case TokenConstants.TNUM : 109 tmpByteArray = numEncoder(t); 110 break; 111 case TokenConstants.TSTRING : 112 tmpByteArray = stringEncoder(t); 113 break; 114 default : 115 Debug.log(Debug.ERROR, "Encoder found unrecognized Token"); 116 } 117 } 118 119 byte cellRefArray[] = new byte[tmpByteArray.size()]; 120 int i = 0; 121 String s = new String(); 122 for(Enumeration e = tmpByteArray.elements();e.hasMoreElements();) { 123 Byte tmpByte = (Byte) e.nextElement(); 124 s = s + tmpByte + " "; 125 cellRefArray[i] = tmpByte.byteValue(); 126 i++; 127 } 128 Debug.log(Debug.TRACE, "Encoding Token " + t.getValue() + " as [" + s + "]"); 129 return cellRefArray; 130 } 131 132 /** 133 * An Operator Encoder. 134 * 135 * @param t <code>Token</code> to be encoded 136 * @return A <code>Vector</code> of pexcel <code>Byte</code> 137 */ operatorEncoder(Token t)138 private Vector operatorEncoder(Token t) { 139 140 Vector tmpByteArray = new Vector(); 141 tmpByteArray.add(new Byte((byte)t.getTokenID())); 142 return tmpByteArray; 143 } 144 145 146 /** 147 * A String Encoder. 148 * 149 * @param t <code>Token</code> to be encoded 150 * @return A <code>Vector</code> of pexcel <code>Byte</code> 151 */ stringEncoder(Token t)152 private Vector stringEncoder(Token t) throws IOException{ 153 154 Vector tmpByteArray = new Vector(); 155 tmpByteArray.add(new Byte((byte)t.getTokenID())); 156 tmpByteArray.add(new Byte((byte)(t.getValue().length()))); 157 tmpByteArray.add(new Byte((byte)0x01)); 158 byte [] stringBytes = t.getValue().getBytes("UTF-16LE"); 159 for (int i=0; i<stringBytes.length; i++) { 160 tmpByteArray.add(new Byte(stringBytes[i])); 161 } 162 return tmpByteArray; 163 } 164 165 166 /** 167 * An Integer Encoder. 168 * 169 * @param t <code>Token</code> to be encoded 170 * @return A <code>Vector</code> of pexcel <code>Byte</code> 171 */ numEncoder(Token t)172 private Vector numEncoder(Token t) { 173 174 Vector tmpByteArray = new Vector(); 175 176 double cellLong = (double) Double.parseDouble(t.getValue()); 177 tmpByteArray.add(new Byte((byte)t.getTokenID())); 178 byte[] tempByte = EndianConverter.writeDouble(cellLong); 179 for(int byteIter=0;byteIter<tempByte.length;byteIter++) { 180 tmpByteArray.add(new Byte(tempByte[byteIter])); 181 } 182 return tmpByteArray; 183 } 184 185 /** 186 * Converts a char to an int. It is zero based 187 * so a=0, b=1 etc. 188 * 189 * @param ch the character to be converted 190 * @return -1 if not a character otherwise a 0 based index 191 */ char2int(char ch)192 private int char2int(char ch) { 193 if(!Character.isLetter(ch)) 194 return -1; 195 196 ch = Character.toUpperCase(ch); 197 return ch-'A'; 198 } 199 200 /** 201 * Identify letters 202 * 203 * @param c The character which is to be identified 204 * @return A boolean returning the result of the comparison 205 */ isAlpha(char c)206 private boolean isAlpha(char c) { 207 return(Character.isLetter(c)); 208 } 209 210 /** 211 * Identify numbers 212 * 213 * @param c The character which is to be identified 214 * @return A boolean returning the result of the comparison 215 */ isDigit(char c)216 private boolean isDigit(char c) { 217 return(Character.isDigit(c)); 218 } 219 220 /** 221 * Identify letters or numbers 222 * 223 * @param c The character which is to be identified 224 * @return A boolean returning the result of the comparison 225 */ isAlphaNum(char c)226 private boolean isAlphaNum(char c) { 227 return(isAlpha(c) || isDigit(c)); 228 } 229 230 /** 231 * Parses a column reference and returns its integer equivalent. (e.g. 232 * A=0, D=3, BA=27) 233 * 234 * @return an 0 based index to a column 235 */ column()236 private int column() { 237 char ch = parseString.charAt(index); 238 String columnStr = new String(); 239 int col = 0; 240 241 while(isAlpha(ch)) { 242 columnStr += ch; 243 index++; 244 ch = parseString.charAt(index); 245 } 246 247 if(columnStr.length()==1) { 248 col = char2int(columnStr.charAt(0)); 249 } else if (columnStr.length()==2) { 250 col = char2int(columnStr.charAt(0)) + 1; 251 col = (col*26) + char2int(columnStr.charAt(1)); 252 } else { 253 Debug.log(Debug.ERROR, "Invalid Column Reference " + columnStr ); 254 } 255 256 257 return col; 258 } 259 260 /** 261 * Parses a column reference and returns its integer equivalent. (e.g. 262 * A=0, D=3, BA=27) 263 * 264 * @return an 0 based index to a column 265 */ row()266 private int row() { 267 char ch = parseString.charAt(index); 268 String rowStr = new String(); 269 int row = 0; 270 boolean status = true; 271 272 do { 273 rowStr += ch; 274 index++; 275 if(index>=parseString.length()) { 276 status = false; 277 } else { 278 ch = parseString.charAt(index); 279 } 280 } while(isDigit(ch) && status); 281 return Integer.parseInt(rowStr)-1; // Pexcel uses a 0 based index 282 } 283 284 /** 285 * A Cell Reference Encoder (It supports absolute and relative addressing) 286 * 287 * @param cellCoordinates 288 * @return A <code>Vector</code> of pexcel <code>Byte</code> 289 */ encodeCellCoordinates(String cellCoordinates)290 private byte[] encodeCellCoordinates(String cellCoordinates) { 291 int col = 0, row = 0; 292 int addressing = 0xC000; 293 294 index = 0; 295 parseString = cellCoordinates; 296 Debug.log(Debug.TRACE,"Encoding cell coordinates " + cellCoordinates); 297 if(cellCoordinates.charAt(index)=='$') { 298 addressing &= 0x8000; 299 index++; 300 } 301 col = column(); 302 if(cellCoordinates.charAt(index)=='$') { 303 addressing &= 0x4000; 304 index++; 305 } 306 row = row(); // Pexcel uses a 0 based index 307 row |= addressing; 308 byte tokenBytes[] = new byte[3]; 309 tokenBytes[0] = (byte)row; 310 tokenBytes[1] = (byte)(row>>8); 311 tokenBytes[2] = (byte)col; 312 return tokenBytes; 313 } 314 315 /** 316 * A name definition Encoder 317 * 318 * @param t <code>Token</code> to be encoded 319 * @return A <code>Vector</code> of pexcel <code>Byte</code> 320 */ nameDefinitionEncoder(Token t)321 private Vector nameDefinitionEncoder(Token t) { 322 323 Vector tmpByteArray = new Vector(); 324 325 String nameString = t.getValue(); 326 Debug.log(Debug.TRACE,"NameDefinitionEncoder : " + nameString); 327 tmpByteArray.add(new Byte((byte)t.getTokenID())); 328 Enumeration e = wb.getDefinedNames(); 329 DefinedName dn; 330 String name; 331 int definedNameIndex = 0; 332 do { 333 dn = (DefinedName)e.nextElement(); 334 name = dn.getName(); 335 Debug.log(Debug.TRACE,"Name pulled from DefinedName : " + name); 336 definedNameIndex++; 337 } while(!nameString.equalsIgnoreCase(name) && e.hasMoreElements()); 338 339 tmpByteArray.add(new Byte((byte)definedNameIndex)); 340 tmpByteArray.add(new Byte((byte)0x00)); 341 342 for(int i = 0;i < 12;i++) { 343 tmpByteArray.add(new Byte((byte)0x00)); 344 } 345 346 return tmpByteArray; 347 } 348 /** 349 * A Cell Reference Encoder. It supports absolute and relative addressing 350 * but not sheetnames. 351 * 352 * @param t <code>Token</code> to be encoded 353 * @return A <code>Vector</code> of pexcel <code>Byte</code> 354 */ cellRefEncoder(Token t)355 private Vector cellRefEncoder(Token t) { 356 357 Vector tmpByteArray = new Vector(); 358 359 tmpByteArray.add(new Byte((byte)t.getTokenID())); 360 byte cellRefBytes[] = encodeCellCoordinates(t.getValue()); 361 for(int i = 0;i < cellRefBytes.length;i++) { 362 tmpByteArray.add(new Byte(cellRefBytes[i])); 363 } 364 return tmpByteArray; 365 } 366 367 /** 368 * This function will find the sheetname index for a given String 369 * 370 * @param s 371 * @return A <code>Vector</code> of pexcel <code>Byte</code> 372 */ findSheetIndex(String s)373 private short findSheetIndex(String s) { 374 375 short sheetIndex = 0; 376 String savedName; 377 String sheetName; 378 if (s.startsWith("$")) { 379 sheetName = s.substring(1,s.length()); // Remove $ 380 } else { 381 sheetName = s.substring(0,s.length()); 382 } 383 Debug.log(Debug.TRACE,"Searching for Worksheet : " + sheetName); 384 Vector names = wb.getWorksheetNames(); 385 Enumeration e = names.elements(); 386 do { 387 savedName = (String) e.nextElement(); 388 sheetIndex++; 389 } while(!savedName.equalsIgnoreCase(sheetName) && e.hasMoreElements()); 390 391 Debug.log(Debug.TRACE,"Setting sheetindex to " + sheetIndex); 392 return (short)(sheetIndex-1); 393 } 394 395 /** 396 * A 3D Cell reference encoder 397 * 398 * @param t <code>Token</code> to be encoded 399 * @return A <code>Vector</code> of pexcel <code>Byte</code> 400 */ threeDCellRefEncoder(Token t)401 private Vector threeDCellRefEncoder(Token t) { 402 403 Vector tmpByteArray = new Vector(); 404 parseString = t.getValue(); 405 Debug.log(Debug.TRACE,"Encoding 3D Cell reference " + t); 406 tmpByteArray.add(new Byte((byte)t.getTokenID())); 407 tmpByteArray.add(new Byte((byte)0xFF)); 408 tmpByteArray.add(new Byte((byte)0xFF)); 409 for(int i = 0;i < 8;i++) { 410 tmpByteArray.add(new Byte((byte)0x00)); 411 } 412 413 String sheetRef = parseString.substring(0, parseString.indexOf('.') + 1); 414 if (sheetRef.indexOf(':')!=-1) { 415 sheetRef = parseString.substring(0, parseString.indexOf(':')); 416 short sheetNum1 = findSheetIndex(sheetRef); 417 sheetRef = parseString.substring(parseString.indexOf(':') + 1, parseString.length()); 418 short sheetNum2 = findSheetIndex(sheetRef); 419 tmpByteArray.add(new Byte((byte)sheetNum1)); 420 tmpByteArray.add(new Byte((byte)0x00)); 421 tmpByteArray.add(new Byte((byte)sheetNum2)); 422 tmpByteArray.add(new Byte((byte)0x00)); 423 } else { 424 sheetRef = parseString.substring(0, parseString.indexOf('.')); 425 short sheetNum = findSheetIndex(sheetRef); 426 tmpByteArray.add(new Byte((byte)sheetNum)); 427 tmpByteArray.add(new Byte((byte)0x00)); 428 tmpByteArray.add(new Byte((byte)sheetNum)); 429 tmpByteArray.add(new Byte((byte)0x00)); 430 } 431 String s = parseString.substring(parseString.indexOf('.') + 1, parseString.length()); 432 Debug.log(Debug.TRACE,"Parsing : " + s); 433 byte cellRefBytes[] = encodeCellCoordinates(s); 434 for(int i = 0;i < cellRefBytes.length;i++) { 435 tmpByteArray.add(new Byte(cellRefBytes[i])); 436 } 437 return tmpByteArray; 438 } 439 /** 440 * A 3D Area Reference Encoder. 441 * 442 * @param t <code>Token</code> to be encoded 443 * @return A <code>Vector</code> of pexcel <code>Byte</code> 444 */ threeDAreaRefEncoder(Token t)445 private Vector threeDAreaRefEncoder(Token t) { 446 447 Vector tmpByteArray = new Vector(); 448 parseString = t.getValue(); 449 Debug.log(Debug.TRACE,"Encoding 3D Area reference " + t); 450 tmpByteArray.add(new Byte((byte)t.getTokenID())); 451 tmpByteArray.add(new Byte((byte)0xFF)); 452 tmpByteArray.add(new Byte((byte)0xFF)); 453 for(int i = 0;i < 8;i++) { 454 tmpByteArray.add(new Byte((byte)0x00)); 455 } 456 457 String param1= parseString.substring(0, parseString.indexOf(':')); 458 String cellRef1 = param1.substring(parseString.indexOf('.') + 1, param1.length()); 459 String sheetRef1 = param1.substring(0, param1.indexOf('.')); 460 short sheetNum1 = findSheetIndex(sheetRef1); 461 462 String param2 = parseString.substring(parseString.indexOf(':') + 1, parseString.length()); 463 Debug.log(Debug.TRACE,"param2: " + param2); 464 String cellRef2 = param2.substring(param2.indexOf('.') + 1, param2.length()); 465 Debug.log(Debug.TRACE,"cellRef2: " + cellRef2); 466 467 if(param2.indexOf('.')==-1) { 468 tmpByteArray.add(new Byte((byte)sheetNum1)); 469 tmpByteArray.add(new Byte((byte)0x00)); 470 tmpByteArray.add(new Byte((byte)sheetNum1)); 471 tmpByteArray.add(new Byte((byte)0x00)); 472 } else { 473 String sheetRef2 = param2.substring(0, param2.indexOf('.')); 474 short sheetNum2 = findSheetIndex(sheetRef2); 475 tmpByteArray.add(new Byte((byte)sheetNum1)); 476 tmpByteArray.add(new Byte((byte)0x00)); 477 tmpByteArray.add(new Byte((byte)sheetNum2)); 478 tmpByteArray.add(new Byte((byte)0x00)); 479 } 480 481 byte cellRefBytes1[] = encodeCellCoordinates(cellRef1); 482 byte cellRefBytes2[] = encodeCellCoordinates(cellRef2); 483 484 tmpByteArray.add(new Byte(cellRefBytes1[0])); 485 tmpByteArray.add(new Byte(cellRefBytes1[1])); 486 487 tmpByteArray.add(new Byte(cellRefBytes2[0])); 488 tmpByteArray.add(new Byte(cellRefBytes2[1])); 489 490 tmpByteArray.add(new Byte(cellRefBytes1[2])); 491 tmpByteArray.add(new Byte(cellRefBytes2[2])); 492 493 return tmpByteArray; 494 } 495 496 /** 497 * A Cell Range Encoder. 498 * 499 * @param t <code>Token</code> to be encoded 500 * @return A <code>Vector</code> of pexcel <code>Byte</code> 501 */ areaRefEncoder(Token t)502 private Vector areaRefEncoder(Token t) { 503 504 Vector tmpByteArray = new Vector(); 505 506 tmpByteArray.add(new Byte((byte)t.getTokenID())); 507 String param = t.getValue(); 508 String cellRef1 = new String(); 509 String cellRef2 = new String(); 510 511 if(param.indexOf(':')==-1) { 512 Debug.log(Debug.ERROR, "Invalid Cell Range, could not find :"); 513 } else { 514 cellRef1 = param.substring(0, param.indexOf(':')); 515 cellRef2 = param.substring(param.indexOf(':') + 1, param.length()); 516 } 517 byte cellRefBytes1[] = encodeCellCoordinates(cellRef1); 518 byte cellRefBytes2[] = encodeCellCoordinates(cellRef2); 519 520 tmpByteArray.add(new Byte(cellRefBytes1[0])); 521 tmpByteArray.add(new Byte(cellRefBytes1[1])); 522 523 tmpByteArray.add(new Byte(cellRefBytes2[0])); 524 tmpByteArray.add(new Byte(cellRefBytes2[1])); 525 526 tmpByteArray.add(new Byte(cellRefBytes1[2])); 527 tmpByteArray.add(new Byte(cellRefBytes2[2])); 528 529 return tmpByteArray; 530 } 531 532 /** 533 * A Function Encoder. 534 * 535 * @param t <code>Token</code> to be encoded 536 * @return A <code>Vector</code> of pexcel <code>Byte</code> 537 */ functionEncoder(Token t)538 private Vector functionEncoder(Token t) { 539 Vector tmpByteArray = new Vector(); 540 541 int id = t.getTokenID(); 542 if(t.getTokenType()==ParseToken.TOKEN_FUNCTION_VARIABLE) { 543 tmpByteArray.add(new Byte((byte)TokenConstants.TFUNCVAR)); 544 tmpByteArray.add(new Byte((byte)t.getNumArgs())); 545 } else { 546 tmpByteArray.add(new Byte((byte)TokenConstants.TFUNC)); 547 } 548 549 tmpByteArray.add(new Byte((byte)id)); 550 tmpByteArray.add(new Byte((byte)(id>>8))); 551 return tmpByteArray; 552 } 553 554 } 555