xref: /AOO41X/main/xmerge/source/pexcel/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenEncoder.java (revision 0c0e82a55dc5b7baa849907647dcb88a54f5f573)
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 onthe 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 varaible 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 it's integer equivalent. (eg.
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 it's integer equivalent. (eg.
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 }
556