1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 import java.io.*; 23 import java.util.*; 24 import java.lang.*; 25 26 27 class ResourceWriter { 28 29 BufferedWriter theWriter = null; 30 String theTag = null; 31 32 static final String NL = new String("\n"); 33 static final String LOCALIZE = new String("// LOCALIZE"); 34 static final String NOLOCALIZE = new String("// DO NOT LOCALIZE"); 35 static final String INDENT = new String(" "); 36 static final String INDENT_2 = new String(INDENT + INDENT); 37 static final String COMMENTBLOCK_START = new String("/*"); 38 static final String COMMENTBLOCK_END = new String(" */"); 39 static final String COMMENTLINE_START = new String(" * "); 40 ResourceWriter(BufferedWriter w)41 public ResourceWriter(BufferedWriter w) { 42 theWriter = w; 43 } 44 close()45 public void close() throws IOException { 46 theWriter.flush(); 47 theWriter.close(); 48 } 49 writenl(String s)50 protected void writenl(String s) throws IOException { 51 theWriter.write(s, 0, s.length()); 52 theWriter.newLine(); 53 } 54 write(String s)55 protected void write(String s) throws IOException { 56 theWriter.write(s, 0, s.length()); 57 } 58 fileheader()59 public void fileheader() throws IOException { 60 writenl("/* "); 61 writenl(" * GENERATED CODE"); 62 writenl(" *"); 63 writenl(" * Copyright 1999 Sun Microsystems, Inc."); 64 writenl(" * All rights reserved."); 65 writenl(" *"); 66 writenl(" */"); 67 writenl(""); 68 writenl("package com.sun.admin.pm.client;"); 69 writenl("import java.util.*;"); 70 } 71 classheader(String className)72 public void classheader(String className) throws IOException { 73 writenl("public class " + className + 74 " extends ListResourceBundle {"); 75 writenl(INDENT + 76 "static final Object[][] pmHelpBundlecontents = {"); 77 } 78 footer()79 public void footer() throws IOException { 80 writenl(INDENT + "};"); 81 writenl(INDENT + "public Object[][] getContents() {"); 82 writenl(INDENT_2 + "return pmHelpBundlecontents;"); 83 writenl(INDENT + "}"); 84 writenl("}"); 85 } 86 87 setTag(String tag)88 public void setTag(String tag) throws IOException { 89 theTag = new String(tag); 90 } 91 tag(String tag)92 public void tag(String tag) throws IOException { 93 if (tag != null) { 94 writenl(NL + INDENT_2 + NOLOCALIZE); 95 writenl(INDENT_2 + 96 "{\"" + theTag + ".tag\", \"" + tag + "\"},"); 97 } 98 } 99 title(String title)100 public void title(String title) throws IOException { 101 if (title != null) { 102 writenl(NL + INDENT_2 + LOCALIZE); 103 writenl(INDENT_2 + 104 "{\"" + theTag + ".title\", \"" + title + "\"},"); 105 } 106 } 107 seealso(String seealso)108 public void seealso(String seealso) throws IOException { 109 if (seealso != null) { 110 writenl(NL + INDENT_2 + NOLOCALIZE); 111 writenl(INDENT_2 + 112 "{\"" + theTag + ".seealso\", \"" + seealso + "\"},"); 113 } 114 } 115 keywords(String keywords)116 public void keywords(String keywords) throws IOException { 117 if (keywords != null) { 118 writenl(NL + INDENT_2 + LOCALIZE); 119 writenl(INDENT_2 + 120 "{\"" + theTag + ".keywords\", \"" + keywords + "\"},"); 121 } 122 } 123 content(Vector content)124 public void content(Vector content) throws IOException { 125 if (content == null) 126 return; 127 128 writenl(NL + INDENT_2 + LOCALIZE); 129 writenl(INDENT_2 + "{\"" + theTag + ".content\","); 130 131 Enumeration e = content.elements(); 132 while (e.hasMoreElements()) { 133 String s = (String) e.nextElement(); 134 if (s.length() == 0) { 135 if (e.hasMoreElements()) 136 continue; 137 else { 138 writenl(INDENT_2 + " \"\"\n },"); 139 break; 140 } 141 } 142 String endOfLine = (e.hasMoreElements() ? 143 " +" : 144 "\n },"); 145 writenl(INDENT_2 + " \"" + s + " \"" + endOfLine); 146 } 147 } 148 delimiter()149 public void delimiter() throws IOException { 150 writenl(NL); 151 } 152 commentStart()153 public void commentStart() throws IOException { 154 writenl(COMMENTBLOCK_START); 155 } 156 commentEnd()157 public void commentEnd() throws IOException { 158 writenl(COMMENTBLOCK_END); 159 } 160 comment(String s)161 public void comment(String s) throws IOException { 162 writenl(COMMENTLINE_START + s); 163 } 164 } 165 166 167 168 169 class Article { 170 String theTitle; 171 String theKeywords; 172 String theSeeAlso; 173 Vector theContent; 174 String theTag; 175 176 Reader r; 177 read(Reader theReader)178 public void read(Reader theReader) { 179 r = theReader; 180 181 Tag theTag = null; 182 183 try { 184 while (true) { 185 theTag = readNextTag(); 186 Debug.message("Article read: " + theTag); 187 188 if (theTag instanceof CommentTag) { 189 Debug.message("Comment ignored"); 190 } else if (theTag instanceof TitleTag) { 191 theTitle = theTag.content; 192 } else if (theTag instanceof ContentTag) { 193 theContent = theTag.contentVector; 194 } else if (theTag instanceof SeeAlsoTag) { 195 theSeeAlso = theTag.content; 196 } else if (theTag instanceof KeywordsTag) { 197 theKeywords = theTag.content; 198 } else { 199 Debug.message("Unknown tag: " + theTag); 200 } 201 } 202 } catch (IOException x) { 203 Debug.message("Article read caught " + x); 204 } 205 206 207 } 208 209 localread()210 int localread() throws IOException { 211 int ch = r.read(); 212 if (ch == -1) { 213 Debug.message("localread: eof"); 214 throw new IOException(); 215 } 216 // Debug.message("localread: " + ch); 217 return ch; 218 } 219 220 221 /* 222 * read the word within tagOpen/tagClose pair 223 */ readTagName()224 String readTagName() throws IOException { 225 String rv = null; 226 int ch; 227 StringBuffer b = new StringBuffer(); 228 229 while (true) 230 if (localread() == HelpSyntax.tagOpen) 231 break; 232 233 Debug.message("readTagName: got a tagOpen"); 234 235 while (true) { 236 ch = localread(); 237 if (ch == HelpSyntax.tagClose) 238 break; 239 else 240 b.append((char) ch); 241 } 242 243 Debug.message("readTagName: " + (new String(b)).trim()); 244 return (new String(b)).trim(); 245 } 246 247 readNextTag()248 Tag readNextTag() throws IOException { 249 Tag rv = null; 250 int ch; 251 StringBuffer b; 252 253 String tag = readTagName(); 254 Debug.message("readNextTag name: " + tag); 255 256 if (tag.equalsIgnoreCase(HelpSyntax.tagTitle)) { 257 rv = new TitleTag(); 258 rv.read(r); 259 } else if (tag.equalsIgnoreCase(HelpSyntax.tagKeywords)) { 260 rv = new KeywordsTag(); 261 rv.read(r); 262 } else if (tag.equalsIgnoreCase(HelpSyntax.tagSeeAlso)) { 263 rv = new SeeAlsoTag(); 264 rv.read(r); 265 } else if (tag.equalsIgnoreCase(HelpSyntax.tagContent)) { 266 rv = new ContentTag(); 267 rv.readMultipleLines(r); 268 } else { 269 Debug.message("Bad tag: " + tag); 270 } 271 return rv; 272 } 273 274 toString()275 public String toString() { 276 return new String("Title <" + theTitle + 277 "> Keywords <" + theKeywords + 278 "> See-Also <" + theSeeAlso + 279 "> Content <" + theContent + ">"); 280 } 281 } 282 283 284 class HelpSyntax { 285 public final static int tagOpen = '<'; 286 public final static int tagClose = '>'; 287 public final static String startComment = "!-"; 288 public final static String endComment = "--"; 289 public final static String tagContent = "CONTENT"; 290 public final static String tagTitle = "TITLE"; 291 public final static String tagSeeAlso = "SEEALSO"; 292 public final static String tagKeywords = "KEYWORDS"; 293 } 294 295 class ParseException extends Exception { 296 } 297 298 class BadTagException extends ParseException { 299 } 300 301 class SyntaxErrorException extends ParseException { 302 } 303 304 abstract class Tag { 305 String content; 306 Vector contentVector; 307 protected String name; 308 protected boolean escapeQuotes = false; 309 Tag(String s)310 public Tag(String s) { 311 content = s; 312 } 313 Tag()314 public Tag() { 315 this(null); 316 } 317 toString()318 public String toString() { 319 return new String(this.getClass().getName() + ": " + content); 320 } 321 322 // respect line spacing, stuff contentVector readMultipleLines(Reader r)323 public void readMultipleLines(Reader r) throws IOException { 324 int ch; 325 StringBuffer b = new StringBuffer(); 326 Vector v = new Vector(); 327 boolean spaced = false; 328 329 while (true) { 330 ch = r.read(); 331 if (ch == -1) 332 break; 333 334 if (ch == '\n') { 335 v.addElement(new String(b)); 336 b = new StringBuffer(); 337 continue; 338 } 339 340 if (Character.isWhitespace((char) ch)) { 341 if (spaced == false) { 342 b.append(" "); 343 spaced = true; 344 } 345 continue; 346 } 347 348 if (escapeQuotes && ch == '\"') { 349 b.append("\\\""); 350 continue; 351 } 352 353 spaced = false; 354 if (ch == HelpSyntax.tagOpen) { 355 boolean localspaced = false; 356 boolean localopen = true; 357 Debug.message("Tag: got a tagOpen"); 358 359 StringBuffer tmp = new StringBuffer(); 360 while ((ch = r.read()) != HelpSyntax.tagClose) { 361 if (Character.isWhitespace((char) ch)) { 362 if (localspaced == false) { 363 tmp.append(" "); 364 localspaced = true; 365 } 366 continue; 367 } 368 tmp.append((char) ch); 369 } 370 371 String t = new String(tmp); 372 373 if ((t.trim()).equalsIgnoreCase("/" + this.name)) { 374 Debug.message("Tag: close tag = " + t); 375 break; 376 } else { 377 Debug.message("Tag: ignoring bad close tag = " + t); 378 b.append((char) HelpSyntax.tagOpen); 379 b.append(t); 380 b.append((char) HelpSyntax.tagClose); 381 } 382 } else { 383 b.append((char)ch); 384 } 385 } 386 contentVector = v; 387 Debug.message("Tag: contentVector = " + contentVector); 388 } 389 390 // catenate input lines, eliminating whitespace read(Reader r)391 public void read(Reader r) throws IOException { 392 int ch; 393 StringBuffer b = new StringBuffer(); 394 boolean spaced = false; 395 396 while (true) { 397 ch = r.read(); 398 if (ch == -1) 399 break; 400 401 if (Character.isWhitespace((char) ch)) { 402 if (spaced == false) { 403 b.append(" "); 404 spaced = true; 405 } 406 continue; 407 } 408 409 if (escapeQuotes && ch == '\"') { 410 b.append("\\\""); 411 continue; 412 } 413 414 spaced = false; 415 if (ch == HelpSyntax.tagOpen) { 416 boolean localspaced = false; 417 boolean localopen = true; 418 Debug.message("Tag: got a tagOpen"); 419 420 StringBuffer tmp = new StringBuffer(); 421 while ((ch = r.read()) != HelpSyntax.tagClose) { 422 if (Character.isWhitespace((char) ch)) { 423 if (localspaced == false) { 424 tmp.append(" "); 425 localspaced = true; 426 } 427 continue; 428 } 429 tmp.append((char) ch); 430 } 431 432 String t = new String(tmp); 433 434 if ((t.trim()).equalsIgnoreCase("/" + this.name)) { 435 Debug.message("Tag: close tag = " + t); 436 break; 437 } else { 438 Debug.message("Tag: ignoring bad close tag = " + t); 439 b.append((char) HelpSyntax.tagOpen); 440 b.append(t); 441 b.append((char) HelpSyntax.tagClose); 442 } 443 } else { 444 b.append((char)ch); 445 } 446 } 447 content = (new String(b)).trim(); 448 Debug.message("Tag: content = " + content); 449 } 450 } 451 452 class TitleTag extends Tag { TitleTag()453 public TitleTag() { 454 name = HelpSyntax.tagTitle; 455 } 456 } 457 458 class SeeAlsoTag extends Tag { SeeAlsoTag()459 public SeeAlsoTag() { 460 name = HelpSyntax.tagSeeAlso; 461 } 462 } 463 464 class ContentTag extends Tag { ContentTag()465 public ContentTag() { 466 name = HelpSyntax.tagContent; 467 escapeQuotes = true; 468 } 469 } 470 471 class CommentTag extends Tag { CommentTag()472 public CommentTag() { 473 name = null; 474 } 475 } 476 477 class KeywordsTag extends Tag { KeywordsTag()478 public KeywordsTag() { 479 name = HelpSyntax.tagKeywords; 480 escapeQuotes = true; 481 } 482 } 483 484 485 486 class parseMain { 487 488 static String outputFileName = "pmHelpResources.java"; 489 static String commentFileName = "comments.txt"; 490 static int firstFile = 0; 491 492 // returns -1 if error, 0 otherwise parseArgs(String[] args)493 protected static int parseArgs(String[] args) { 494 int rv = 0; 495 int i; 496 497 for (i = 0; i < args.length; ++i) { 498 if (args[i].compareTo("-d") == 0) { 499 if (args[i].length() > 2) { 500 outputFileName = args[i].substring(2); 501 } else { 502 outputFileName = args[++i]; 503 } 504 } else if (args[i].compareTo("-c") == 0) { 505 if (args[i].length() > 2) { 506 commentFileName = args[i].substring(2); 507 } else { 508 commentFileName = args[++i]; 509 } 510 } else if (args[i].compareTo("-v") == 0) { 511 Debug.setDebugLevel(Debug.WARNING); 512 } else 513 break; // unknown arg ==> list of files starts 514 515 } 516 517 firstFile = i; 518 519 /* 520 * System.out.println("outputFileName = " + outputFileName + 521 * " commentFileName = " + commentFileName + 522 * " firstFile = " + firstFile); 523 */ 524 525 return rv; 526 } 527 528 main(String args[])529 public static void main(String args[]) { 530 FileReader f = null; 531 FileWriter fw = null; 532 String filename = null; 533 534 Debug.setDebugLevel(Debug.ERROR); 535 536 // validate command line 537 if (args.length == 0) { 538 System.err.println("At least one filename required."); 539 System.exit(-1); 540 } 541 542 if (parseArgs(args) < 0) 543 System.exit(-1); 544 545 546 outputFileName = outputFileName.trim(); 547 548 Debug.warning("Writing to " + outputFileName); 549 550 try { 551 552 // create output file 553 fw = new FileWriter(outputFileName); 554 BufferedWriter w = new BufferedWriter(fw); 555 ResourceWriter rw = new ResourceWriter(w); 556 557 // imports and package statement 558 rw.fileheader(); 559 560 // comment block 561 File commentFile = new File(commentFileName); 562 if (commentFile.exists()) { 563 rw.delimiter(); 564 rw.commentStart(); 565 BufferedReader comments = 566 new BufferedReader(new FileReader(commentFileName)); 567 String commentLine; 568 while ((commentLine = comments.readLine()) != null) 569 rw.comment(commentLine); 570 comments.close(); 571 rw.commentEnd(); 572 rw.delimiter(); 573 } else { 574 Debug.error("Comment file " + commentFileName + 575 " not found."); 576 } 577 578 // create class name w/o extension or leading path 579 File cf = new File(outputFileName); 580 String className = cf.getName(); 581 582 // class name is output filename w/o extension 583 int dotIndex = className.indexOf("."); 584 if (dotIndex < 0) 585 dotIndex = className.length(); 586 587 className = className.substring(0, dotIndex); 588 589 // class definition 590 rw.classheader(className); 591 592 // iterate over input files 593 for (int i = firstFile; i < args.length; ++i) { 594 filename = args[i]; 595 Debug.warning("Reading file " + filename); 596 597 try { 598 f = new FileReader(filename); 599 } catch (Exception x) { 600 Debug.fatal(x.toString()); 601 return; 602 } 603 604 BufferedReader r = new BufferedReader(f); 605 606 Article a = new Article(); 607 a.read(r); 608 // System.out.println(a); 609 610 // process the Article 611 612 String tagName = filenameToTag(filename); 613 Debug.warning("Creating tag " + tagName); 614 615 // HTML syntax checking on content 616 if (!validHTMLSyntax(a.theContent)) 617 throw new IOException( 618 "Bad HTML syntax in article " + tagName); 619 620 621 rw.setTag(tagName); 622 rw.tag(tagName); 623 rw.seealso(a.theSeeAlso); 624 rw.title(a.theTitle); 625 rw.keywords(a.theKeywords); 626 rw.content(a.theContent); 627 rw.delimiter(); 628 } 629 630 rw.footer(); 631 rw.close(); 632 w.close(); 633 } catch (IOException x) { 634 Debug.fatal(x.toString()); 635 636 // try to unlink the broken output file 637 boolean unlink = true; 638 639 try { 640 fw.close(); 641 } catch (IOException xx) { 642 Debug.error(xx.toString()); 643 unlink = false; 644 } 645 646 if (unlink) { 647 File theFile = new File(outputFileName); 648 649 Debug.warning("Deleting file " + outputFileName); 650 651 if (theFile.exists()) 652 theFile.delete(); 653 } 654 655 System.exit(-2); 656 } 657 } 658 659 660 // return true if no syntax errors found validHTMLSyntax(String s)661 static boolean validHTMLSyntax(String s) { 662 663 if (s == null) 664 return true; 665 666 // check only for <b>..</b> pairs 667 668 String src = s.toLowerCase(); // html tags are case-neutral 669 670 int i; 671 672 int opens = 0; 673 for (i = src.indexOf("<b>"); 674 i != -1; 675 i = src.indexOf("<b>", i + 1)) 676 ++opens; 677 678 int closes = 0; 679 for (i = src.indexOf("</b>"); 680 i != -1; 681 i = src.indexOf("</b>", i + 1)) 682 ++closes; 683 684 // System.out.println("syntax: " + opens + " " + closes); 685 686 return opens == closes; 687 688 } 689 690 // return true if no syntax errors found validHTMLSyntax(Vector v)691 static boolean validHTMLSyntax(Vector v) { 692 String s = new String(); 693 Enumeration e = v.elements(); 694 while (e.hasMoreElements()) 695 s = s + (String) e.nextElement(); 696 return validHTMLSyntax(s); 697 } 698 699 /* 700 * extract the tag name from a filename, possibly containing 701 * a fully qualified path as well as a complex extension. 702 */ filenameToTag(String filename)703 static String filenameToTag(String filename) { 704 705 // the help tag is the filename exclusive of path or extensions 706 707 File f = new File(filename); 708 String s = f.getName(); 709 int period = s.indexOf('.'); 710 // System.out.println("filename: " + s); 711 if (period < 0) 712 period = filename.length(); 713 // System.out.println("period = " + period); 714 return s.substring(0, period); 715 } 716 717 718 } 719 720 class Debug { 721 722 /** 723 * Log a highest-priority message. 724 * @param String s The message to be logged. 725 */ fatal(String s)726 static public void fatal(String s) { 727 printIf(s, FATAL); 728 } 729 730 /** 731 * Log a highest-priority message. 732 * @param String s The message to be logged. 733 */ error(String s)734 static public void error(String s) { 735 printIf(s, ERROR); 736 } 737 738 /** 739 * Log a highest-priority message. 740 * @param String s The message to be logged. 741 */ warning(String s)742 static public void warning(String s) { 743 printIf(s, WARNING); 744 } 745 746 /** 747 * Log a highest-priority message. 748 * @param String s The message to be logged. 749 */ message(String s)750 static public void message(String s) { 751 printIf(s, MESSAGE); 752 } 753 754 /** 755 * Log a highest-priority message. 756 * @param String s The message to be logged. 757 */ setDebugLevel(int lvl)758 static public void setDebugLevel(int lvl) { 759 if (lvl < ALL || lvl > NONE) 760 return; 761 762 globalDebugLevel = lvl; 763 } 764 printIf(String s, int lvl)765 private static void printIf(String s, int lvl) { 766 if (lvl < globalDebugLevel) 767 return; 768 DebugPrint(s); 769 } 770 771 // here is where we could hide syslog or file destination... DebugPrint(String s)772 private static void DebugPrint(String s) { 773 System.out.println(s); // for now 774 } 775 776 777 /** 778 * Verbosity level to suppress all messages. 779 */ 780 static public final int NONE = 5; 781 782 /** 783 * Verbosity level to log only highest-priority messages. 784 */ 785 static public final int FATAL = 4; 786 787 /** 788 * Verbosity level to log high- and highest-priority messages. 789 */ 790 static public final int ERROR = 3; 791 792 /** 793 * Verbosity level to log medium-, high-, and 794 * highest-priority messages. 795 */ 796 static public final int WARNING = 2; 797 798 /** 799 * Verbosity level to log low-, medium-, high-, and 800 * highest-priority messages. 801 */ 802 static public final int MESSAGE = 1; 803 804 /** 805 * Verbosity level to log all messages. 806 */ 807 static public final int ALL = 0; 808 809 private static int globalDebugLevel = ERROR; 810 811 } 812