1 /* 2 * $Id$ 3 * 4 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.interview; 28 29 import com.sun.javatest.util.Properties; 30 31 import java.io.*; 32 import java.nio.charset.StandardCharsets; 33 import java.text.MessageFormat; 34 import java.util.*; 35 36 /** 37 * An API (with a basic front-end application) for generating HTML printouts 38 * of an {@link Interview interview}. 39 */ 40 public class WizPrint 41 { 42 /** 43 * This exception is to report problems that occur with command line arguments. 44 */ 45 public static class BadArgs extends Exception 46 { 47 /** 48 * Create a Fault. 49 * @param i18n A resource bundle in which to find the detail message. 50 * @param s The key for the detail message. 51 */ 52 public BadArgs(ResourceBundle i18n, String s) { 53 super(i18n.getString(s)); 54 } 55 56 /** 57 * Create a Fault. 58 * @param i18n A resource bundle in which to find the detail message. 59 * @param s The key for the detail message. 60 * @param o An argument to be formatted with the detail message by 61 * {@link java.text.MessageFormat#format} 62 */ 63 public BadArgs(ResourceBundle i18n, String s, Object o) { 64 super(MessageFormat.format(i18n.getString(s), new Object[] {o})); 65 } 66 67 /** 68 * Create a Fault. 69 * @param i18n A resource bundle in which to find the detail message. 70 * @param s The key for the detail message. 71 * @param o An array of arguments to be formatted with the detail message by 72 * {@link java.text.MessageFormat#format} 73 */ 74 public BadArgs(ResourceBundle i18n, String s, Object[] o) { 75 super(MessageFormat.format(i18n.getString(s), o)); 76 } 77 } 78 79 /** 80 * This exception is to report problems that occur while updating an interview. 81 */ 82 public static class Fault extends Exception 83 { 84 /** 85 * Create a Fault. 86 * @param i18n A resource bundle in which to find the detail message. 87 * @param s The key for the detail message. 88 */ 89 public Fault(ResourceBundle i18n, String s) { 90 super(i18n.getString(s)); 91 } 92 93 /** 94 * Create a Fault. 95 * @param i18n A resource bundle in which to find the detail message. 96 * @param s The key for the detail message. 97 * @param o An argument to be formatted with the detail message by 98 * {@link java.text.MessageFormat#format} 99 */ 100 public Fault(ResourceBundle i18n, String s, Object o) { 101 super(MessageFormat.format(i18n.getString(s), new Object[] {o})); 102 } 103 104 /** 105 * Create a Fault. 106 * @param i18n A resource bundle in which to find the detail message. 107 * @param s The key for the detail message. 108 * @param o An array of arguments to be formatted with the detail message by 109 * {@link java.text.MessageFormat#format} 110 */ 111 public Fault(ResourceBundle i18n, String s, Object[] o) { 112 super(MessageFormat.format(i18n.getString(s), o)); 113 } 114 } 115 116 /** 117 * Write a short description of the command line syntax and options to System.err. 118 */ 119 public static void usage() { 120 String prog = System.getProperty("program"); 121 if (prog == null) 122 prog = formatI18N("wp.prog", WizPrint.class.getName()); 123 String msg = formatI18N("wp.usage", prog); 124 125 boolean newline = true; 126 for (int i = 0; i < msg.length(); i++) { 127 char c = msg.charAt(i); 128 if (c == '\n') { 129 System.err.println(); 130 newline = true; 131 } 132 else { 133 System.err.print(c); 134 newline = false; 135 } 136 } 137 if (!newline) 138 System.err.println(); 139 } 140 141 /** 142 * Simple command-line front-end to the facilities of the API. 143 * @param args Command line arguments. 144 * @see #usage 145 */ 146 public static void main(String[] args) { 147 try { 148 boolean path = false;; 149 String interviewClassName = null; 150 File interviewFile = null; 151 File outFileName = null; 152 153 for (int i = 0; i < args.length; i++) { 154 if (args[i].equals("-o") && i + 1 < args.length) 155 outFileName = new File(args[++i]); 156 else if (args[i].equals("-path")) 157 path = true; 158 else if (args[i].equals("-all")) 159 path = false; 160 else if (args[i].startsWith("-")) 161 throw new BadArgs(i18n, "wp.badArg", args[i]); 162 else if (i == args.length - 1) { 163 if (args[i].endsWith(".jti")) 164 interviewFile = new File(args[i]); 165 else 166 interviewClassName = args[i]; 167 } 168 else 169 throw new BadArgs(i18n, "wp.badArg", args[i]); 170 } 171 172 Map<String, String> interviewData = null; 173 174 if (interviewFile != null) { 175 try { 176 InputStream in = new BufferedInputStream(new FileInputStream(interviewFile)); 177 interviewData = Properties.load(in); 178 interviewClassName = interviewData.get("INTERVIEW"); 179 } 180 catch (FileNotFoundException e) { 181 throw new Fault(i18n, "wp.cantFindFile", interviewFile); 182 } 183 catch (IOException e) { 184 throw new Fault(i18n, "wp.cantReadFile", new Object[] { interviewFile, e }); 185 } 186 } 187 188 if (interviewClassName == null) 189 throw new BadArgs(i18n, "wp.noInterview"); 190 191 if (outFileName == null) { 192 // try and create a default 193 if (interviewFile != null) { 194 String ip = interviewFile.getPath(); 195 int dot = ip.lastIndexOf("."); 196 if (dot != -1) 197 outFileName = new File(ip.substring(0, dot) + ".html"); 198 } 199 if (outFileName == null) 200 throw new BadArgs(i18n, "wp.noOutput"); 201 } 202 203 Class<?> ic = Class.forName(interviewClassName, true, ClassLoader.getSystemClassLoader()); 204 Interview interview = (Interview)(ic.newInstance()); 205 Question[] questions; 206 207 if (interviewData != null) 208 interview.load(interviewData); 209 210 if (path) { 211 questions = interview.getPath(); 212 } 213 else { 214 // enumerate questions, sort on tag 215 SortedVector v = new SortedVector(); 216 for (Iterator<Question> iter = interview.getQuestions().iterator(); iter.hasNext(); ) { 217 Question q = iter.next(); 218 v.insert(q); 219 } 220 questions = new Question[v.size()]; 221 v.copyInto(questions); 222 } 223 224 try { 225 Writer out = new OutputStreamWriter(new FileOutputStream(outFileName), StandardCharsets.UTF_8); 226 WizPrint wp = new WizPrint(interview, questions); 227 wp.setShowTags(!path); 228 wp.setShowResponses(path); 229 wp.setShowResponseTypes(!path); 230 wp.write(out); 231 } 232 catch (IOException e) { 233 throw new Fault(i18n, "wp.cantWriteFile", new Object[] { outFileName, e }); 234 } 235 } 236 catch (BadArgs e) { 237 System.err.println(formatI18N("wp.error", e.getMessage())); 238 usage(); 239 System.exit(1); 240 } 241 catch (Fault e) { 242 System.err.println(e.getMessage()); 243 System.exit(2); 244 } 245 catch (Throwable e) { 246 e.printStackTrace(); 247 System.exit(3); 248 } 249 } 250 251 /** 252 * Create an object for printing the current set of questions from an interview. 253 * @param interview The parent interview which contains the questions. 254 */ 255 public WizPrint(Interview interview) { 256 this(interview, interview.getPath()); 257 } 258 259 /** 260 * Create an object for printing a set of questions from an interview. 261 * @param interview The parent interview which contains the questions. 262 * @param questions The selected set of questions to be printed. 263 */ 264 public WizPrint(Interview interview, Question[] questions) { 265 this.interview = interview; 266 this.questions = questions; 267 } 268 269 /** 270 * Determine whether or not responses should be shown when the 271 * interview is "printed" to HTML. 272 * @return true if responses should be shown 273 * @see #setShowResponses 274 */ 275 public boolean getShowResponses() { 276 return showResponses; 277 } 278 279 /** 280 * Specify whether or not responses should be shown when the 281 * interview is "printed" to HTML. 282 * @param showResponses should be true if responses should be shown 283 * @see #getShowResponses 284 */ 285 public void setShowResponses(boolean showResponses) { 286 this.showResponses = showResponses; 287 } 288 289 /** 290 * Determine whether or not the types of responses should be shown 291 * when the interview is "printed" to HTML. 292 * @return true if the types of responses should be shown 293 * @see #setShowResponseTypes 294 */ 295 public boolean getShowResponseTypes() { 296 return showResponseTypes; 297 } 298 299 /** 300 * Specify whether or not the types of responses should be shown 301 * when the interview is "printed" to HTML. 302 * @param showResponseTypes should be true if the types of responses should be shown 303 * @see #getShowResponseTypes 304 */ 305 public void setShowResponseTypes(boolean showResponseTypes) { 306 this.showResponseTypes = showResponseTypes; 307 } 308 309 /** 310 * Determine whether or not question tags should be shown 311 * when the interview is "printed" to HTML. 312 * @return true if the questions' tags should be shown 313 * @see #setShowTags 314 */ 315 public boolean getShowTags() { 316 return showTags; 317 } 318 319 /** 320 * Specify whether or not question tags should be shown 321 * when the interview is "printed" to HTML. 322 * @param showTags should be true if the questions' tags should be shown 323 * @see #getShowTags 324 */ 325 public void setShowTags(boolean showTags) { 326 this.showTags = showTags; 327 } 328 329 /** 330 * Write the selected questions to the given stream, as a complete 331 * HTML document. The stream is closed after the writing is complete. 332 * @param o the Writer to which to write the specified information 333 * about an interview 334 * @throws IOException if there are problems writing to the given Writer 335 */ 336 public void write(Writer o) throws IOException { 337 try { 338 setWriter(o); 339 startTag(DOCTYPE); 340 newLine(); 341 startTag(HTML); 342 newLine(); 343 startTag(HEAD); 344 writeTag(TITLE, interview.getTitle()); 345 endTag(HEAD); 346 newLine(); 347 startTag(BODY); 348 writeTag(H1, interview.getTitle()); 349 newLine(); 350 writeIndex(); 351 writeQuestions(); 352 endTag(BODY); 353 endTag(HTML); 354 newLine(); 355 } 356 finally { 357 out.flush(); 358 out.close(); 359 } 360 } 361 362 /** 363 * Write an index to the set of questions. 364 */ 365 private void writeIndex() throws IOException { 366 startTag(UL); 367 newLine(); 368 for (int i = 0; i < questions.length; i++) { 369 Question q = questions[i]; 370 startTag(LI); 371 startTag(A); 372 writeAttr(HREF, "#" + q.getTag()); 373 writeText(q.getSummary()); 374 endTag(A); 375 newLine(); 376 } 377 endTag(UL); 378 newLine(); 379 newLine(); 380 } 381 382 /** 383 * Write the body of the document, containing the questions. 384 */ 385 private void writeQuestions() throws IOException { 386 startTag(HR); 387 for (int i = 0; i < questions.length; i++) { 388 if (i > 0) { 389 startTag(HR); 390 writeAttr(STYLE, TEXT_LEFT); 391 writeAttr(WIDTH, "25%"); 392 } 393 394 Question q = questions[i]; 395 396 String tag = q.getTag(); 397 if (tag != null) { 398 startTag(A); 399 writeAttr(NAME, q.getTag()); 400 endTag(A); 401 newLine(); 402 } 403 404 writeTag(H3, q.getSummary()); 405 newLine(); 406 407 if (showTags) { 408 startTag(P); 409 startTag(I); 410 writeI18N("wp.tag"); 411 if (tag == null) 412 writeTag(I, "null"); // I18N?? 413 else 414 writeTag(B, tag); 415 endTag(I); 416 endTag(P); 417 newLine(); 418 } 419 420 if (q instanceof ErrorQuestion) { 421 startTag(P); 422 startTag(FONT); 423 writeAttr(SIZE, "+1"); 424 writeText(q.getText()); 425 endTag(FONT); 426 endTag(P); 427 } 428 else { 429 startTag(P); 430 writeText(q.getText()); 431 endTag(P); 432 newLine(); 433 if (showResponseTypes) 434 writeResponseType(q); 435 if (showResponses) 436 writeResponse(q); 437 } 438 } 439 } 440 441 /** 442 * Write the response to a question. 443 * @param q The question whose response is to be printed. 444 */ 445 private void writeResponse(Question q) throws IOException { 446 if (q instanceof ChoiceArrayQuestion) { 447 ChoiceArrayQuestion caq = (ChoiceArrayQuestion)q; 448 writeResponse(caq.getValue(), caq.getChoices(), caq.getDisplayChoices()); 449 } 450 else if (q instanceof ChoiceQuestion) { 451 ChoiceQuestion cq = (ChoiceQuestion)q; 452 if (cq.getChoices() == cq.getDisplayChoices()) 453 writeResponse(cq.getValue()); 454 else 455 writeResponse(cq.getValue(), cq.getDisplayValue()); 456 } 457 else if (q instanceof ErrorQuestion) { 458 // no response 459 } 460 else if (q instanceof FileListQuestion) { 461 FileListQuestion fq = (FileListQuestion)q; 462 File[] f = fq.getValue(); 463 writeResponse(filesToStrings(f)); 464 } 465 else if (q instanceof FileQuestion) { 466 FileQuestion fq = (FileQuestion)q; 467 File f = fq.getValue(); 468 writeResponse(f == null ? null : f.getPath()); 469 } 470 else if (q instanceof FinalQuestion) { 471 // no response 472 } 473 else if (q instanceof FloatQuestion) { 474 FloatQuestion fq = (FloatQuestion)q; 475 writeResponse(fq.getStringValue()); 476 } 477 else if (q instanceof InetAddressQuestion) { 478 InetAddressQuestion iq = (InetAddressQuestion)q; 479 writeResponse(iq.getStringValue()); 480 } 481 else if (q instanceof IntQuestion) { 482 IntQuestion iq = (IntQuestion)q; 483 writeResponse(iq.getStringValue()); 484 } 485 else if (q instanceof NullQuestion) { 486 // no response 487 } 488 else if (q instanceof PropertiesQuestion) { 489 PropertiesQuestion pq = (PropertiesQuestion)q; 490 writeResponse(pq); 491 } 492 else if (q instanceof StringQuestion) { 493 StringQuestion sq = (StringQuestion)q; 494 writeResponse(sq.getValue()); 495 } 496 else if (q instanceof StringListQuestion) { 497 StringListQuestion sq = (StringListQuestion)q; 498 writeResponse(sq.getValue()); 499 } 500 else if (q instanceof TreeQuestion) { 501 TreeQuestion tq = (TreeQuestion)q; 502 String[] nodes = tq.getValue(); 503 if (nodes == null || nodes.length == 0) 504 writeResponse(i18n.getString("wp.all")); 505 else 506 writeResponse(nodes); 507 } 508 else { 509 writeResponse(q.getStringValue()); 510 } 511 } 512 513 /** 514 * Write a response. 515 * @param s The text of the response. 516 */ 517 private void writeResponse(String s) throws IOException { 518 startTag(P); 519 startTag(I); 520 writeI18N("wp.response"); 521 if (s == null) 522 writeI18N("wp.noResponse"); 523 else 524 writeTag(B, s); 525 endTag(I); 526 endTag(P); 527 newLine(); 528 newLine(); 529 } 530 531 /** 532 * Write a response. 533 * @param response The text of the response. 534 * @param displayText The display text of the response. 535 */ 536 private void writeResponse(String response, String displayText) throws IOException { 537 startTag(P); 538 startTag(I); 539 writeI18N("wp.response"); 540 if (response == null) 541 writeI18N("wp.noResponse"); 542 else { 543 writeTag(B, response); 544 if (displayText != null) { 545 writeText(" "); 546 writeI18N("wp.display", displayText); 547 } 548 } 549 endTag(I); 550 endTag(P); 551 newLine(); 552 newLine(); 553 } 554 555 /** 556 * Write a response based on a set of named boolean values 557 * @param values An array of boolean values. 558 * @param choices An array of matching names, one per boolean. 559 */ 560 private void writeResponse(boolean[] values, String[] choices, String[] displayChoices) throws IOException { 561 startTag(P); 562 startTag(I); 563 writeI18N("wp.response"); 564 if (values == null) 565 writeI18N("wp.noResponse"); 566 else { 567 for (int i = 0; i < values.length; i++) { 568 if (i > 0) 569 writeI18N("wp.listSep"); 570 startTag(B); 571 /* 572 if (values[i]) 573 writeText(choices[i]); 574 else 575 writeTag(STRIKE, choices[i]); 576 */ 577 if (values[i] == false) 578 startTag(STRIKE); 579 580 writeText(choices[i]); 581 if (displayChoices != null && !equal(choices[i], displayChoices[i])) { 582 writeText(" "); 583 writeI18N("wp.display", displayChoices[i]); 584 } 585 586 if (values[i] == false) 587 endTag(STRIKE); 588 589 endTag(B); 590 } 591 } 592 endTag(I); 593 endTag(P); 594 newLine(); 595 newLine(); 596 } 597 598 /** 599 * Write a response list 600 * @param responses The text of the response. 601 */ 602 private void writeResponse(String[] responses) throws IOException { 603 startTag(P); 604 startTag(I); 605 writeI18N("wp.response"); 606 if (responses == null || responses.length == 0 607 || (responses.length == 1 && responses[0].isEmpty())) { 608 writeI18N("wp.noResponse"); 609 endTag(I); 610 endTag(P); 611 } else { 612 endTag(I); 613 endTag(P); 614 startTag(UL); 615 for (int i = 0; i < responses.length; i++) { 616 if (responses[i] != null && !responses[i].isEmpty()) { 617 startTag(LI); 618 startTag(B); 619 startTag(I); 620 writeText(responses[i]); 621 endTag(I); 622 endTag(B); 623 } 624 } 625 endTag(UL); 626 } 627 newLine(); 628 newLine(); 629 } 630 631 /** 632 * Write the response output. This methods is not generic like the others 633 * because of the rich and optional API for this particular question. 634 */ 635 private void writeResponse(PropertiesQuestion pq) throws IOException { 636 String[] groups = pq.getGroups(); 637 String[] headers = new String[] {pq.getKeyHeaderName(), 638 pq.getValueHeaderName()}; 639 String[][] nullGroup = pq.getGroup(null); 640 if (nullGroup != null && nullGroup.length != 0) 641 writePQTable(headers, nullGroup); 642 else if (groups == null || groups.length == 0) { 643 // no properties for this question it seems 644 writeI18N("wp.noResponse"); 645 } 646 else { 647 // fall through 648 } 649 650 if (groups != null) 651 for (int i = 0; i < groups.length; i++) { 652 // heading 653 startTag(BR); 654 startTag(B); 655 writeText(pq.getGroupDisplayName(groups[i])); 656 endTag(B); 657 658 // data 659 startTag(BR); 660 writePQTable(headers, pq.getGroup(groups[i])); 661 startTag(P); 662 } // for 663 } 664 665 private void writePQTable(String[] headers, String[][] values) 666 throws IOException { 667 if (values == null || values.length == 0) 668 return; 669 670 startTag(TABLE); 671 writeAttr("border", "2"); 672 writeAttr("title", 673 i18n.getString("wp.table.title")); 674 writeAttr("summary", 675 i18n.getString("wp.table.summ")); 676 startTag(TR); 677 678 // headers 679 startTag(TH); 680 writeAttr("align", "left"); 681 writeAttr("scope", "col"); // 508 682 writeText(headers[0]); 683 startTag(TH); 684 writeAttr("align", "left"); 685 writeAttr("scope", "col"); // 508 686 writeText(headers[1]); 687 688 for (int i = 0; i < values.length; i++) { 689 startTag(TR); 690 startTag(TD); 691 writeText(values[i][0]); 692 startTag(TD); 693 writeText(values[i][1]); 694 endTag(TR); 695 } // for 696 697 endTag(TABLE); 698 } 699 700 /** 701 * Write the response to a question. 702 * @param q The question whose response is to be printed. 703 */ 704 private void writeResponseType(Question q) throws IOException { 705 if (q instanceof ChoiceArrayQuestion) { 706 ChoiceArrayQuestion cq = (ChoiceArrayQuestion)q; 707 StringBuffer sb = new StringBuffer(); 708 sb.append(i18n.getString("wp.type.chooseAny")); 709 String[] choices = cq.getChoices(); 710 for (int i = 0; i < choices.length; i++) { 711 if (i > 0) 712 sb.append(i18n.getString("wp.listSep")); 713 sb.append(choices[i] == null ? i18n.getString("wp.unset") : choices[i]); 714 } 715 writeResponseType(sb.toString()); 716 } 717 else if (q instanceof ChoiceQuestion) { 718 ChoiceQuestion cq = (ChoiceQuestion)q; 719 StringBuffer sb = new StringBuffer(); 720 sb.append(i18n.getString("wp.type.chooseOne")); 721 String[] choices = cq.getChoices(); 722 for (int i = 0; i < choices.length; i++) { 723 if (i > 0) 724 sb.append(i18n.getString("wp.listSep")); 725 sb.append(choices[i] == null ? i18n.getString("wp.unset") : choices[i]); 726 } 727 writeResponseType(sb.toString()); 728 } 729 else if (q instanceof ErrorQuestion) { 730 // no response 731 } 732 else if (q instanceof FileQuestion) { 733 writeResponseType(i18n.getString("wp.type.file")); 734 } 735 else if (q instanceof FileListQuestion) { 736 writeResponseType(i18n.getString("wp.type.fileList")); 737 } 738 else if (q instanceof FinalQuestion) { 739 // no response 740 } 741 else if (q instanceof FloatQuestion) { 742 FloatQuestion fq = (FloatQuestion)q; 743 float lwb = fq.getLowerBound(); 744 float upb = fq.getUpperBound(); 745 writeResponseType(formatI18N("wp.type.float", 746 new Object[] { 747 new Integer(lwb == Float.MIN_VALUE ? 0 : 1), 748 new Float(lwb), 749 new Integer(upb == Float.MAX_VALUE ? 0 : 1), 750 new Float(upb) })); 751 } 752 else if (q instanceof InetAddressQuestion) { 753 writeResponseType(i18n.getString("wp.type.inetAddress")); 754 } 755 else if (q instanceof IntQuestion) { 756 IntQuestion iq = (IntQuestion)q; 757 int lwb = iq.getLowerBound(); 758 int upb = iq.getUpperBound(); 759 writeResponseType(formatI18N("wp.type.int", 760 new Object[] { 761 new Integer(lwb == Integer.MIN_VALUE ? 0 : 1), 762 new Integer(lwb), 763 new Integer(upb == Integer.MAX_VALUE ? 0 : 1), 764 new Integer(upb) })); 765 } 766 else if (q instanceof NullQuestion) { 767 // no response 768 } 769 else if (q instanceof StringQuestion) { 770 writeResponseType(i18n.getString("wp.type.string")); 771 } 772 else if (q instanceof StringListQuestion) { 773 writeResponseType(i18n.getString("wp.type.stringList")); 774 } 775 else if (q instanceof TreeQuestion) { 776 writeResponseType(i18n.getString("wp.type.tree")); 777 } 778 else { 779 startTag(P); 780 writeTag(I, "unknown type of question; cannot determine response type"); 781 endTag(P); 782 } 783 } 784 785 /** 786 * Write a response. 787 * @param s The text of the response. 788 */ 789 private void writeResponseType(String s) throws IOException { 790 startTag(P); 791 startTag(I); 792 if (showResponseTypes && showResponses) 793 writeI18N("wp.responseType"); // is this being too clever? 794 else 795 writeI18N("wp.response"); 796 startTag(B); 797 writeText(s); 798 endTag(B); 799 endTag(I); 800 endTag(P); 801 newLine(); 802 newLine(); 803 } 804 805 private void writeI18N(String key) throws IOException { 806 writeText(i18n.getString(key)); 807 } 808 809 private void writeI18N(String key, Object arg) throws IOException { 810 String s = formatI18N(key, arg); 811 writeText(s); 812 } 813 814 /** 815 * Convert a set of files to their string paths 816 */ 817 private String[] filesToStrings(File[] f) { 818 if (f == null) 819 return null; 820 String[] s = new String[f.length]; 821 for (int i = 0; i < s.length; i++) 822 s[i] = f[i].getPath(); 823 return s; 824 } 825 826 /** 827 * Write a newline. 828 */ 829 private void newLine() throws IOException { 830 if (state == IN_TAG) { 831 out.write('>'); 832 state = IN_BODY; 833 } 834 835 out.newLine(); 836 } 837 838 /** 839 * Write an opening tag. 840 * @param t The tag to be written 841 */ 842 private void startTag(String t) throws IOException { 843 if (state == IN_TAG) 844 out.write('>'); 845 846 out.write('<'); 847 out.write(t); 848 state = IN_TAG; 849 } 850 851 /** 852 * Write an closing tag. 853 * @param t The tag to be written 854 */ 855 private void endTag(String t) throws IOException { 856 if (state == IN_TAG) 857 out.write('>'); 858 859 out.write("</"); 860 out.write(t); 861 out.write('>'); 862 state = IN_BODY; 863 } 864 865 private void writeAttr(String name, String value) throws IOException { 866 if (state != IN_TAG) 867 throw new IllegalStateException(); 868 869 out.write(" "); 870 out.write(name); 871 out.write("="); 872 boolean alpha = true; 873 for (int i = 0; i < value.length() && alpha; i++) 874 alpha = Character.isLetter(value.charAt(i)); 875 if (!alpha) 876 out.write("\""); 877 out.write(value); 878 if (!alpha) 879 out.write("\""); 880 } 881 882 883 /** 884 * Write a string between opening and closing tags. 885 * @param t The enclosing tag 886 * @param s The enclosed text 887 */ 888 private void writeTag(String t, String s) throws IOException { 889 startTag(t); 890 writeText(s); 891 endTag(t); 892 } 893 894 895 /** 896 * Write body text, applying any necessary escapes. 897 */ 898 private void writeText(String s) throws IOException { 899 if (state == IN_TAG) { 900 out.write(">"); 901 state = IN_BODY; 902 } 903 904 if (s == null) { 905 out.write("<i>"); 906 out.write(i18n.getString("wp.null")); 907 out.write("</i>"); 908 } 909 else { 910 for (int i = 0; i < s.length(); i++) { 911 char c = s.charAt(i); 912 switch (c) { 913 case '\n': 914 out.write("<br>"); 915 break; 916 case '<': 917 out.write("<"); 918 break; 919 case '>': 920 out.write(">"); 921 break; 922 case '&': 923 out.write("&"); 924 break; 925 default: 926 out.write(c); 927 } 928 } 929 } 930 } 931 932 private static String formatI18N(String key, Object arg) { 933 return MessageFormat.format(i18n.getString(key), new Object[] { arg }); 934 } 935 936 private static String formatI18N(String key, Object[] args) { 937 return MessageFormat.format(i18n.getString(key), args); 938 } 939 940 private void setWriter(Writer o) { 941 if (out instanceof BufferedWriter) 942 out = (BufferedWriter)o; 943 else 944 out = new BufferedWriter(o); 945 } 946 947 private static boolean equal(String s1, String s2) { 948 return (s1 == null ? s2 == null : s1.equals(s2)); 949 } 950 951 private Interview interview; 952 private Question[] questions; 953 private BufferedWriter out; 954 private boolean showResponses; 955 private boolean showResponseTypes; 956 private boolean showTags; 957 958 private int state; 959 private static final int IN_TAG = 1; 960 private static final int IN_BODY = 2; 961 962 private static final ResourceBundle i18n = ResourceBundle.getBundle("com.sun.interview.i18n"); 963 964 private static final String DOCTYPE = "!DOCTYPE HTML"; 965 966 private static final String A = "a"; 967 private static final String B = "b"; 968 private static final String BODY = "body"; 969 private static final String BR = "br"; 970 private static final String FONT = "font"; 971 private static final String H1 = "h1"; 972 private static final String H3 = "h3"; 973 private static final String HEAD = "head"; 974 private static final String HR = "hr"; 975 private static final String HREF = "href"; 976 private static final String HTML = "html"; 977 private static final String I = "i"; 978 private static final String TEXT_LEFT = "text-align:left;"; 979 private static final String LI = "li"; 980 private static final String NAME = "name"; 981 private static final String P = "p"; 982 private static final String SIZE = "size"; 983 private static final String STRIKE = "strike"; 984 private static final String TABLE = "table"; 985 private static final String TITLE = "title"; 986 private static final String TD = "td"; 987 private static final String TH = "th"; 988 private static final String TR = "tr"; 989 private static final String UL = "ul"; 990 private static final String WIDTH = "width"; 991 private static final String STYLE="style"; 992 993 private static class SortedVector 994 { 995 public SortedVector() { 996 v = new Vector<Question>(); 997 } 998 999 public SortedVector(int initialSize) { 1000 v = new Vector<>(initialSize); 1001 } 1002 1003 1004 public int size() { 1005 return v.size(); 1006 } 1007 1008 public Question elementAt(int index) { 1009 return v.elementAt(index); 1010 } 1011 1012 public void insert(Question o) { 1013 v.insertElementAt(o, findSortIndex(o)); 1014 } 1015 1016 public void insert(Question o, boolean ignoreDuplicates) { 1017 int i = findSortIndex(o); 1018 if (ignoreDuplicates && (i < v.size()) && (compare(o, v.elementAt(i)) == 0)) 1019 return; 1020 1021 v.insertElementAt(o, i); 1022 } 1023 1024 public void copyInto(Question[] target) { 1025 v.copyInto(target); 1026 } 1027 1028 protected int compare(Question o1, Question o2) { 1029 String p1 = o1.getTag(); 1030 String p2 = o2.getTag(); 1031 1032 if (p1 == null && p2 == null) 1033 return 0; 1034 1035 if (p1 == null) 1036 return -1; 1037 1038 if (p2 == null) 1039 return +1; 1040 1041 return p1.compareTo(p2); 1042 } 1043 1044 private int findSortIndex(Question o) { 1045 int lower = 0; 1046 int upper = v.size() - 1; 1047 int mid = 0; 1048 1049 if (upper == -1) { 1050 return 0; 1051 } 1052 1053 int cmp = 0; 1054 Question last = v.elementAt(upper); 1055 cmp = compare(o, last); 1056 if (cmp > 0) 1057 return upper + 1; 1058 1059 while (lower <= upper) { 1060 mid = lower + ((upper - lower) / 2); 1061 Question entry = v.elementAt(mid); 1062 cmp = compare(o, entry); 1063 1064 if (cmp == 0) { 1065 // found a matching description 1066 return mid; 1067 } else if (cmp < 0) { 1068 upper = mid - 1; 1069 } else { 1070 lower = mid + 1; 1071 } 1072 } 1073 1074 // didn't find it, but we indicate the index of where it would belong. 1075 return (cmp < 0) ? mid : mid + 1; 1076 } 1077 1078 private Vector<Question> v; 1079 } 1080 1081 }