< prev index next >

src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java

Print this page




  41 import java.util.Date;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.LinkedHashSet;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Optional;
  48 import java.util.Properties;
  49 import java.util.ResourceBundle;
  50 import java.util.Set;
  51 import java.util.HashSet;
  52 import java.util.function.BiFunction;
  53 import java.util.function.Function;
  54 import java.util.jar.Attributes;
  55 import java.util.jar.JarFile;
  56 import java.util.jar.Manifest;
  57 import java.util.regex.Pattern;
  58 import java.util.stream.Collectors;
  59 
  60 /**
  61  * StandardBundlerParams
  62  *
  63  * A parameter to a bundler.
  64  *
  65  * Also contains static definitions of all of the common bundler parameters.
  66  * (additional platform specific and mode specific bundler parameters
  67  * are defined in each of the specific bundlers)
  68  *
  69  * Also contains static methods that operate on maps of parameters.
  70  */
  71 class StandardBundlerParam<T> extends BundlerParamInfo<T> {
  72 
  73     private static final ResourceBundle I18N = ResourceBundle.getBundle(
  74             "jdk.jpackage.internal.resources.MainResources");
  75     private static final String JAVABASEJMOD = "java.base.jmod";
  76 
  77     StandardBundlerParam(String name, String description, String id,
  78             Class<T> valueType,
  79             Function<Map<String, ? super Object>, T> defaultValueFunction,
  80             BiFunction<String, Map<String, ? super Object>, T> stringConverter)
  81     {
  82         this.name = name;
  83         this.description = description;
  84         this.id = id;
  85         this.valueType = valueType;
  86         this.defaultValueFunction = defaultValueFunction;
  87         this.stringConverter = stringConverter;
  88     }
  89 
  90     static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
  91             new StandardBundlerParam<>(
  92                     I18N.getString("param.app-resources.name"),
  93                     I18N.getString("param.app-resource.description"),
  94                     BundleParams.PARAM_APP_RESOURCES,
  95                     RelativeFileSet.class,
  96                     null, // no default.  Required parameter
  97                     null  // no string translation,
  98                           // tool must provide complex type
  99             );
 100 
 101     @SuppressWarnings("unchecked")
 102     static final
 103             StandardBundlerParam<List<RelativeFileSet>> APP_RESOURCES_LIST =
 104             new StandardBundlerParam<>(
 105                     I18N.getString("param.app-resources-list.name"),
 106                     I18N.getString("param.app-resource-list.description"),
 107                     BundleParams.PARAM_APP_RESOURCES + "List",
 108                     (Class<List<RelativeFileSet>>) (Object) List.class,
 109                     // Default is appResources, as a single item list
 110                     p -> new ArrayList<>(Collections.singletonList(
 111                             APP_RESOURCES.fetchFrom(p))),
 112                     StandardBundlerParam::createAppResourcesListFromString
 113             );
 114 
 115     static final StandardBundlerParam<String> SOURCE_DIR =
 116             new StandardBundlerParam<>(
 117                     I18N.getString("param.source-dir.name"),
 118                     I18N.getString("param.source-dir.description"),
 119                     Arguments.CLIOptions.INPUT.getId(),
 120                     String.class,
 121                     p -> null,
 122                     (s, p) -> {
 123                         String value = String.valueOf(s);
 124                         if (value.charAt(value.length() - 1) ==
 125                                 File.separatorChar) {
 126                             return value.substring(0, value.length() - 1);
 127                         }
 128                         else {
 129                             return value;
 130                         }
 131                     }
 132             );
 133 
 134     // note that each bundler is likely to replace this one with
 135     // their own converter
 136     static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
 137             new StandardBundlerParam<>(
 138                     I18N.getString("param.main-jar.name"),
 139                     I18N.getString("param.main-jar.description"),
 140                     Arguments.CLIOptions.MAIN_JAR.getId(),
 141                     RelativeFileSet.class,
 142                     params -> {
 143                         extractMainClassInfoFromAppResources(params);
 144                         return (RelativeFileSet) params.get("mainJar");
 145                     },
 146                     (s, p) -> getMainJar(s, p)
 147             );
 148 
 149     // TODO: test CLASSPATH jar manifest Attributet
 150     static final StandardBundlerParam<String> CLASSPATH =
 151             new StandardBundlerParam<>(
 152                     I18N.getString("param.classpath.name"),
 153                     I18N.getString("param.classpath.description"),
 154                     "classpath",
 155                     String.class,
 156                     params -> {
 157                         extractMainClassInfoFromAppResources(params);
 158                         String cp = (String) params.get("classpath");
 159                         return cp == null ? "" : cp;
 160                     },
 161                     (s, p) -> s.replace(File.pathSeparator, " ")
 162             );
 163 
 164     static final StandardBundlerParam<Boolean> RUNTIME_INSTALLER  =
 165             new StandardBundlerParam<>(
 166                     "",
 167                     "",
 168                     Arguments.CLIOptions.RUNTIME_INSTALLER.getId(),
 169                     Boolean.class,
 170                     params -> false,
 171                     // valueOf(null) is false, and we actually do want null
 172                     (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
 173                             true : Boolean.valueOf(s)
 174             );
 175 
 176 
 177     static final StandardBundlerParam<String> MAIN_CLASS =
 178             new StandardBundlerParam<>(
 179                     I18N.getString("param.main-class.name"),
 180                     I18N.getString("param.main-class.description"),
 181                     Arguments.CLIOptions.APPCLASS.getId(),
 182                     String.class,
 183                     params -> {
 184                         if (RUNTIME_INSTALLER.fetchFrom(params)) {
 185                             return null;
 186                         }
 187                         extractMainClassInfoFromAppResources(params);
 188                         String s = (String) params.get(
 189                                 BundleParams.PARAM_APPLICATION_CLASS);
 190                         if (s == null) {
 191                             s = JLinkBundlerHelper.getMainClass(params);
 192                         }
 193                         return s;
 194                     },
 195                     (s, p) -> s
 196             );
 197 
 198     static final StandardBundlerParam<String> APP_NAME =
 199             new StandardBundlerParam<>(
 200                     I18N.getString("param.app-name.name"),
 201                     I18N.getString("param.app-name.description"),
 202                     Arguments.CLIOptions.NAME.getId(),
 203                     String.class,
 204                     params -> {
 205                         String s = MAIN_CLASS.fetchFrom(params);
 206                         if (s == null) return null;
 207 
 208                         int idx = s.lastIndexOf(".");
 209                         if (idx >= 0) {
 210                             return s.substring(idx+1);
 211                         }
 212                         return s;
 213                     },
 214                     (s, p) -> s
 215             );
 216 
 217     static final StandardBundlerParam<File> ICON =
 218             new StandardBundlerParam<>(
 219                     I18N.getString("param.icon-file.name"),
 220                     I18N.getString("param.icon-file.description"),
 221                     Arguments.CLIOptions.ICON.getId(),
 222                     File.class,
 223                     params -> null,
 224                     (s, p) -> new File(s)
 225             );
 226 
 227     static final StandardBundlerParam<String> VENDOR =
 228             new StandardBundlerParam<>(
 229                     I18N.getString("param.vendor.name"),
 230                     I18N.getString("param.vendor.description"),
 231                     Arguments.CLIOptions.VENDOR.getId(),
 232                     String.class,
 233                     params -> I18N.getString("param.vendor.default"),
 234                     (s, p) -> s
 235             );
 236 
 237     static final StandardBundlerParam<String> CATEGORY =
 238             new StandardBundlerParam<>(
 239                     I18N.getString("param.category.name"),
 240                     I18N.getString("param.category.description"),
 241                    Arguments.CLIOptions.CATEGORY.getId(),
 242                     String.class,
 243                     params -> I18N.getString("param.category.default"),
 244                     (s, p) -> s
 245             );
 246 
 247     static final StandardBundlerParam<String> DESCRIPTION =
 248             new StandardBundlerParam<>(
 249                     I18N.getString("param.description.name"),
 250                     I18N.getString("param.description.description"),
 251                     Arguments.CLIOptions.DESCRIPTION.getId(),
 252                     String.class,
 253                     params -> params.containsKey(APP_NAME.getID())
 254                             ? APP_NAME.fetchFrom(params)
 255                             : I18N.getString("param.description.default"),
 256                     (s, p) -> s
 257             );
 258 
 259     static final StandardBundlerParam<String> COPYRIGHT =
 260             new StandardBundlerParam<>(
 261                     I18N.getString("param.copyright.name"),
 262                     I18N.getString("param.copyright.description"),
 263                     Arguments.CLIOptions.COPYRIGHT.getId(),
 264                     String.class,
 265                     params -> MessageFormat.format(I18N.getString(
 266                             "param.copyright.default"), new Date()),
 267                     (s, p) -> s
 268             );
 269 
 270     @SuppressWarnings("unchecked")
 271     static final StandardBundlerParam<List<String>> ARGUMENTS =
 272             new StandardBundlerParam<>(
 273                     I18N.getString("param.arguments.name"),
 274                     I18N.getString("param.arguments.description"),
 275                     Arguments.CLIOptions.ARGUMENTS.getId(),
 276                     (Class<List<String>>) (Object) List.class,
 277                     params -> Collections.emptyList(),
 278                     (s, p) -> splitStringWithEscapes(s)
 279             );
 280 
 281     @SuppressWarnings("unchecked")
 282     static final StandardBundlerParam<List<String>> JVM_OPTIONS =
 283             new StandardBundlerParam<>(
 284                     I18N.getString("param.jvm-options.name"),
 285                     I18N.getString("param.jvm-options.description"),
 286                     Arguments.CLIOptions.JVM_ARGS.getId(),
 287                     (Class<List<String>>) (Object) List.class,
 288                     params -> Collections.emptyList(),
 289                     (s, p) -> Arrays.asList(s.split("\n\n"))
 290             );
 291 
 292     static final StandardBundlerParam<String> TITLE =
 293             new StandardBundlerParam<>(
 294                     I18N.getString("param.title.name"),
 295                     I18N.getString("param.title.description"),
 296                     BundleParams.PARAM_TITLE,
 297                     String.class,
 298                     APP_NAME::fetchFrom,
 299                     (s, p) -> s
 300             );
 301 
 302     // note that each bundler is likely to replace this one with
 303     // their own converter
 304     static final StandardBundlerParam<String> VERSION =
 305             new StandardBundlerParam<>(
 306                     I18N.getString("param.version.name"),
 307                     I18N.getString("param.version.description"),
 308                     Arguments.CLIOptions.VERSION.getId(),
 309                     String.class,
 310                     params -> I18N.getString("param.version.default"),
 311                     (s, p) -> s
 312             );
 313 
 314     @SuppressWarnings("unchecked")
 315     public static final StandardBundlerParam<String> LICENSE_FILE =
 316             new StandardBundlerParam<>(
 317                     I18N.getString("param.license-file.name"),
 318                     I18N.getString("param.license-file.description"),
 319                     Arguments.CLIOptions.LICENSE_FILE.getId(),
 320                     String.class,
 321                     params -> null,
 322                     (s, p) -> s
 323             );
 324 
 325     static final StandardBundlerParam<File> BUILD_ROOT =
 326             new StandardBundlerParam<>(
 327                     I18N.getString("param.build-root.name"),
 328                     I18N.getString("param.build-root.description"),
 329                     Arguments.CLIOptions.BUILD_ROOT.getId(),
 330                     File.class,
 331                     params -> {
 332                         try {
 333                             return Files.createTempDirectory(
 334                                     "jdk.jpackage").toFile();
 335                         } catch (IOException ioe) {
 336                             return null;
 337                         }
 338                     },
 339                     (s, p) -> new File(s)
 340             );
 341 
 342     public static final StandardBundlerParam<File> CONFIG_ROOT =
 343             new StandardBundlerParam<>(
 344                 I18N.getString("param.config-root.name"),
 345                 I18N.getString("param.config-root.description"),
 346                 "configRoot",
 347                 File.class,
 348                 params -> {
 349                     File root =
 350                             new File(BUILD_ROOT.fetchFrom(params), "config");
 351                     root.mkdirs();
 352                     return root;
 353                 },
 354                 (s, p) -> null
 355             );
 356 
 357     static final StandardBundlerParam<String> IDENTIFIER =
 358             new StandardBundlerParam<>(
 359                     I18N.getString("param.identifier.name"),
 360                     I18N.getString("param.identifier.description"),
 361                     Arguments.CLIOptions.IDENTIFIER.getId(),
 362                     String.class,
 363                     params -> {
 364                         String s = MAIN_CLASS.fetchFrom(params);
 365                         if (s == null) return null;
 366 
 367                         int idx = s.lastIndexOf(".");
 368                         if (idx >= 1) {
 369                             return s.substring(0, idx);
 370                         }
 371                         return s;
 372                     },
 373                     (s, p) -> s
 374             );
 375 
 376     static final StandardBundlerParam<String> PREFERENCES_ID =
 377             new StandardBundlerParam<>(
 378                     I18N.getString("param.preferences-id.name"),
 379                     I18N.getString("param.preferences-id.description"),
 380                     "preferencesID",
 381                     String.class,
 382                     p -> Optional.ofNullable(IDENTIFIER.fetchFrom(p)).
 383                              orElse("").replace('.', '/'),
 384                     (s, p) -> s
 385             );
 386 
 387     static final StandardBundlerParam<Boolean> VERBOSE  =
 388             new StandardBundlerParam<>(
 389                     I18N.getString("param.verbose.name"),
 390                     I18N.getString("param.verbose.description"),
 391                     Arguments.CLIOptions.VERBOSE.getId(),
 392                     Boolean.class,
 393                     params -> false,
 394                     // valueOf(null) is false, and we actually do want null
 395                     (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
 396                             true : Boolean.valueOf(s)
 397             );
 398 
 399     static final StandardBundlerParam<Boolean> OVERWRITE  =
 400             new StandardBundlerParam<>(
 401                     I18N.getString("param.overwrite.name"),
 402                     I18N.getString("param.overwrite.description"),
 403                     Arguments.CLIOptions.OVERWRITE.getId(),
 404                     Boolean.class,
 405                     params -> false,
 406                     // valueOf(null) is false, and we actually do want null
 407                     (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
 408                             true : Boolean.valueOf(s)
 409             );
 410 
 411     static final StandardBundlerParam<File> RESOURCE_DIR =
 412             new StandardBundlerParam<>(
 413                     I18N.getString("param.resource-dir.name"),
 414                     I18N.getString("param.resource-dir.description"),
 415                     Arguments.CLIOptions.RESOURCE_DIR.getId(),
 416                     File.class,
 417                     params -> null,
 418                     (s, p) -> new File(s)
 419             );
 420 
 421     static final BundlerParamInfo<String> INSTALL_DIR =
 422             new StandardBundlerParam<>(
 423                     I18N.getString("param.install-dir.name"),
 424                     I18N.getString("param.install-dir.description"),
 425                     Arguments.CLIOptions.INSTALL_DIR.getId(),
 426                     String.class,
 427                      params -> null,
 428                     (s, p) -> s
 429     );
 430 
 431     static final StandardBundlerParam<File> PREDEFINED_APP_IMAGE =
 432             new StandardBundlerParam<>(
 433             I18N.getString("param.predefined-app-image.name"),
 434             I18N.getString("param.predefined-app-image.description"),
 435             Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(),
 436             File.class,
 437             params -> null,
 438             (s, p) -> new File(s));
 439 
 440     static final StandardBundlerParam<File> PREDEFINED_RUNTIME_IMAGE =
 441             new StandardBundlerParam<>(
 442             I18N.getString("param.predefined-runtime-image.name"),
 443             I18N.getString("param.predefined-runtime-image.description"),
 444             Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(),
 445             File.class,
 446             params -> null,
 447             (s, p) -> new File(s));
 448 
 449     @SuppressWarnings("unchecked")
 450     static final StandardBundlerParam<List<Map<String, ? super Object>>> SECONDARY_LAUNCHERS =
 451             new StandardBundlerParam<>(
 452                     I18N.getString("param.secondary-launchers.name"),
 453                     I18N.getString("param.secondary-launchers.description"),
 454                     Arguments.CLIOptions.SECONDARY_LAUNCHER.getId(),
 455                     (Class<List<Map<String, ? super Object>>>) (Object)
 456                             List.class,
 457                     params -> new ArrayList<>(1),
 458                     // valueOf(null) is false, and we actually do want null
 459                     (s, p) -> null
 460             );
 461 
 462     @SuppressWarnings("unchecked")
 463     static final StandardBundlerParam
 464             <List<Map<String, ? super Object>>> FILE_ASSOCIATIONS =
 465             new StandardBundlerParam<>(
 466                     I18N.getString("param.file-associations.name"),
 467                     I18N.getString("param.file-associations.description"),
 468                     Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(),
 469                     (Class<List<Map<String, ? super Object>>>) (Object)
 470                             List.class,
 471                     params -> new ArrayList<>(1),
 472                     // valueOf(null) is false, and we actually do want null
 473                     (s, p) -> null
 474             );
 475 
 476     @SuppressWarnings("unchecked")
 477     static final StandardBundlerParam<List<String>> FA_EXTENSIONS =
 478             new StandardBundlerParam<>(
 479                     I18N.getString("param.fa-extension.name"),
 480                     I18N.getString("param.fa-extension.description"),
 481                     "fileAssociation.extension",
 482                     (Class<List<String>>) (Object) List.class,
 483                     params -> null, // null means not matched to an extension
 484                     (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
 485             );
 486 
 487     @SuppressWarnings("unchecked")
 488     static final StandardBundlerParam<List<String>> FA_CONTENT_TYPE =
 489             new StandardBundlerParam<>(
 490                     I18N.getString("param.fa-content-type.name"),
 491                     I18N.getString("param.fa-content-type.description"),
 492                     "fileAssociation.contentType",
 493                     (Class<List<String>>) (Object) List.class,
 494                     params -> null,
 495                             // null means not matched to a content/mime type
 496                     (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
 497             );
 498 
 499     static final StandardBundlerParam<String> FA_DESCRIPTION =
 500             new StandardBundlerParam<>(
 501                     I18N.getString("param.fa-description.name"),
 502                     I18N.getString("param.fa-description.description"),
 503                     "fileAssociation.description",
 504                     String.class,
 505                     params -> APP_NAME.fetchFrom(params) + " File",
 506                     null
 507             );
 508 
 509     static final StandardBundlerParam<File> FA_ICON =
 510             new StandardBundlerParam<>(
 511                     I18N.getString("param.fa-icon.name"),
 512                     I18N.getString("param.fa-icon.description"),
 513                     "fileAssociation.icon",
 514                     File.class,
 515                     ICON::fetchFrom,
 516                     (s, p) -> new File(s)
 517             );
 518 
 519     @SuppressWarnings("unchecked")
 520     static final BundlerParamInfo<List<Path>> MODULE_PATH =
 521             new StandardBundlerParam<>(
 522                     I18N.getString("param.module-path.name"),
 523                     I18N.getString("param.module-path.description"),
 524                     Arguments.CLIOptions.MODULE_PATH.getId(),
 525                     (Class<List<Path>>) (Object)List.class,
 526                     p -> { return getDefaultModulePath(); },
 527                     (s, p) -> {
 528                         List<Path> modulePath = Arrays.asList(s
 529                                 .split(File.pathSeparator)).stream()
 530                                 .map(ss -> new File(ss).toPath())
 531                                 .collect(Collectors.toList());
 532                         Path javaBasePath = null;
 533                         if (modulePath != null) {
 534                             javaBasePath = JLinkBundlerHelper
 535                                     .findPathOfModule(modulePath, JAVABASEJMOD);
 536                         } else {
 537                             modulePath = new ArrayList<Path>();
 538                         }
 539 
 540                         // Add the default JDK module path to the module path.
 541                         if (javaBasePath == null) {
 542                             List<Path> jdkModulePath = getDefaultModulePath();
 543 
 544                             if (jdkModulePath != null) {
 545                                 modulePath.addAll(jdkModulePath);
 546                                 javaBasePath =
 547                                         JLinkBundlerHelper.findPathOfModule(
 548                                         modulePath, JAVABASEJMOD);
 549                             }
 550                         }
 551 
 552                         if (javaBasePath == null ||
 553                                 !Files.exists(javaBasePath)) {
 554                             Log.error(String.format(I18N.getString(
 555                                     "warning.no.jdk.modules.found")));
 556                         }
 557 
 558                         return modulePath;
 559                     });
 560 
 561     static final BundlerParamInfo<String> MODULE =
 562             new StandardBundlerParam<>(
 563                     I18N.getString("param.main.module.name"),
 564                     I18N.getString("param.main.module.description"),
 565                     Arguments.CLIOptions.MODULE.getId(),
 566                     String.class,
 567                     p -> null,
 568                     (s, p) -> {
 569                         return String.valueOf(s);
 570                     });
 571 
 572     @SuppressWarnings("unchecked")
 573     static final BundlerParamInfo<Set<String>> ADD_MODULES =
 574             new StandardBundlerParam<>(
 575                     I18N.getString("param.add-modules.name"),
 576                     I18N.getString("param.add-modules.description"),
 577                     Arguments.CLIOptions.ADD_MODULES.getId(),
 578                     (Class<Set<String>>) (Object) Set.class,
 579                     p -> new LinkedHashSet<String>(),
 580                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 581             );
 582 
 583     @SuppressWarnings("unchecked")
 584     static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
 585             new StandardBundlerParam<>(
 586                     I18N.getString("param.limit-modules.name"),
 587                     I18N.getString("param.limit-modules.description"),
 588                     "limit-modules",
 589                     (Class<Set<String>>) (Object) Set.class,
 590                     p -> new LinkedHashSet<String>(),
 591                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 592             );
 593 
 594     static final BundlerParamInfo<Boolean> STRIP_NATIVE_COMMANDS =
 595             new StandardBundlerParam<>(
 596                     I18N.getString("param.strip-executables.name"),
 597                     I18N.getString("param.strip-executables.description"),
 598                     Arguments.CLIOptions.STRIP_NATIVE_COMMANDS.getId(),
 599                     Boolean.class,
 600                     p -> Boolean.FALSE,
 601                     (s, p) -> Boolean.valueOf(s)
 602             );

 603 
 604     static File getPredefinedAppImage(Map<String, ? super Object> p) {
 605         File applicationImage = null;
 606         if (PREDEFINED_APP_IMAGE.fetchFrom(p) != null) {
 607             applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(p);
 608             Log.debug("Using App Image from " + applicationImage);
 609             if (!applicationImage.exists()) {
 610                 throw new RuntimeException(
 611                         MessageFormat.format(I18N.getString(
 612                                 "message.app-image-dir-does-not-exist"),
 613                                 PREDEFINED_APP_IMAGE.getID(),
 614                                 applicationImage.toString()));
 615             }
 616         }
 617         return applicationImage;
 618     }
 619 
 620     static void copyPredefinedRuntimeImage(
 621             Map<String, ? super Object> p,
 622             AbstractAppImageBuilder appBuilder)


 643         Path dest = appBuilder.getAppModsDir();
 644 
 645         if (dest != null) {
 646             for (Path mp : modulePath) {
 647                 if (!defaultModulePath.contains(mp)) {
 648                     Files.createDirectories(dest);
 649                     IOUtils.copyRecursive(mp, dest);
 650                 }
 651             }
 652         }
 653 
 654         appBuilder.prepareApplicationFiles();
 655     }
 656 
 657     static void extractMainClassInfoFromAppResources(
 658             Map<String, ? super Object> params) {
 659         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 660         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 661         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 662         boolean hasModule = params.containsKey(MODULE.getID());
 663         boolean runtimeInstaller =
 664                 params.containsKey(RUNTIME_INSTALLER.getID());
 665 
 666         if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule ||
 667                 runtimeInstaller) {
 668             return;
 669         }
 670 
 671         // it's a pair.
 672         // The [0] is the srcdir [1] is the file relative to sourcedir
 673         List<String[]> filesToCheck = new ArrayList<>();
 674 
 675         if (hasMainJar) {
 676             RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);
 677             for (String s : rfs.getIncludedFiles()) {
 678                 filesToCheck.add(
 679                         new String[] {rfs.getBaseDirectory().toString(), s});
 680             }
 681         } else if (hasMainJarClassPath) {
 682             for (String s : CLASSPATH.fetchFrom(params).split("\\s+")) {
 683                 if (APP_RESOURCES.fetchFrom(params) != null) {
 684                     filesToCheck.add(
 685                             new String[] {APP_RESOURCES.fetchFrom(params)
 686                             .getBaseDirectory().toString(), s});
 687                 }


 731                                     attrs.getValue(Attributes.Name.CLASS_PATH);
 732                             params.put(CLASSPATH.getID(),
 733                                     cp == null ? "" : cp);
 734                         }
 735                         break;
 736                     }
 737                 }
 738             } catch (IOException ignore) {
 739                 ignore.printStackTrace();
 740             }
 741         }
 742     }
 743 
 744     static void validateMainClassInfoFromAppResources(
 745             Map<String, ? super Object> params) throws ConfigException {
 746         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 747         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 748         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 749         boolean hasModule = params.containsKey(MODULE.getID());
 750         boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID());
 751         boolean runtimeInstaller =
 752                 params.containsKey(RUNTIME_INSTALLER.getID());
 753 
 754         if (hasMainClass && hasMainJar && hasMainJarClassPath ||
 755                hasModule || runtimeInstaller || hasAppImage) {
 756             return;
 757         }
 758 
 759         extractMainClassInfoFromAppResources(params);
 760 
 761         if (!params.containsKey(MAIN_CLASS.getID())) {
 762             if (hasMainJar) {
 763                 throw new ConfigException(
 764                         MessageFormat.format(I18N.getString(
 765                         "error.no-main-class-with-main-jar"),
 766                         MAIN_JAR.fetchFrom(params)),
 767                         MessageFormat.format(I18N.getString(
 768                         "error.no-main-class-with-main-jar.advice"),
 769                         MAIN_JAR.fetchFrom(params)));
 770             } else {
 771                 throw new ConfigException(
 772                         I18N.getString("error.no-main-class"),
 773                         I18N.getString("error.no-main-class.advice"));
 774             }
 775         }
 776     }
 777 
 778 
 779     private static List<String> splitStringWithEscapes(String s) {
 780         List<String> l = new ArrayList<>();
 781         StringBuilder current = new StringBuilder();
 782         boolean quoted = false;
 783         boolean escaped = false;
 784         for (char c : s.toCharArray()) {
 785             if (escaped) {
 786                 current.append(c);
 787             } else if ('"' == c) {
 788                 quoted = !quoted;
 789             } else if (!quoted && Character.isWhitespace(c)) {
 790                 l.add(current.toString());
 791                 current = new StringBuilder();
 792             } else {
 793                 current.append(c);
 794             }
 795         }
 796         l.add(current.toString());
 797         return l;




  41 import java.util.Date;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.LinkedHashSet;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Optional;
  48 import java.util.Properties;
  49 import java.util.ResourceBundle;
  50 import java.util.Set;
  51 import java.util.HashSet;
  52 import java.util.function.BiFunction;
  53 import java.util.function.Function;
  54 import java.util.jar.Attributes;
  55 import java.util.jar.JarFile;
  56 import java.util.jar.Manifest;
  57 import java.util.regex.Pattern;
  58 import java.util.stream.Collectors;
  59 
  60 /**
  61  * StandardBundlerParam
  62  *
  63  * A parameter to a bundler.
  64  *
  65  * Also contains static definitions of all of the common bundler parameters.
  66  * (additional platform specific and mode specific bundler parameters
  67  * are defined in each of the specific bundlers)
  68  *
  69  * Also contains static methods that operate on maps of parameters.
  70  */
  71 class StandardBundlerParam<T> extends BundlerParamInfo<T> {
  72 
  73     private static final ResourceBundle I18N = ResourceBundle.getBundle(
  74             "jdk.jpackage.internal.resources.MainResources");
  75     private static final String JAVABASEJMOD = "java.base.jmod";
  76 
  77     StandardBundlerParam(String id, Class<T> valueType,

  78             Function<Map<String, ? super Object>, T> defaultValueFunction,
  79             BiFunction<String, Map<String, ? super Object>, T> stringConverter)
  80     {


  81         this.id = id;
  82         this.valueType = valueType;
  83         this.defaultValueFunction = defaultValueFunction;
  84         this.stringConverter = stringConverter;
  85     }
  86 
  87     static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
  88             new StandardBundlerParam<>(


  89                     BundleParams.PARAM_APP_RESOURCES,
  90                     RelativeFileSet.class,
  91                     null, // no default.  Required parameter
  92                     null  // no string translation,
  93                           // tool must provide complex type
  94             );
  95 
  96     @SuppressWarnings("unchecked")
  97     static final
  98             StandardBundlerParam<List<RelativeFileSet>> APP_RESOURCES_LIST =
  99             new StandardBundlerParam<>(


 100                     BundleParams.PARAM_APP_RESOURCES + "List",
 101                     (Class<List<RelativeFileSet>>) (Object) List.class,
 102                     // Default is appResources, as a single item list
 103                     p -> new ArrayList<>(Collections.singletonList(
 104                             APP_RESOURCES.fetchFrom(p))),
 105                     StandardBundlerParam::createAppResourcesListFromString
 106             );
 107 
 108     static final StandardBundlerParam<String> SOURCE_DIR =
 109             new StandardBundlerParam<>(


 110                     Arguments.CLIOptions.INPUT.getId(),
 111                     String.class,
 112                     p -> null,
 113                     (s, p) -> {
 114                         String value = String.valueOf(s);
 115                         if (value.charAt(value.length() - 1) ==
 116                                 File.separatorChar) {
 117                             return value.substring(0, value.length() - 1);
 118                         }
 119                         else {
 120                             return value;
 121                         }
 122                     }
 123             );
 124 
 125     // note that each bundler is likely to replace this one with
 126     // their own converter
 127     static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
 128             new StandardBundlerParam<>(


 129                     Arguments.CLIOptions.MAIN_JAR.getId(),
 130                     RelativeFileSet.class,
 131                     params -> {
 132                         extractMainClassInfoFromAppResources(params);
 133                         return (RelativeFileSet) params.get("mainJar");
 134                     },
 135                     (s, p) -> getMainJar(s, p)
 136             );
 137 
 138     // TODO: test CLASSPATH jar manifest Attributet
 139     static final StandardBundlerParam<String> CLASSPATH =
 140             new StandardBundlerParam<>(


 141                     "classpath",
 142                     String.class,
 143                     params -> {
 144                         extractMainClassInfoFromAppResources(params);
 145                         String cp = (String) params.get("classpath");
 146                         return cp == null ? "" : cp;
 147                     },
 148                     (s, p) -> s.replace(File.pathSeparator, " ")
 149             );
 150 













 151     static final StandardBundlerParam<String> MAIN_CLASS =
 152             new StandardBundlerParam<>(


 153                     Arguments.CLIOptions.APPCLASS.getId(),
 154                     String.class,
 155                     params -> {
 156                         if (isRuntimeInstaller(params)) {
 157                             return null;
 158                         }
 159                         extractMainClassInfoFromAppResources(params);
 160                         String s = (String) params.get(
 161                                 BundleParams.PARAM_APPLICATION_CLASS);
 162                         if (s == null) {
 163                             s = JLinkBundlerHelper.getMainClass(params);
 164                         }
 165                         return s;
 166                     },
 167                     (s, p) -> s
 168             );
 169 
 170     static final StandardBundlerParam<String> APP_NAME =
 171             new StandardBundlerParam<>(


 172                     Arguments.CLIOptions.NAME.getId(),
 173                     String.class,
 174                     params -> {
 175                         String s = MAIN_CLASS.fetchFrom(params);
 176                         if (s == null) return null;
 177 
 178                         int idx = s.lastIndexOf(".");
 179                         if (idx >= 0) {
 180                             return s.substring(idx+1);
 181                         }
 182                         return s;
 183                     },
 184                     (s, p) -> s
 185             );
 186 
 187     static final StandardBundlerParam<File> ICON =
 188             new StandardBundlerParam<>(


 189                     Arguments.CLIOptions.ICON.getId(),
 190                     File.class,
 191                     params -> null,
 192                     (s, p) -> new File(s)
 193             );
 194 
 195     static final StandardBundlerParam<String> VENDOR =
 196             new StandardBundlerParam<>(


 197                     Arguments.CLIOptions.VENDOR.getId(),
 198                     String.class,
 199                     params -> I18N.getString("param.vendor.default"),
 200                     (s, p) -> s
 201             );
 202 
 203     static final StandardBundlerParam<String> CATEGORY =
 204             new StandardBundlerParam<>(


 205                    Arguments.CLIOptions.CATEGORY.getId(),
 206                     String.class,
 207                     params -> I18N.getString("param.category.default"),
 208                     (s, p) -> s
 209             );
 210 
 211     static final StandardBundlerParam<String> DESCRIPTION =
 212             new StandardBundlerParam<>(


 213                     Arguments.CLIOptions.DESCRIPTION.getId(),
 214                     String.class,
 215                     params -> params.containsKey(APP_NAME.getID())
 216                             ? APP_NAME.fetchFrom(params)
 217                             : I18N.getString("param.description.default"),
 218                     (s, p) -> s
 219             );
 220 
 221     static final StandardBundlerParam<String> COPYRIGHT =
 222             new StandardBundlerParam<>(


 223                     Arguments.CLIOptions.COPYRIGHT.getId(),
 224                     String.class,
 225                     params -> MessageFormat.format(I18N.getString(
 226                             "param.copyright.default"), new Date()),
 227                     (s, p) -> s
 228             );
 229 
 230     @SuppressWarnings("unchecked")
 231     static final StandardBundlerParam<List<String>> ARGUMENTS =
 232             new StandardBundlerParam<>(


 233                     Arguments.CLIOptions.ARGUMENTS.getId(),
 234                     (Class<List<String>>) (Object) List.class,
 235                     params -> Collections.emptyList(),
 236                     (s, p) -> splitStringWithEscapes(s)
 237             );
 238 
 239     @SuppressWarnings("unchecked")
 240     static final StandardBundlerParam<List<String>> JVM_OPTIONS =
 241             new StandardBundlerParam<>(


 242                     Arguments.CLIOptions.JVM_ARGS.getId(),
 243                     (Class<List<String>>) (Object) List.class,
 244                     params -> Collections.emptyList(),
 245                     (s, p) -> Arrays.asList(s.split("\n\n"))
 246             );
 247 
 248     static final StandardBundlerParam<String> TITLE =
 249             new StandardBundlerParam<>(


 250                     BundleParams.PARAM_TITLE,
 251                     String.class,
 252                     APP_NAME::fetchFrom,
 253                     (s, p) -> s
 254             );
 255 
 256     // note that each bundler is likely to replace this one with
 257     // their own converter
 258     static final StandardBundlerParam<String> VERSION =
 259             new StandardBundlerParam<>(


 260                     Arguments.CLIOptions.VERSION.getId(),
 261                     String.class,
 262                     params -> I18N.getString("param.version.default"),
 263                     (s, p) -> s
 264             );
 265 
 266     @SuppressWarnings("unchecked")
 267     public static final StandardBundlerParam<String> LICENSE_FILE =
 268             new StandardBundlerParam<>(


 269                     Arguments.CLIOptions.LICENSE_FILE.getId(),
 270                     String.class,
 271                     params -> null,
 272                     (s, p) -> s
 273             );
 274 
 275     static final StandardBundlerParam<File> TEMP_ROOT =
 276             new StandardBundlerParam<>(
 277                     Arguments.CLIOptions.TEMP_ROOT.getId(),


 278                     File.class,
 279                     params -> {
 280                         try {
 281                             return Files.createTempDirectory(
 282                                     "jdk.jpackage").toFile();
 283                         } catch (IOException ioe) {
 284                             return null;
 285                         }
 286                     },
 287                     (s, p) -> new File(s)
 288             );
 289 
 290     public static final StandardBundlerParam<File> CONFIG_ROOT =
 291             new StandardBundlerParam<>(


 292                 "configRoot",
 293                 File.class,
 294                 params -> {
 295                     File root =
 296                             new File(TEMP_ROOT.fetchFrom(params), "config");
 297                     root.mkdirs();
 298                     return root;
 299                 },
 300                 (s, p) -> null
 301             );
 302 
 303     static final StandardBundlerParam<String> IDENTIFIER =
 304             new StandardBundlerParam<>(


 305                     Arguments.CLIOptions.IDENTIFIER.getId(),
 306                     String.class,
 307                     params -> {
 308                         String s = MAIN_CLASS.fetchFrom(params);
 309                         if (s == null) return null;
 310 
 311                         int idx = s.lastIndexOf(".");
 312                         if (idx >= 1) {
 313                             return s.substring(0, idx);
 314                         }
 315                         return s;
 316                     },
 317                     (s, p) -> s
 318             );
 319 
 320     static final StandardBundlerParam<String> PREFERENCES_ID =
 321             new StandardBundlerParam<>(


 322                     "preferencesID",
 323                     String.class,
 324                     p -> Optional.ofNullable(IDENTIFIER.fetchFrom(p)).
 325                              orElse("").replace('.', '/'),
 326                     (s, p) -> s
 327             );
 328 
 329     static final StandardBundlerParam<Boolean> VERBOSE  =
 330             new StandardBundlerParam<>(


 331                     Arguments.CLIOptions.VERBOSE.getId(),
 332                     Boolean.class,
 333                     params -> false,
 334                     // valueOf(null) is false, and we actually do want null
 335                     (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
 336                             true : Boolean.valueOf(s)
 337             );
 338 












 339     static final StandardBundlerParam<File> RESOURCE_DIR =
 340             new StandardBundlerParam<>(


 341                     Arguments.CLIOptions.RESOURCE_DIR.getId(),
 342                     File.class,
 343                     params -> null,
 344                     (s, p) -> new File(s)
 345             );
 346 
 347     static final BundlerParamInfo<String> INSTALL_DIR =
 348             new StandardBundlerParam<>(


 349                     Arguments.CLIOptions.INSTALL_DIR.getId(),
 350                     String.class,
 351                      params -> null,
 352                     (s, p) -> s
 353     );
 354 
 355     static final StandardBundlerParam<File> PREDEFINED_APP_IMAGE =
 356             new StandardBundlerParam<>(


 357             Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(),
 358             File.class,
 359             params -> null,
 360             (s, p) -> new File(s));
 361 
 362     static final StandardBundlerParam<File> PREDEFINED_RUNTIME_IMAGE =
 363             new StandardBundlerParam<>(


 364             Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(),
 365             File.class,
 366             params -> null,
 367             (s, p) -> new File(s));
 368 
 369     @SuppressWarnings("unchecked")
 370     static final StandardBundlerParam<List<Map<String, ? super Object>>> ADD_LAUNCHERS =
 371             new StandardBundlerParam<>(
 372                     Arguments.CLIOptions.ADD_LAUNCHER.getId(),


 373                     (Class<List<Map<String, ? super Object>>>) (Object)
 374                             List.class,
 375                     params -> new ArrayList<>(1),
 376                     // valueOf(null) is false, and we actually do want null
 377                     (s, p) -> null
 378             );
 379 
 380     @SuppressWarnings("unchecked")
 381     static final StandardBundlerParam
 382             <List<Map<String, ? super Object>>> FILE_ASSOCIATIONS =
 383             new StandardBundlerParam<>(


 384                     Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(),
 385                     (Class<List<Map<String, ? super Object>>>) (Object)
 386                             List.class,
 387                     params -> new ArrayList<>(1),
 388                     // valueOf(null) is false, and we actually do want null
 389                     (s, p) -> null
 390             );
 391 
 392     @SuppressWarnings("unchecked")
 393     static final StandardBundlerParam<List<String>> FA_EXTENSIONS =
 394             new StandardBundlerParam<>(


 395                     "fileAssociation.extension",
 396                     (Class<List<String>>) (Object) List.class,
 397                     params -> null, // null means not matched to an extension
 398                     (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
 399             );
 400 
 401     @SuppressWarnings("unchecked")
 402     static final StandardBundlerParam<List<String>> FA_CONTENT_TYPE =
 403             new StandardBundlerParam<>(


 404                     "fileAssociation.contentType",
 405                     (Class<List<String>>) (Object) List.class,
 406                     params -> null,
 407                             // null means not matched to a content/mime type
 408                     (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
 409             );
 410 
 411     static final StandardBundlerParam<String> FA_DESCRIPTION =
 412             new StandardBundlerParam<>(


 413                     "fileAssociation.description",
 414                     String.class,
 415                     params -> APP_NAME.fetchFrom(params) + " File",
 416                     null
 417             );
 418 
 419     static final StandardBundlerParam<File> FA_ICON =
 420             new StandardBundlerParam<>(


 421                     "fileAssociation.icon",
 422                     File.class,
 423                     ICON::fetchFrom,
 424                     (s, p) -> new File(s)
 425             );
 426 
 427     @SuppressWarnings("unchecked")
 428     static final BundlerParamInfo<List<Path>> MODULE_PATH =
 429             new StandardBundlerParam<>(


 430                     Arguments.CLIOptions.MODULE_PATH.getId(),
 431                     (Class<List<Path>>) (Object)List.class,
 432                     p -> { return getDefaultModulePath(); },
 433                     (s, p) -> {
 434                         List<Path> modulePath = Arrays.asList(s
 435                                 .split(File.pathSeparator)).stream()
 436                                 .map(ss -> new File(ss).toPath())
 437                                 .collect(Collectors.toList());
 438                         Path javaBasePath = null;
 439                         if (modulePath != null) {
 440                             javaBasePath = JLinkBundlerHelper
 441                                     .findPathOfModule(modulePath, JAVABASEJMOD);
 442                         } else {
 443                             modulePath = new ArrayList<Path>();
 444                         }
 445 
 446                         // Add the default JDK module path to the module path.
 447                         if (javaBasePath == null) {
 448                             List<Path> jdkModulePath = getDefaultModulePath();
 449 
 450                             if (jdkModulePath != null) {
 451                                 modulePath.addAll(jdkModulePath);
 452                                 javaBasePath =
 453                                         JLinkBundlerHelper.findPathOfModule(
 454                                         modulePath, JAVABASEJMOD);
 455                             }
 456                         }
 457 
 458                         if (javaBasePath == null ||
 459                                 !Files.exists(javaBasePath)) {
 460                             Log.error(String.format(I18N.getString(
 461                                     "warning.no.jdk.modules.found")));
 462                         }
 463 
 464                         return modulePath;
 465                     });
 466 
 467     static final BundlerParamInfo<String> MODULE =
 468             new StandardBundlerParam<>(


 469                     Arguments.CLIOptions.MODULE.getId(),
 470                     String.class,
 471                     p -> null,
 472                     (s, p) -> {
 473                         return String.valueOf(s);
 474                     });
 475 
 476     @SuppressWarnings("unchecked")
 477     static final BundlerParamInfo<Set<String>> ADD_MODULES =
 478             new StandardBundlerParam<>(


 479                     Arguments.CLIOptions.ADD_MODULES.getId(),
 480                     (Class<Set<String>>) (Object) Set.class,
 481                     p -> new LinkedHashSet<String>(),
 482                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 483             );
 484 
 485     @SuppressWarnings("unchecked")
 486     static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
 487             new StandardBundlerParam<>(


 488                     "limit-modules",
 489                     (Class<Set<String>>) (Object) Set.class,
 490                     p -> new LinkedHashSet<String>(),
 491                     (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
 492             );
 493 
 494     static boolean isRuntimeInstaller(Map<String, ? super Object> p) {
 495         if (p.containsKey(MODULE.getID()) ||
 496                 p.containsKey(MAIN_JAR.getID()) ||
 497                 p.containsKey(PREDEFINED_APP_IMAGE.getID())) {
 498             return false; // we are building or are given an application
 499         }
 500         // runtime installer requires --runtime-image, if this is false
 501         // here then we should have thrown error validating args.
 502         return p.containsKey(PREDEFINED_RUNTIME_IMAGE.getID());
 503     }
 504 
 505     static File getPredefinedAppImage(Map<String, ? super Object> p) {
 506         File applicationImage = null;
 507         if (PREDEFINED_APP_IMAGE.fetchFrom(p) != null) {
 508             applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(p);
 509             Log.debug("Using App Image from " + applicationImage);
 510             if (!applicationImage.exists()) {
 511                 throw new RuntimeException(
 512                         MessageFormat.format(I18N.getString(
 513                                 "message.app-image-dir-does-not-exist"),
 514                                 PREDEFINED_APP_IMAGE.getID(),
 515                                 applicationImage.toString()));
 516             }
 517         }
 518         return applicationImage;
 519     }
 520 
 521     static void copyPredefinedRuntimeImage(
 522             Map<String, ? super Object> p,
 523             AbstractAppImageBuilder appBuilder)


 544         Path dest = appBuilder.getAppModsDir();
 545 
 546         if (dest != null) {
 547             for (Path mp : modulePath) {
 548                 if (!defaultModulePath.contains(mp)) {
 549                     Files.createDirectories(dest);
 550                     IOUtils.copyRecursive(mp, dest);
 551                 }
 552             }
 553         }
 554 
 555         appBuilder.prepareApplicationFiles();
 556     }
 557 
 558     static void extractMainClassInfoFromAppResources(
 559             Map<String, ? super Object> params) {
 560         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 561         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 562         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 563         boolean hasModule = params.containsKey(MODULE.getID());


 564 
 565         if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule ||
 566                 isRuntimeInstaller(params)) {
 567             return;
 568         }
 569 
 570         // it's a pair.
 571         // The [0] is the srcdir [1] is the file relative to sourcedir
 572         List<String[]> filesToCheck = new ArrayList<>();
 573 
 574         if (hasMainJar) {
 575             RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);
 576             for (String s : rfs.getIncludedFiles()) {
 577                 filesToCheck.add(
 578                         new String[] {rfs.getBaseDirectory().toString(), s});
 579             }
 580         } else if (hasMainJarClassPath) {
 581             for (String s : CLASSPATH.fetchFrom(params).split("\\s+")) {
 582                 if (APP_RESOURCES.fetchFrom(params) != null) {
 583                     filesToCheck.add(
 584                             new String[] {APP_RESOURCES.fetchFrom(params)
 585                             .getBaseDirectory().toString(), s});
 586                 }


 630                                     attrs.getValue(Attributes.Name.CLASS_PATH);
 631                             params.put(CLASSPATH.getID(),
 632                                     cp == null ? "" : cp);
 633                         }
 634                         break;
 635                     }
 636                 }
 637             } catch (IOException ignore) {
 638                 ignore.printStackTrace();
 639             }
 640         }
 641     }
 642 
 643     static void validateMainClassInfoFromAppResources(
 644             Map<String, ? super Object> params) throws ConfigException {
 645         boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
 646         boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
 647         boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
 648         boolean hasModule = params.containsKey(MODULE.getID());
 649         boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID());


 650 
 651         if (hasMainClass && hasMainJar && hasMainJarClassPath ||
 652                hasModule || hasAppImage || isRuntimeInstaller(params)) {
 653             return;
 654         }
 655 
 656         extractMainClassInfoFromAppResources(params);
 657 
 658         if (!params.containsKey(MAIN_CLASS.getID())) {
 659             if (hasMainJar) {
 660                 throw new ConfigException(
 661                         MessageFormat.format(I18N.getString(
 662                         "error.no-main-class-with-main-jar"),
 663                         MAIN_JAR.fetchFrom(params)),
 664                         MessageFormat.format(I18N.getString(
 665                         "error.no-main-class-with-main-jar.advice"),
 666                         MAIN_JAR.fetchFrom(params)));
 667             } else {
 668                 throw new ConfigException(
 669                         I18N.getString("error.no-main-class"),
 670                         I18N.getString("error.no-main-class.advice"));
 671             }
 672         }
 673     }

 674 
 675     private static List<String> splitStringWithEscapes(String s) {
 676         List<String> l = new ArrayList<>();
 677         StringBuilder current = new StringBuilder();
 678         boolean quoted = false;
 679         boolean escaped = false;
 680         for (char c : s.toCharArray()) {
 681             if (escaped) {
 682                 current.append(c);
 683             } else if ('"' == c) {
 684                 quoted = !quoted;
 685             } else if (!quoted && Character.isWhitespace(c)) {
 686                 l.add(current.toString());
 687                 current = new StringBuilder();
 688             } else {
 689                 current.append(c);
 690             }
 691         }
 692         l.add(current.toString());
 693         return l;


< prev index next >