1 /********************************************************************* 2 * 3 * Copyright (C) 2002 Andrew Khan 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 ***************************************************************************/ 19 20 package jxl.read.biff; 21 22 import jxl.common.Logger; 23 24 import jxl.WorkbookSettings; 25 import jxl.biff.IntegerHelper; 26 import jxl.biff.RecordData; 27 import jxl.biff.StringHelper; 28 29 /** 30 * A record containing the references to the various sheets (internal and 31 * external) referenced by formulas in this workbook 32 */ 33 public class SupbookRecord extends RecordData 34 { 35 /** 36 * The logger 37 */ 38 private static Logger logger = Logger.getLogger(SupbookRecord.class); 39 40 /** 41 * The type of this supbook record 42 */ 43 private Type type; 44 45 /** 46 * The number of sheets - internal & external supbooks only 47 */ 48 private int numSheets; 49 50 /** 51 * The name of the external file 52 */ 53 private String fileName; 54 55 /** 56 * The names of the external sheets 57 */ 58 private String[] sheetNames; 59 60 /** 61 * The type of supbook this refers to 62 */ 63 private static class Type {}; 64 65 66 public static final Type INTERNAL = new Type(); 67 public static final Type EXTERNAL = new Type(); 68 public static final Type ADDIN = new Type(); 69 public static final Type LINK = new Type(); 70 public static final Type UNKNOWN = new Type(); 71 72 /** 73 * Constructs this object from the raw data 74 * 75 * @param t the raw data 76 * @param ws the workbook settings 77 */ 78 SupbookRecord(Record t, WorkbookSettings ws) 79 { 80 super(t); 81 byte[] data = getRecord().getData(); 82 83 // First deduce the type 84 if (data.length == 4) 85 { 86 if (data[2] == 0x01 && data[3] == 0x04) 87 { 88 type = INTERNAL; 89 } 90 else if (data[2] == 0x01 && data[3] == 0x3a) 91 { 92 type = ADDIN; 93 } 94 else 95 { 96 type = UNKNOWN; 97 } 98 } 99 else if (data[0] == 0 && data[1] == 0) 100 { 101 type = LINK; 102 } 103 else 104 { 105 type = EXTERNAL; 106 } 107 108 if (type == INTERNAL) 109 { 110 numSheets = IntegerHelper.getInt(data[0], data[1]); 111 } 112 113 if (type == EXTERNAL) 114 { 115 readExternal(data, ws); 116 } 117 } 118 119 /** 120 * Reads the external data records 121 * 122 * @param data the data 123 * @param ws the workbook settings 124 */ 125 private void readExternal(byte[] data, WorkbookSettings ws) 126 { 127 numSheets = IntegerHelper.getInt(data[0], data[1]); 128 129 // subtract file name encoding from the length 130 int ln = IntegerHelper.getInt(data[2], data[3]) - 1; 131 int pos = 0; 132 133 if (data[4] == 0) 134 { 135 // non-unicode string 136 int encoding = data[5]; 137 pos = 6; 138 if (encoding == 0) 139 { 140 fileName = StringHelper.getString(data, ln, pos, ws); 141 pos += ln; 142 } 143 else 144 { 145 fileName = getEncodedFilename(data, ln, pos); 146 pos += ln; 147 } 148 } 149 else 150 { 151 // unicode string 152 int encoding = IntegerHelper.getInt(data[5], data[6]); 153 pos = 7; 154 if (encoding == 0) 155 { 156 fileName = StringHelper.getUnicodeString(data, ln, pos); 157 pos += ln * 2; 158 } 159 else 160 { 161 fileName = getUnicodeEncodedFilename(data, ln, pos); 162 pos += ln * 2; 163 } 164 } 165 166 sheetNames = new String[numSheets]; 167 168 for (int i = 0; i < sheetNames.length; i++) 169 { 170 ln = IntegerHelper.getInt(data[pos], data[pos + 1]); 171 172 if (data[pos + 2] == 0x0) 173 { 174 sheetNames[i] = StringHelper.getString(data, ln, pos + 3, ws); 175 pos += ln + 3; 176 } 177 else if (data[pos + 2] == 0x1) 178 { 179 sheetNames[i] = StringHelper.getUnicodeString(data, ln, pos + 3); 180 pos += ln * 2 + 3; 181 } 182 } 183 } 184 185 /** 186 * Gets the type of this supbook record 187 * 188 * @return the type of this supbook 189 */ 190 public Type getType() 191 { 192 return type; 193 } 194 195 /** 196 * Gets the number of sheets. This will only be non-zero for internal 197 * and external supbooks 198 * 199 * @return the number of sheets 200 */ 201 public int getNumberOfSheets() 202 { 203 return numSheets; 204 } 205 206 /** 207 * Gets the name of the external file 208 * 209 * @return the name of the external file 210 */ 211 public String getFileName() 212 { 213 return fileName; 214 } 215 216 /** 217 * Gets the name of the external sheet 218 * 219 * @param i the index of the external sheet 220 * @return the name of the sheet 221 */ 222 public String getSheetName(int i) 223 { 224 return sheetNames[i]; 225 } 226 227 /** 228 * Gets the data - used when copying a spreadsheet 229 * 230 * @return the raw external sheet data 231 */ 232 public byte[] getData() 233 { 234 return getRecord().getData(); 235 } 236 237 /** 238 * Gets the encoded string from the data array 239 * 240 * @param data the data 241 * @param ln length of the string 242 * @param pos the position in the array 243 * @return the string 244 */ 245 private String getEncodedFilename(byte[] data, int ln, int pos) 246 { 247 StringBuffer buf = new StringBuffer(); 248 int endpos = pos + ln; 249 while (pos < endpos) 250 { 251 char c = (char) data[pos]; 252 253 if (c == '\u0001') 254 { 255 // next character is a volume letter 256 pos++; 257 c = (char) data[pos]; 258 buf.append(c); 259 buf.append(":\\\\"); 260 } 261 else if (c == '\u0002') 262 { 263 // file is on the same volume 264 buf.append('\\'); 265 } 266 else if (c == '\u0003') 267 { 268 // down directory 269 buf.append('\\'); 270 } 271 else if (c == '\u0004') 272 { 273 // up directory 274 buf.append("..\\"); 275 } 276 else 277 { 278 // just add on the character 279 buf.append(c); 280 } 281 282 pos++; 283 } 284 285 return buf.toString(); 286 } 287 288 /** 289 * Gets the encoded string from the data array 290 * 291 * @param data the data 292 * @param ln length of the string 293 * @param pos the position in the array 294 * @return the string 295 */ 296 private String getUnicodeEncodedFilename(byte[] data, int ln, int pos) 297 { 298 StringBuffer buf = new StringBuffer(); 299 int endpos = pos + ln * 2; 300 while (pos < endpos) 301 { 302 char c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); 303 304 if (c == '\u0001') 305 { 306 // next character is a volume letter 307 pos += 2; 308 c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); 309 buf.append(c); 310 buf.append(":\\\\"); 311 } 312 else if (c == '\u0002') 313 { 314 // file is on the same volume 315 buf.append('\\'); 316 } 317 else if (c == '\u0003') 318 { 319 // down directory 320 buf.append('\\'); 321 } 322 else if (c == '\u0004') 323 { 324 // up directory 325 buf.append("..\\"); 326 } 327 else 328 { 329 // just add on the character 330 buf.append(c); 331 } 332 333 pos += 2; 334 } 335 336 return buf.toString(); 337 } 338 }