1 /* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.javadoc.internal.tool; 27 28 import java.io.File; 29 import java.io.FileNotFoundException; 30 import java.io.IOException; 31 import java.io.PrintWriter; 32 import java.lang.reflect.Method; 33 import java.nio.file.Path; 34 import java.text.BreakIterator; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.List; 40 import java.util.Locale; 41 import java.util.Objects; 42 import java.util.Set; 43 44 import static javax.tools.DocumentationTool.Location.*; 45 46 import javax.tools.JavaFileManager; 47 import javax.tools.JavaFileObject; 48 import javax.tools.StandardJavaFileManager; 49 import javax.tools.StandardLocation; 50 51 import com.sun.tools.javac.api.JavacTrees; 52 import com.sun.tools.javac.file.BaseFileManager; 53 import com.sun.tools.javac.file.JavacFileManager; 54 import com.sun.tools.javac.main.CommandLine; 55 import com.sun.tools.javac.main.OptionHelper; 56 import com.sun.tools.javac.main.OptionHelper.GrumpyHelper; 57 import com.sun.tools.javac.platform.PlatformDescription; 58 import com.sun.tools.javac.platform.PlatformUtils; 59 import com.sun.tools.javac.util.ClientCodeException; 60 import com.sun.tools.javac.util.Context; 61 import com.sun.tools.javac.util.Log; 62 import com.sun.tools.javac.util.Options; 63 64 import jdk.javadoc.doclet.Doclet; 65 import jdk.javadoc.doclet.Doclet.Option; 66 import jdk.javadoc.doclet.DocletEnvironment; 67 68 import static com.sun.tools.javac.main.Option.*; 69 70 /** 71 * Main program of Javadoc. 72 * Previously named "Main". 73 * 74 * <p><b>This is NOT part of any supported API. 75 * If you write code that depends on this, you do so at your own risk. 76 * This code and its internal interfaces are subject to change or 77 * deletion without notice.</b> 78 * 79 * @author Robert Field 80 * @author Neal Gafter (rewrite) 81 */ 82 public class Start extends ToolOption.Helper { 83 84 @SuppressWarnings("deprecation") 85 private static final Class<?> OldStdDoclet = 86 com.sun.tools.doclets.standard.Standard.class; 87 88 private static final Class<?> StdDoclet = 89 jdk.javadoc.internal.doclets.standard.Standard.class; 90 /** Context for this invocation. */ 91 private final Context context; 92 93 private static final String ProgramName = "javadoc"; 94 95 // meaning we allow all visibility of PROTECTED and PUBLIC 96 private static final String defaultModifier = "protected"; 97 98 private Messager messager; 99 100 private final String docletName; 101 102 private final ClassLoader classLoader; 103 104 private Class<?> docletClass; 105 106 private Doclet doclet; 107 108 // used to determine the locale for the messager 109 private Locale locale; 110 111 112 /** 113 * In API mode, exceptions thrown while calling the doclet are 114 * propagated using ClientCodeException. 115 */ 116 private boolean apiMode; 117 118 private JavaFileManager fileManager; 119 120 Start() { 121 this(null, null, null, null, null); 122 } 123 124 Start(PrintWriter writer) { 125 this(null, null, writer, null, null); 126 } 127 128 Start(Context context, String programName, PrintWriter writer, 129 String docletName, ClassLoader classLoader) { 130 this.context = context == null ? new Context() : context; 131 String pname = programName == null ? ProgramName : programName; 132 this.messager = writer == null 133 ? new Messager(this.context, pname) 134 : new Messager(this.context, pname, writer, writer); 135 this.docletName = docletName; 136 this.classLoader = classLoader; 137 this.docletClass = null; 138 this.locale = Locale.getDefault(); 139 } 140 141 public Start(Context context) { 142 this.docletClass = null; 143 this.context = Objects.requireNonNull(context); 144 this.apiMode = true; 145 this.docletName = null; 146 this.classLoader = null; 147 this.locale = Locale.getDefault(); 148 } 149 150 void initMessager() { 151 if (!apiMode) 152 return; 153 if (messager == null) { 154 Log log = context.get(Log.logKey); 155 if (log instanceof Messager) { 156 messager = (Messager) log; 157 } else { 158 PrintWriter out = context.get(Log.outKey); 159 messager = (out == null) 160 ? new Messager(context, ProgramName) 161 : new Messager(context, ProgramName, out, out); 162 } 163 } 164 } 165 166 /** 167 * Usage 168 */ 169 @Override 170 void usage() { 171 usage(true); 172 } 173 174 void usage(boolean exit) { 175 usage("main.usage", "-help", null, exit); 176 } 177 178 @Override 179 void Xusage() { 180 Xusage(true); 181 } 182 183 void Xusage(boolean exit) { 184 usage("main.Xusage", "-X", "main.Xusage.foot", exit); 185 } 186 187 private void usage(String main, String option, String foot, boolean exit) { 188 messager.notice(main); 189 // let doclet print usage information (does nothing on error) 190 if (docletClass != null) { 191 String name = doclet.getName(); 192 Set<Option> supportedOptions = doclet.getSupportedOptions(); 193 messager.notice("main.doclet.usage.header", name); 194 Option.Kind myKind = option.equals("-X") 195 ? Option.Kind.EXTENDED 196 : Option.Kind.STANDARD; 197 supportedOptions.stream() 198 .filter(opt -> opt.getKind() == myKind) 199 .forEach(opt -> messager.printNotice(opt.toString())); 200 } 201 if (foot != null) 202 messager.notice(foot); 203 204 if (exit) 205 throw new Messager.ExitJavadoc(); 206 } 207 208 209 /** 210 * Main program - external wrapper. In order to maintain backward 211 * CLI compatibility, we dispatch to the old tool or the old doclet's 212 * Start mechanism, based on the options present on the command line 213 * with the following precedence: 214 * 1. presence of -Xold, dispatch to old tool 215 * 2. doclet variant, if old, dispatch to old Start 216 * 3. taglet variant, if old, dispatch to old Start 217 * 218 * Thus the presence of -Xold switches the tool, soon after command files 219 * if any, are expanded, this is performed here, noting that the messager 220 * is available at this point in time. 221 * The doclet/taglet tests are performed in the begin method, further on, 222 * this is to minimize argument processing and most importantly the impact 223 * of class loader creation, needed to detect the doclet/taglet class variants. 224 */ 225 @SuppressWarnings("deprecation") 226 int begin(String... argv) { 227 // Preprocess @file arguments 228 try { 229 argv = CommandLine.parse(argv); 230 } catch (FileNotFoundException e) { 231 messager.error("main.cant.read", e.getMessage()); 232 throw new Messager.ExitJavadoc(); 233 } catch (IOException e) { 234 e.printStackTrace(System.err); 235 throw new Messager.ExitJavadoc(); 236 } 237 238 if (argv.length > 0 && "-Xold".equals(argv[0])) { 239 messager.warning("main.legacy_api"); 240 String[] nargv = Arrays.copyOfRange(argv, 1, argv.length); 241 return com.sun.tools.javadoc.Main.execute(nargv); 242 } 243 boolean ok = begin(Arrays.asList(argv), Collections.<JavaFileObject> emptySet()); 244 return ok ? 0 : 1; 245 } 246 247 // Called by 199 API. 248 public boolean begin(Class<?> docletClass, 249 Iterable<String> options, 250 Iterable<? extends JavaFileObject> fileObjects) { 251 this.docletClass = docletClass; 252 List<String> opts = new ArrayList<>(); 253 for (String opt: options) 254 opts.add(opt); 255 256 return begin(opts, fileObjects); 257 } 258 259 @SuppressWarnings("deprecation") 260 private boolean begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) { 261 fileManager = context.get(JavaFileManager.class); 262 if (fileManager == null) { 263 JavacFileManager.preRegister(context); 264 fileManager = context.get(JavaFileManager.class); 265 if (fileManager instanceof BaseFileManager) { 266 ((BaseFileManager) fileManager).autoClose = true; 267 } 268 } 269 // locale, doclet and maybe taglet, needs to be determined first 270 docletClass = preProcess(fileManager, options); 271 if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) { 272 // no need to dispatch to old, safe to init now 273 initMessager(); 274 messager.setLocale(locale); 275 try { 276 Object o = docletClass.newInstance(); 277 doclet = (Doclet) o; 278 } catch (InstantiationException | IllegalAccessException exc) { 279 exc.printStackTrace(); 280 if (!apiMode) { 281 error("main.could_not_instantiate_class", docletClass); 282 throw new Messager.ExitJavadoc(); 283 } 284 throw new ClientCodeException(exc); 285 } 286 } else { 287 if (this.apiMode) { 288 com.sun.tools.javadoc.main.Start ostart 289 = new com.sun.tools.javadoc.main.Start(context); 290 return ostart.begin(docletClass, options, fileObjects); 291 } 292 warn("main.legacy_api"); 293 String[] array = options.toArray(new String[options.size()]); 294 return com.sun.tools.javadoc.Main.execute(array) == 0; 295 } 296 297 boolean failed = false; 298 try { 299 failed = !parseAndExecute(options, fileObjects); 300 } catch (Messager.ExitJavadoc exc) { 301 // ignore, we just exit this way 302 } catch (OutOfMemoryError ee) { 303 messager.error("main.out.of.memory"); 304 failed = true; 305 } catch (ClientCodeException e) { 306 // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl 307 throw e; 308 } catch (Error ee) { 309 ee.printStackTrace(System.err); 310 messager.error("main.fatal.error"); 311 failed = true; 312 } catch (Exception ee) { 313 ee.printStackTrace(System.err); 314 messager.error("main.fatal.exception"); 315 failed = true; 316 } finally { 317 if (fileManager != null 318 && fileManager instanceof BaseFileManager 319 && ((BaseFileManager) fileManager).autoClose) { 320 try { 321 fileManager.close(); 322 } catch (IOException ignore) {} 323 } 324 boolean haveErrorWarnings = messager.nerrors() > 0 || 325 (rejectWarnings && messager.nwarnings() > 0); 326 if (failed && !haveErrorWarnings) { 327 // the doclet failed, but nothing reported, flag it!. 328 messager.error("main.unknown.error"); 329 } 330 failed |= haveErrorWarnings; 331 messager.exitNotice(); 332 messager.flush(); 333 } 334 return !failed; 335 } 336 337 /** 338 * Main program - internal 339 */ 340 private boolean parseAndExecute(List<String> argList, 341 Iterable<? extends JavaFileObject> fileObjects) throws IOException { 342 long tm = System.currentTimeMillis(); 343 344 List<String> javaNames = new ArrayList<>(); 345 346 compOpts = Options.instance(context); 347 348 // Make sure no obsolete source/target messages are reported 349 com.sun.tools.javac.main.Option.XLINT.process(getOptionHelper(), "-Xlint:-options"); 350 351 doclet.init(locale, messager); 352 parseArgs(argList, javaNames); 353 354 if (fileManager instanceof BaseFileManager) { 355 ((BaseFileManager) fileManager).handleOptions(fileManagerOpts); 356 } 357 358 String platformString = compOpts.get("-release"); 359 360 if (platformString != null) { 361 if (compOpts.isSet("-source")) { 362 usageError("main.release.bootclasspath.conflict", "-source"); 363 } 364 if (fileManagerOpts.containsKey(BOOTCLASSPATH)) { 365 usageError("main.release.bootclasspath.conflict", BOOTCLASSPATH.getText()); 366 } 367 368 PlatformDescription platformDescription = 369 PlatformUtils.lookupPlatformDescription(platformString); 370 371 if (platformDescription == null) { 372 usageError("main.unsupported.release.version", platformString); 373 } 374 375 compOpts.put(SOURCE, platformDescription.getSourceVersion()); 376 377 context.put(PlatformDescription.class, platformDescription); 378 379 Collection<Path> platformCP = platformDescription.getPlatformPath(); 380 381 if (platformCP != null) { 382 if (fileManager instanceof StandardJavaFileManager) { 383 StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager; 384 385 sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP); 386 } else { 387 usageError("main.release.not.standard.file.manager", platformString); 388 } 389 } 390 } 391 392 compOpts.notifyListeners(); 393 394 if (javaNames.isEmpty() && subPackages.isEmpty() && isEmpty(fileObjects)) { 395 usageError("main.No_packages_or_classes_specified"); 396 } 397 398 JavadocTool comp = JavadocTool.make0(context); 399 if (comp == null) return false; 400 401 if (showAccess == null) { 402 setFilter(defaultModifier); 403 } 404 405 DocletEnvironment root = comp.getEnvironment( 406 encoding, 407 showAccess, 408 overviewpath, 409 javaNames, 410 fileObjects, 411 subPackages, 412 excludedPackages, 413 docClasses, 414 quiet); 415 416 // release resources 417 comp = null; 418 419 if (breakiterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { 420 JavacTrees trees = JavacTrees.instance(context); 421 trees.setBreakIterator(BreakIterator.getSentenceInstance(locale)); 422 } 423 // pass off control to the doclet 424 boolean ok = root != null; 425 if (ok) ok = doclet.run(root); 426 427 // We're done. 428 if (compOpts.get("-verbose") != null) { 429 tm = System.currentTimeMillis() - tm; 430 messager.notice("main.done_in", Long.toString(tm)); 431 } 432 433 return ok; 434 } 435 436 Set<Doclet.Option> docletOptions = null; 437 int handleDocletOptions(int idx, List<String> args, boolean isToolOption) { 438 if (docletOptions == null) { 439 docletOptions = doclet.getSupportedOptions(); 440 } 441 String arg = args.get(idx); 442 443 for (Doclet.Option opt : docletOptions) { 444 if (opt.matches(arg)) { 445 if (args.size() - idx < opt.getArgumentCount()) { 446 usageError("main.requires_argument", arg); 447 } 448 opt.process(arg, args.listIterator(idx + 1)); 449 idx += opt.getArgumentCount(); 450 return idx; 451 } 452 } 453 // check if arg is accepted by the tool before emitting error 454 if (!isToolOption) 455 usageError("main.invalid_flag", arg); 456 return idx; 457 } 458 459 private Class<?> preProcess(JavaFileManager jfm, List<String> argv) { 460 // doclet specifying arguments 461 String userDocletPath = null; 462 String userDocletName = null; 463 464 // taglet specifying arguments, since tagletpath is a doclet 465 // functionality, assume they are repeated and inspect all. 466 List<File> userTagletPath = new ArrayList<>(); 467 List<String> userTagletNames = new ArrayList<>(); 468 469 // Step 1: loop through the args, set locale early on, if found. 470 for (int i = 0 ; i < argv.size() ; i++) { 471 String arg = argv.get(i); 472 if (arg.equals(ToolOption.LOCALE.opt)) { 473 oneArg(argv, i++); 474 String lname = argv.get(i); 475 locale = getLocale(lname); 476 } else if (arg.equals(ToolOption.DOCLET.opt)) { 477 oneArg(argv, i++); 478 if (userDocletName != null) { 479 usageError("main.more_than_one_doclet_specified_0_and_1", 480 userDocletName, argv.get(i)); 481 } 482 if (docletName != null) { 483 usageError("main.more_than_one_doclet_specified_0_and_1", 484 docletName, argv.get(i)); 485 } 486 userDocletName = argv.get(i); 487 } else if (arg.equals(ToolOption.DOCLETPATH.opt)) { 488 oneArg(argv, i++); 489 if (userDocletPath == null) { 490 userDocletPath = argv.get(i); 491 } else { 492 userDocletPath += File.pathSeparator + argv.get(i); 493 } 494 } else if ("-taglet".equals(arg)) { 495 userTagletNames.add(argv.get(i + 1)); 496 } else if ("-tagletpath".equals(arg)) { 497 for (String pathname : argv.get(i + 1).split(File.pathSeparator)) { 498 userTagletPath.add(new File(pathname)); 499 } 500 } 501 } 502 503 // Step 2: a doclet is provided, nothing more to do. 504 if (docletClass != null) { 505 return docletClass; 506 } 507 508 // Step 3: doclet name specified ? if so find a ClassLoader, 509 // and load it. 510 if (userDocletName != null) { 511 ClassLoader cl = classLoader; 512 if (cl == null) { 513 if (!fileManager.hasLocation(DOCLET_PATH)) { 514 List<File> paths = new ArrayList<>(); 515 if (userDocletPath != null) { 516 for (String pathname : userDocletPath.split(File.pathSeparator)) { 517 paths.add(new File(pathname)); 518 } 519 } 520 try { 521 ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths); 522 } catch (IOException ioe) { 523 error("main.doclet_could_not_set_location", paths); 524 throw new Messager.ExitJavadoc(); 525 } 526 } 527 cl = fileManager.getClassLoader(DOCLET_PATH); 528 if (cl == null) { 529 // despite doclet specified on cmdline no classloader found! 530 error("main.doclet_no_classloader_found", userDocletName); 531 throw new Messager.ExitJavadoc(); 532 } 533 } 534 try { 535 Class<?> klass = cl.loadClass(userDocletName); 536 return klass; 537 } catch (ClassNotFoundException cnfe) { 538 error("main.doclet_class_not_found", userDocletName); 539 throw new Messager.ExitJavadoc(); 540 } 541 } 542 543 // Step 4: we have a doclet, try loading it 544 if (docletName != null) { 545 try { 546 return Class.forName(docletName, true, getClass().getClassLoader()); 547 } catch (ClassNotFoundException cnfe) { 548 error("main.doclet_class_not_found", userDocletName); 549 throw new Messager.ExitJavadoc(); 550 } 551 } 552 553 // Step 5: we don't have a doclet specified, do we have taglets ? 554 if (!userTagletNames.isEmpty() && hasOldTaglet(userTagletNames, userTagletPath)) { 555 // found a bogey, return the old doclet 556 return OldStdDoclet; 557 } 558 559 // finally 560 return StdDoclet; 561 } 562 563 /* 564 * This method returns true iff it finds a legacy taglet, but for 565 * all other conditions including errors it returns false, allowing 566 * nature to take its own course. 567 */ 568 @SuppressWarnings("deprecation") 569 private boolean hasOldTaglet(List<String> tagletNames, List<File> tagletPaths) { 570 if (!fileManager.hasLocation(TAGLET_PATH)) { 571 try { 572 ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, tagletPaths); 573 } catch (IOException ioe) { 574 error("main.doclet_could_not_set_location", tagletPaths); 575 throw new Messager.ExitJavadoc(); 576 } 577 } 578 ClassLoader cl = fileManager.getClassLoader(TAGLET_PATH); 579 if (cl == null) { 580 // no classloader found! 581 error("main.doclet_no_classloader_found", tagletNames.get(0)); 582 throw new Messager.ExitJavadoc(); 583 } 584 for (String tagletName : tagletNames) { 585 try { 586 Class<?> klass = cl.loadClass(tagletName); 587 if (com.sun.tools.doclets.Taglet.class.isAssignableFrom(klass)) { 588 return true; 589 } 590 } catch (ClassNotFoundException cnfe) { 591 error("main.doclet_class_not_found", tagletName); 592 throw new Messager.ExitJavadoc(); 593 } 594 } 595 return false; 596 } 597 598 private void parseArgs(List<String> args, List<String> javaNames) { 599 for (int i = 0 ; i < args.size() ; i++) { 600 String arg = args.get(i); 601 ToolOption o = ToolOption.get(arg); 602 if (o != null) { 603 // handle a doclet argument that may be needed however 604 // don't increment the index, and allow the tool to consume args 605 handleDocletOptions(i, args, true); 606 607 if (o.hasArg) { 608 oneArg(args, i++); 609 o.process(this, args.get(i)); 610 } else if (o.hasSuffix) { 611 o.process(this, arg); 612 } else { 613 setOption(arg); 614 o.process(this); 615 } 616 } else if (arg.startsWith("-XD")) { 617 // hidden javac options 618 String s = arg.substring("-XD".length()); 619 int eq = s.indexOf('='); 620 String key = (eq < 0) ? s : s.substring(0, eq); 621 String value = (eq < 0) ? s : s.substring(eq+1); 622 compOpts.put(key, value); 623 } else if (arg.startsWith("-")) { 624 i = handleDocletOptions(i, args, false); 625 } else { 626 javaNames.add(arg); 627 } 628 } 629 } 630 631 private <T> boolean isEmpty(Iterable<T> iter) { 632 return !iter.iterator().hasNext(); 633 } 634 635 /** 636 * Set one arg option. 637 * Error and exit if one argument is not provided. 638 */ 639 private void oneArg(List<String> args, int index) { 640 if ((index + 1) < args.size()) { 641 setOption(args.get(index), args.get(index+1)); 642 } else { 643 usageError("main.requires_argument", args.get(index)); 644 } 645 } 646 647 @Override 648 void usageError(String key, Object... args) { 649 error(key, args); 650 usage(true); 651 } 652 653 void error(String key, Object... args) { 654 messager.error(key, args); 655 } 656 657 void warn(String key, Object... args) { 658 messager.warning(key, args); 659 } 660 661 /** 662 * indicate an option with no arguments was given. 663 */ 664 private void setOption(String opt) { 665 String[] option = { opt }; 666 options.add(Arrays.asList(option)); 667 } 668 669 /** 670 * indicate an option with one argument was given. 671 */ 672 private void setOption(String opt, String argument) { 673 String[] option = { opt, argument }; 674 options.add(Arrays.asList(option)); 675 } 676 677 /** 678 * indicate an option with the specified list of arguments was given. 679 */ 680 private void setOption(String opt, List<String> arguments) { 681 List<String> args = new ArrayList<>(arguments.size() + 1); 682 args.add(opt); 683 args.addAll(arguments); 684 options.add(args); 685 } 686 687 /** 688 * Get the locale if specified on the command line 689 * else return null and if locale option is not used 690 * then return default locale. 691 */ 692 private Locale getLocale(String localeName) { 693 Locale userlocale = null; 694 if (localeName == null || localeName.isEmpty()) { 695 return Locale.getDefault(); 696 } 697 int firstuscore = localeName.indexOf('_'); 698 int seconduscore = -1; 699 String language = null; 700 String country = null; 701 String variant = null; 702 if (firstuscore == 2) { 703 language = localeName.substring(0, firstuscore); 704 seconduscore = localeName.indexOf('_', firstuscore + 1); 705 if (seconduscore > 0) { 706 if (seconduscore != firstuscore + 3 707 || localeName.length() <= seconduscore + 1) { 708 usageError("main.malformed_locale_name", localeName); 709 return null; 710 } 711 country = localeName.substring(firstuscore + 1, 712 seconduscore); 713 variant = localeName.substring(seconduscore + 1); 714 } else if (localeName.length() == firstuscore + 3) { 715 country = localeName.substring(firstuscore + 1); 716 } else { 717 usageError("main.malformed_locale_name", localeName); 718 return null; 719 } 720 } else if (firstuscore == -1 && localeName.length() == 2) { 721 language = localeName; 722 } else { 723 usageError("main.malformed_locale_name", localeName); 724 return null; 725 } 726 userlocale = searchLocale(language, country, variant); 727 if (userlocale == null) { 728 usageError("main.illegal_locale_name", localeName); 729 return null; 730 } else { 731 return userlocale; 732 } 733 } 734 735 /** 736 * Search the locale for specified language, specified country and 737 * specified variant. 738 */ 739 private Locale searchLocale(String language, String country, 740 String variant) { 741 for (Locale loc : Locale.getAvailableLocales()) { 742 if (loc.getLanguage().equals(language) && 743 (country == null || loc.getCountry().equals(country)) && 744 (variant == null || loc.getVariant().equals(variant))) { 745 return loc; 746 } 747 } 748 return null; 749 } 750 751 @Override 752 OptionHelper getOptionHelper() { 753 return new GrumpyHelper(null) { 754 @Override 755 public String get(com.sun.tools.javac.main.Option option) { 756 return compOpts.get(option); 757 } 758 759 @Override 760 public void put(String name, String value) { 761 compOpts.put(name, value); 762 } 763 }; 764 } 765 }