1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id$ 20 */ 21 22 package org.apache.qetest.xsl; 23 24 import java.io.BufferedReader; 25 import java.io.File; 26 import java.io.FileReader; 27 import java.io.IOException; 28 import java.util.Properties; 29 import java.util.Vector; 30 31 import org.apache.qetest.Logger; 32 33 /** 34 * Simple services for getting lists of StylesheetDatalets. 35 * 36 * 37 * @author shane_curcuru@us.ibm.com 38 * @version $Id$ 39 */ 40 public abstract class StylesheetDataletManager // provide static services only 41 { 42 43 /** 44 * Token in xsl file denoting the text of an expected exception. 45 * Used in getInfoItem. 46 */ 47 public static final String INFOITEM_EXPECTED_EXCEPTION = "ExpectedException:"; 48 49 /** 50 * Get specified information about the datalet. 51 * 52 * <p>Note that different kinds of information may be read 53 * or discovered in different ways.</p> 54 * 55 * <p>Currently only implemented for 56 * INFOITEM_EXPECTED_EXCEPTION.</p> 57 * 58 * @param logger to report problems to 59 * @param datalet to get information about 60 * @param infoItem to get information about 61 * @return Vector of object(s) of appropriate type; 62 * or null if error 63 */ getInfoItem(Logger logger, StylesheetDatalet datalet, String infoItem)64 public static Vector getInfoItem(Logger logger, StylesheetDatalet datalet, String infoItem) 65 { 66 if ((null == datalet) || (null == infoItem)) 67 { 68 logger.logMsg(Logger.ERRORMSG, "getTestInfo called with null datalet or infoItem!"); 69 return null; 70 } 71 if (INFOITEM_EXPECTED_EXCEPTION.equals(infoItem)) 72 { 73 return getExpectedException(logger, datalet); 74 } 75 else 76 { 77 logger.logMsg(Logger.WARNINGMSG, "getTestInfo unsupported infoItem: " + infoItem); 78 return null; 79 } 80 } 81 82 83 /** 84 * Worker method to get expected exception text about a stylesheet. 85 * 86 * Currently parses the inputDir stylesheet for a line that contains 87 * EXPECTED_EXCEPTION inside an xsl comment, on a single line, and 88 * trims off the closing comment -->. 89 * Future work: allow options on datalet to specify some other 90 * expected data in another format - a whole Throwable object to 91 * compare to, or a stacktrace, etc. 92 * 93 * @author Shane Curcuru 94 * @param d Datalet that contains info about the exception 95 * @return Vector of Strings denoting toString of exception(s) 96 * we might expect - any one of them will pass; null if error 97 */ getExpectedException(Logger logger, StylesheetDatalet d)98 protected static Vector getExpectedException(Logger logger, StylesheetDatalet d) 99 { 100 final String EXPECTED_EXCEPTION_END = "-->"; 101 Vector v = null; 102 // Read in the testName file to see if it's expecting something 103 try 104 { 105 FileReader fr = new FileReader(d.inputName); 106 BufferedReader br = new BufferedReader(fr); 107 for (;;) 108 { 109 String inbuf = br.readLine(); 110 111 if (inbuf == null) 112 break; // end of file, break out and return 113 114 int idx = inbuf.indexOf(INFOITEM_EXPECTED_EXCEPTION); 115 116 if (idx < 0) 117 continue; // not on this line, keep going 118 119 // The expected exception.getMessage is the rest of the line... 120 String expExc = inbuf.substring(idx + INFOITEM_EXPECTED_EXCEPTION.length(), 121 inbuf.length()); 122 123 // ... less the trailing " -->" comment end; trimmed 124 int endComment = expExc.indexOf(EXPECTED_EXCEPTION_END); 125 if (endComment > -1) 126 expExc = expExc.substring(0, endComment).trim(); 127 else 128 expExc = expExc.trim(); 129 130 if (null == v) 131 v = new Vector(); // only create if needed 132 v.addElement(expExc); 133 134 // Continue reading the file for more potential 135 // expected exception strings - read them all 136 //@todo optimization: stop parsing after xx lines? 137 138 } // end for (;;) 139 } 140 catch (java.io.IOException ioe) 141 { 142 logger.logMsg(Logger.ERRORMSG, "getExpectedException() threw: " 143 + ioe.toString()); 144 return null; 145 } 146 return v; 147 } 148 149 150 /** '[' character, first char in first line of xsltmark fileList. */ 151 public static final String XSLTMARK_CHAR = "["; 152 153 /** '#' character, comment char in qetest fileList. */ 154 public static final String QETEST_COMMENT_CHAR = "#"; 155 156 /** 157 * Read in a file specifying a list of files to test. 158 * <p>File format is determined from first line in file, 159 * which is a little bit dangerous!</p> 160 * <p>If first line starts with '[', it's an xsltmark-style 161 * fileList, otherwise it's a qetest-style fileList.</p> 162 * 163 * @param logger to report problems to 164 * @param fileName String; name of the file 165 * @param desc description; caller's copy changed 166 * @param defaults default properties to potentially add to each datalet 167 * @return Vector of StylesheetDatalets, or null if error 168 */ readFileList(Logger logger, String fileName, String desc, Properties defaults)169 public static Vector readFileList(Logger logger, String fileName, String desc, Properties defaults) 170 { 171 // Verify the file is there 172 File f = new File(fileName); 173 if (!f.exists()) 174 { 175 logger.logMsg(Logger.ERRORMSG, "readFileList: " + fileName 176 + " does not exist!"); 177 return null; 178 } 179 180 BufferedReader br = null; 181 String line = null; 182 try 183 { 184 br = new BufferedReader(new FileReader(f)); 185 line = br.readLine(); // read just first line 186 } 187 catch (IOException ioe) 188 { 189 logger.logMsg(Logger.ERRORMSG, "readFileList: " + fileName 190 + " threw: " + ioe.toString()); 191 return null; 192 } 193 194 // Verify the first line 195 if (line == null) 196 { 197 logger.logMsg(Logger.ERRORMSG, "readFileList: " + fileName 198 + " appears to be blank!"); 199 return null; 200 } 201 202 // Determine which kind of fileList this is 203 // we support the 'native' org.apache.qetest format, and 204 // alternately the .ini file format used in xsltmark 205 Vector vec = null; 206 if (line.startsWith(XSLTMARK_CHAR)) 207 { 208 // This is an xsltmark .ini style file 209 vec = readXsltmarkFileList(logger, br, line, fileName, desc, defaults); 210 } 211 else if (line.startsWith(QETEST_COMMENT_CHAR)) 212 { 213 // This is a native qetest style file 214 vec = readQetestFileList(logger, br, line, fileName, desc, defaults); 215 } 216 else 217 { 218 logger.logMsg(Logger.WARNINGMSG, "readFileList: " + fileName 219 + " could not determine file type; assuming qetest!"); 220 vec = readQetestFileList(logger, br, line, fileName, desc, defaults); 221 } 222 223 if (vec.size() == 0) 224 { 225 logger.logMsg(Logger.ERRORMSG, "readFileList: " + fileName 226 + " did not have any non-comment lines!"); 227 return null; 228 } 229 return vec; 230 } 231 232 /** 233 * Read in a qetest fileList specifying a list of files to test. 234 * <p>File format is pretty simple:</p> 235 * <ul> 236 * <li># first line of comments is copied into desc</li> 237 * <li># beginning a line is a comment</li> 238 * <li># rest of lines are whitespace delimited filenames and options</li> 239 * <li>inputName xmlName outName goldName flavor options...</li> 240 * <li><b>Note:</b> see {@link StylesheetDatalet} for 241 * details on how the file lines are parsed!</li> 242 * </ul> 243 * <p>Most items are optional, but not having them may result 244 * in validation oddities. Future work would be to coordinate 245 * this with various Datalet's implementations of .load() so 246 * that Datalets can do better defaulting of non-provided 247 * items; or maybe so that a user can specific a default 'mask' 248 * of values to use for unspecified items.</p> 249 * 250 * @param logger to report problems to 251 * @param br BufferedReader to read from 252 * @param firstLine already read from br 253 * @param fileName String; name of the file 254 * @param desc to use of this file 255 * @param defaults default properties to potentially add to each datalet 256 * @return Vector of StylesheetDatalets, or null if error 257 */ readQetestFileList(Logger logger, BufferedReader br, String firstLine, String fileName, String desc, Properties defaults)258 protected static Vector readQetestFileList(Logger logger, BufferedReader br, 259 String firstLine, String fileName, 260 String desc, Properties defaults) 261 { 262 final String ABSOLUTE = "absolute"; 263 final String RELATIVE = "relative"; 264 265 Vector vec = new Vector(); 266 String line = firstLine; 267 // Check if the first line is a comment 268 if (line.startsWith(QETEST_COMMENT_CHAR)) 269 { 270 // Save it as the description 271 desc = line; 272 // Parse the next line 273 try 274 { 275 line = br.readLine(); 276 } 277 catch (IOException ioe) 278 { 279 logger.logMsg(Logger.ERRORMSG, "readQetestFileList: " 280 + fileName + " threw: " + ioe.toString()); 281 return null; 282 } 283 } 284 285 // Load each line into a StylesheetDatalet 286 for (;;) 287 { 288 // Skip any lines beginning with # comment char or that are blank 289 if ((!line.startsWith(QETEST_COMMENT_CHAR)) && (line.length() > 0)) 290 { 291 // Create a Datalet and initialize with the line's 292 // contents and default properties 293 StylesheetDatalet d = new StylesheetDatalet(line, defaults); 294 295 // Also pass over the global runId, if set 296 d.options.put("runId", defaults.getProperty("runId")); 297 298 //@todo Avoid spurious passes when output & gold not specified 299 // needs to detect when StylesheetDatalet doesn't 300 // properly have outputName and goldName set 301 302 // Add it to our vector 303 vec.addElement(d); 304 } 305 306 // Read next line and loop 307 try 308 { 309 line = br.readLine(); 310 } 311 catch (IOException ioe2) 312 { 313 // Just force us out of the loop; if we've already 314 // read part of the file, fine 315 logger.logMsg(Logger.WARNINGMSG, "readQetestFileList: " 316 + fileName + " threw: " + ioe2.toString()); 317 break; 318 } 319 320 if (line == null) 321 break; 322 } // end of for (;;) 323 return vec; 324 } 325 326 /** 327 * Read in an xsltmark fileList specifying a list of files to test. 328 * <p>File format is an .ini file like so:</p> 329 * <pre> 330 * [avts] 331 * input=db100.xml 332 * stylesheet=avts.xsl 333 * output=avts.out 334 * reference=avts.ref 335 * iterations=100 336 * </pre> 337 * <p>Note that additional attributes will be logged as warnings 338 * and will be ignored.</p> 339 * 340 * @param logger to report problems to 341 * @param br BufferedReader to read from 342 * @param firstLine already read from br 343 * @param fileName String; name of the file 344 * @param desc to use of this file 345 * @param defaults default properties to potentially add to each datalet 346 * @return Vector of StylesheetDatalets, or null if error 347 */ readXsltmarkFileList(Logger logger, BufferedReader br, String firstLine, String fileName, String desc, Properties defaults)348 protected static Vector readXsltmarkFileList(Logger logger, BufferedReader br, 349 String firstLine, String fileName, 350 String desc, Properties defaults) 351 { 352 Vector vec = new Vector(); 353 String line = firstLine; 354 // Parse each line and build a datalet 355 for (;;) 356 { 357 // If we're starting a section, parse the section to a datalet 358 if (line.startsWith(XSLTMARK_CHAR)) 359 { 360 StylesheetDatalet d = readXsltmarkDatalet(logger, br, line, fileName, desc, defaults); 361 362 // Also pass over the global runId, if set 363 d.options.put("runId", defaults.getProperty("runId")); 364 365 // Add datalet to our vector 366 vec.addElement(d); 367 } 368 // Skip blank lines 369 else if (line.length() == 0) 370 { 371 /* no-op */ 372 } 373 // Ooops, readXsltmarkDatalet didn't work right 374 else 375 { 376 logger.logMsg(Logger.WARNINGMSG, "readXsltmarkFileList parse error, unknown line: " 377 + line); 378 } 379 380 // Read next line and loop 381 try 382 { 383 line = br.readLine(); 384 } 385 catch (IOException ioe2) 386 { 387 // Just force us out of the loop; if we've already 388 // read part of the file, fine 389 logger.logMsg(Logger.WARNINGMSG, "readXsltmarkFileList: " 390 + fileName + " threw: " + ioe2.toString()); 391 break; 392 } 393 394 if (line == null) 395 break; 396 } // end of for (;;) 397 return vec; 398 } 399 400 /** 401 * Read in an xsltmark fileList specifying a list of files to test. 402 * <p>File format is an .ini file</p> 403 * 404 * @param logger to report problems to 405 * @param br BufferedReader to read from 406 * @param firstLine already read from br 407 * @param fileName String; name of the file 408 * @param desc to use of this file 409 * @param defaults default properties to potentially add to each datalet 410 * @return StylesheetDatalet with appropriate data, or null if error 411 */ readXsltmarkDatalet(Logger logger, BufferedReader br, String firstLine, String fileName, String desc, Properties defaults)412 private static StylesheetDatalet readXsltmarkDatalet(Logger logger, BufferedReader br, 413 String firstLine, String fileName, 414 String desc, Properties defaults) 415 { 416 final String STYLESHEET_MARKER = "stylesheet="; 417 final String INPUT_MARKER = "input="; 418 final String OUTPUT_MARKER = "output="; 419 final String REFERENCE_MARKER = "reference="; 420 final String ITERATIONS_MARKER = "iterations="; 421 422 String line = firstLine; 423 StylesheetDatalet d = new StylesheetDatalet(); 424 425 // Also pass over the default properties as well 426 d.options = new Properties(defaults); 427 428 // Parse lines throughout the section to build the datalet 429 for (;;) 430 { 431 // Each .ini file line starts with name of item to fill 432 if (line.startsWith(STYLESHEET_MARKER)) 433 { 434 d.inputName = line.substring(STYLESHEET_MARKER.length()); 435 } 436 else if (line.startsWith(INPUT_MARKER)) 437 { 438 d.xmlName = line.substring(INPUT_MARKER.length()); 439 } 440 else if (line.startsWith(OUTPUT_MARKER)) 441 { 442 d.outputName = line.substring(OUTPUT_MARKER.length()); 443 } 444 else if (line.startsWith(REFERENCE_MARKER)) 445 { 446 d.goldName = line.substring(REFERENCE_MARKER.length()); 447 } 448 else if (line.startsWith(XSLTMARK_CHAR)) 449 { 450 d.setDescription(line); 451 } 452 else if (line.startsWith(ITERATIONS_MARKER)) 453 { 454 d.options.put("iterations", line.substring(ITERATIONS_MARKER.length())); 455 } 456 else if (line.length() == 0) 457 { 458 // Blank lines mean end-of-section; return datalet 459 // This is the primary exit point for this method 460 return d; 461 } 462 else 463 { 464 logger.logMsg(Logger.WARNINGMSG, "readXsltmarkDatalet, unknown line: " 465 + line); 466 } 467 468 // Read next line and loop 469 try 470 { 471 line = br.readLine(); 472 } 473 catch (IOException ioe2) 474 { 475 // Just force us out of the loop; if we've already 476 // read part of the file, fine 477 logger.logMsg(Logger.WARNINGMSG, "readXsltmarkDatalet: " 478 + fileName + " threw: " + ioe2.toString()); 479 break; 480 } 481 482 if (line == null) 483 break; 484 } // end of for (;;) 485 logger.logMsg(Logger.ERRORMSG, "readXsltmarkDatalet: " + fileName 486 + " no data found!"); 487 return null; 488 } 489 } 490