1 /*
   2  * Copyright (c) 2011, 2015, 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 com.sun.javafx.tools.packager;
  27 
  28 import com.oracle.tools.packager.*;
  29 import com.oracle.tools.packager.RelativeFileSet;
  30 import com.oracle.tools.packager.jnlp.JNLPBundler;
  31 import com.sun.javafx.tools.ant.Callback;
  32 import com.sun.javafx.tools.packager.bundlers.*;
  33 import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType;
  34 import java.io.File;
  35 import java.util.ArrayList;
  36 import java.util.Arrays;
  37 import java.util.Collection;
  38 import java.util.LinkedHashMap;
  39 import java.util.LinkedHashSet;
  40 import java.util.LinkedList;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Set;
  44 import java.util.TreeMap;
  45 import java.util.TreeSet;
  46 
  47 import static com.oracle.tools.packager.jnlp.JNLPBundler.*;
  48 
  49 public class DeployParams extends CommonParams {
  50     public enum RunMode {
  51         WEBSTART, EMBEDDED, STANDALONE, ALL
  52     }
  53 
  54     final List<RelativeFileSet> resources = new ArrayList<>();
  55 
  56     String id;
  57     String title;
  58     String vendor;
  59     String email;
  60     String description;
  61     String category;
  62     String licenseType;
  63     String copyright;
  64     String version;
  65     Boolean systemWide;
  66     Boolean serviceHint;
  67     Boolean signBundle;
  68     Boolean installdirChooser;
  69 
  70     String applicationClass;
  71     String preloader;
  72 
  73     List<Param> params;
  74     List<HtmlParam> htmlParams;
  75     List<String> arguments; //unnamed arguments
  76 
  77     // Java 9 modules support
  78     Set<String> addModules = new LinkedHashSet<>();
  79     Set<String> limitModules = new LinkedHashSet<>();
  80     boolean detectModules;
  81     String linkModulePath;
  82     String appModulePath;
  83 
  84     int width;
  85     int height;
  86     String embeddedWidth = null;
  87     String embeddedHeight = null;
  88 
  89     String appName;
  90     String codebase;
  91 
  92     boolean embedJNLP = true;
  93     @Deprecated final boolean embedCertificates = false;
  94     boolean allPermissions = false;
  95     String updateMode = "background";
  96     boolean isExtension = false;
  97     boolean isSwingApp = false;
  98 
  99     Boolean needShortcut = null;
 100     Boolean needMenu = null;
 101     Boolean needInstall = null;
 102 
 103     String outfile;
 104     //if true then we cobundle js and image files needed
 105     // for web deployment with the application
 106     boolean includeDT;
 107 
 108     String placeholder = "'javafx-app-placeholder'";
 109     String appId = null;
 110 
 111     // didn't have a setter...
 112     boolean offlineAllowed = true;
 113 
 114     List<JSCallback> callbacks;
 115 
 116     //list of HTML templates to process
 117     List<Template> templates = new LinkedList<>();
 118 
 119     String jrePlatform = "1.8+";
 120     String fxPlatform = PackagerLib.JAVAFX_VERSION+"+";
 121     File javaRuntimeToUse = null;
 122     boolean javaRuntimeWasSet = false;
 123 
 124     //list of jvm args (in theory string can contain spaces and need to be escaped
 125     List<String> jvmargs = new LinkedList<>();
 126     Map<String, String> jvmUserArgs = new LinkedHashMap<>();
 127 
 128     //list of jvm properties (can also be passed as VM args
 129     // but keeping them separate make it a bit more convinient for JNLP generation)
 130     Map<String, String> properties = new LinkedHashMap<>();
 131 
 132     // raw arguments to the bundler
 133     Map<String, ? super Object> bundlerArguments = new LinkedHashMap<>();
 134 
 135     String fallbackApp = null;
 136 
 137     public void setJavaRuntimeSource(File src) {
 138         javaRuntimeToUse = src;
 139         javaRuntimeWasSet = true;
 140     }
 141 
 142     public void setCodebase(String codebase) {
 143         this.codebase = codebase;
 144     }
 145 
 146     public void setId(String id) {
 147         this.id = id;
 148     }
 149 
 150     public void setCategory(String category) {
 151         this.category = category;
 152     }
 153 
 154     public void setLicenseType(String licenseType) {
 155         this.licenseType = licenseType;
 156     }
 157 
 158     public void setCopyright(String copyright) {
 159         this.copyright = copyright;
 160     }
 161 
 162     public void setVersion(String version) {
 163         this.version = version;
 164     }
 165 
 166     public void setSystemWide(Boolean systemWide) {
 167         this.systemWide = systemWide;
 168     }
 169 
 170     public void setServiceHint(Boolean serviceHint) {
 171         this.serviceHint = serviceHint;
 172     }
 173 
 174     public void setInstalldirChooser(Boolean installdirChooser) {
 175         this.installdirChooser = installdirChooser;
 176     }
 177 
 178     public void setSignBundle(Boolean signBundle) {
 179         this.signBundle = signBundle;
 180     }
 181 
 182     public void setJRE(String v) {
 183         jrePlatform = v;
 184     }
 185 
 186     public void setSwingAppWithEmbeddedJavaFX(boolean v) {
 187         isSwingApp = v;
 188     }
 189 
 190     public void setNeedInstall(boolean b) {
 191         needInstall = b;
 192     }
 193 
 194     public void setOfflineAllowed(boolean b) {
 195         offlineAllowed = b;
 196     }
 197 
 198     public void setNeedShortcut(Boolean b) {
 199         needShortcut = b;
 200     }
 201 
 202     public void setNeedMenu(Boolean b) {
 203         needMenu = b;
 204     }
 205 
 206     public void setEmbeddedDimensions(String w, String h) {
 207         embeddedWidth = w;
 208         embeddedHeight = h;
 209     }
 210 
 211     public void setFallback(String v) {
 212         if (v == null) {
 213             return;
 214         }
 215 
 216         if ("none".equals(v) || "null".equals(v)) {
 217             fallbackApp = null;
 218         } else {
 219             fallbackApp = v;
 220         }
 221     }
 222 
 223     public void setJavafx(String v) {
 224         fxPlatform = v;
 225     }
 226 
 227     public void addJvmArg(String v) {
 228         jvmargs.add(v);
 229     }
 230 
 231     public void addJvmUserArg(String n, String v) {
 232         jvmUserArgs.put(n, v);
 233     }
 234 
 235     public void addJvmProperty(String n, String v) {
 236         properties.put(n, v);
 237     }
 238 
 239     public void setAllPermissions(boolean allPermissions) {
 240         this.allPermissions = allPermissions;
 241     }
 242 
 243     public void setAppName(String appName) {
 244         this.appName = appName;
 245     }
 246 
 247     public void setArguments(List<String> args) {
 248         this.arguments = args;
 249     }
 250 
 251     public void addAddModule(String module) {
 252         addModules.add(module);
 253     }
 254 
 255     public void addLimitModule(String module) {
 256         limitModules.add(module);
 257     }
 258 
 259     public void setLinkModulePath(String linkModulePath) {
 260         this.linkModulePath = linkModulePath;
 261     }
 262 
 263     public void setAppModulePath(String appModulePath) {
 264         this.appModulePath = appModulePath;
 265     }
 266 
 267     public void setDetectModules(boolean detectModules) {
 268         this.detectModules = detectModules;
 269     }
 270 
 271     public void setDescription(String description) {
 272         this.description = description;
 273     }
 274 
 275     public void setEmbedJNLP(boolean embedJNLP) {
 276         this.embedJNLP = embedJNLP;
 277     }
 278 
 279     @Deprecated
 280     public void setEmbedCertifcates(boolean v) {
 281         if (v) {
 282             System.out.println("JavaFX Packager no longer supports embedding certificates in JNLP files.  Setting will be ignored.");
 283         }
 284     }
 285 
 286     public void setPlaceholder(String p) {
 287         placeholder = p;
 288     }
 289 
 290     public void setAppId(String id) {
 291         appId = id;
 292     }
 293 
 294     public void setHeight(int height) {
 295         this.height = height;
 296     }
 297 
 298     public void setHtmlParams(List<HtmlParam> htmlParams) {
 299         this.htmlParams = htmlParams;
 300     }
 301 
 302     public void setOutfile(String outfile) {
 303         this.outfile = outfile;
 304     }
 305 
 306     public void setParams(List<Param> params) {
 307         this.params = params;
 308     }
 309 
 310     public void setPreloader(String preloader) {
 311         this.preloader = preloader;
 312     }
 313 
 314     public void setTitle(String title) {
 315         this.title = title;
 316     }
 317 
 318     public void setUpdateMode(String updateMode) {
 319         this.updateMode = updateMode;
 320     }
 321 
 322     public void setVendor(String vendor) {
 323         this.vendor = vendor;
 324     }
 325 
 326     public void setEmail(String email) {
 327         this.email = email;
 328     }
 329 
 330     public void setWidth(int width) {
 331         this.width = width;
 332     }
 333 
 334     public void setExtension(boolean isExtension) {
 335         this.isExtension = isExtension;
 336     }
 337 
 338     public void setApplicationClass(String applicationClass) {
 339         this.applicationClass = applicationClass;
 340     }
 341 
 342     public void setIncludeDT(boolean doEmbed) {
 343         includeDT = doEmbed;
 344     }
 345 
 346     public void setJSCallbacks(List<JSCallback> list) {
 347         callbacks = list;
 348     }
 349 
 350     public void setCallbacks(List<Callback> list) {
 351         List<JSCallback> jslist = new ArrayList<>(list.size());
 352         for (Callback cb: list) {
 353             jslist.add(new JSCallback(cb.getName(), cb.getCmd()));
 354         }
 355         callbacks = jslist;
 356     }
 357 
 358     static class Template {
 359         File in;
 360         File out;
 361 
 362         Template(File in, File out) {
 363             this.in = in;
 364             this.out = out;
 365         }
 366     }
 367 
 368     public void addTemplate(File in, File out) {
 369         templates.add(new Template(in, out));
 370     }
 371 
 372     //we need to expand as in some cases
 373     // (most notably javapackager)
 374     //we may get "." as filename and assumption is we include
 375     // everything in the given folder
 376     // (IOUtils.copyfiles() have recursive behavior)
 377     List<File> expandFileset(File root) {
 378         List<File> files = new LinkedList<>();
 379         if (com.oracle.tools.packager.IOUtils.isNotSymbolicLink(root)) {
 380             if (root.isDirectory()) {
 381                 File[] children = root.listFiles();
 382                 if (children != null) {
 383                     for (File f : children) {
 384                         files.addAll(expandFileset(f));
 385                     }
 386                 }
 387             } else {
 388                 files.add(root);
 389             }
 390         }
 391         return files;
 392     }
 393 
 394     @Override
 395     public void addResource(File baseDir, String path) {
 396         File file = new File(baseDir, path);
 397         //normalize top level dir
 398         // to strip things like "." in the path
 399         // or it can confuse symlink detection logic
 400         file = file.getAbsoluteFile();
 401 
 402         if (baseDir == null) {
 403             baseDir = file.getParentFile();
 404         }
 405         resources.add(new RelativeFileSet(baseDir, new LinkedHashSet<>(expandFileset(file))));
 406     }
 407 
 408     @Override
 409     public void addResource(File baseDir, File file) {
 410         //normalize initial file
 411         // to strip things like "." in the path
 412         // or it can confuse symlink detection logic
 413         file = file.getAbsoluteFile();
 414 
 415         if (baseDir == null) {
 416             baseDir = file.getParentFile();
 417         }
 418         resources.add(new RelativeFileSet(baseDir, new LinkedHashSet<>(expandFileset(file))));
 419     }
 420 
 421     public void addResource(File baseDir, String path, String type) {
 422         addResource(baseDir, createFile(baseDir, path), type);
 423     }
 424 
 425     public void addResource(File baseDir, File file, String type) {
 426         addResource(baseDir, file, "eager", type, null, null);
 427     }
 428 
 429     public void addResource(File baseDir, File file, String mode, String type, String os, String arch) {
 430         Set<File> singleFile = new LinkedHashSet<>();
 431         singleFile.add(file);
 432         if (baseDir == null) {
 433             baseDir = file.getParentFile();
 434         }
 435         RelativeFileSet rfs = new RelativeFileSet(baseDir, singleFile);
 436         rfs.setArch(arch);
 437         rfs.setMode(mode);
 438         rfs.setOs(os);
 439         rfs.setType(parseTypeFromString(type, file));
 440         resources.add(rfs);
 441     }
 442 
 443     private RelativeFileSet.Type parseTypeFromString(String type, File file) {
 444         if (type == null) {
 445             if (file.getName().endsWith(".jar")) {
 446                 return RelativeFileSet.Type.jar;
 447             } else if (file.getName().endsWith(".jnlp")) {
 448                 return RelativeFileSet.Type.jnlp;
 449             } else {
 450                 return RelativeFileSet.Type.UNKNOWN;
 451             }
 452         } else {
 453             return RelativeFileSet.Type.valueOf(type);
 454         }
 455     }
 456 
 457     private static File createFile(final File baseDir, final String path) {
 458         final File testFile = new File(path);
 459         return testFile.isAbsolute()
 460                 ? testFile
 461                 : new File(baseDir == null
 462                     ? null
 463                     : baseDir.getAbsolutePath(),
 464                       path);
 465     }
 466 
 467 
 468     @Override
 469     public void validate() throws PackagerException {
 470         if (outdir == null) {
 471             throw new PackagerException("ERR_MissingArgument", "-outdir");
 472         }
 473         if (outfile == null) {
 474             throw new PackagerException("ERR_MissingArgument", "-outfile");
 475         }
 476         if (resources.isEmpty()) {
 477             throw new PackagerException("ERR_MissingAppResources");
 478         }
 479         if (applicationClass == null) {
 480             throw new PackagerException("ERR_MissingArgument", "-appclass");
 481         }
 482     }
 483 
 484     //could be icon or splash
 485     static class Icon {
 486         final static int UNDEFINED = -1;
 487 
 488         String href;
 489         String kind;
 490         int width = UNDEFINED;
 491         int height = UNDEFINED;
 492         int depth = UNDEFINED;
 493         RunMode mode = RunMode.WEBSTART;
 494 
 495         Icon(String href, String kind, int w, int h, int d, RunMode m) {
 496             mode = m;
 497             this.href = href;
 498             this.kind = kind;
 499             if (w > 0) {
 500                 width = w;
 501             }
 502             if (h > 0) {
 503                 height = h;
 504             }
 505             if (d > 0) {
 506                 depth = d;
 507             }
 508         }
 509     }
 510 
 511     List<Icon> icons = new LinkedList<>();
 512 
 513     public void addIcon(String href, String kind, int w, int h, int d, RunMode m) {
 514         icons.add(new Icon(href, kind, w, h, d, m));
 515     }
 516 
 517     BundleType bundleType = BundleType.NONE;
 518     String targetFormat = null; //means any
 519 
 520     public void setBundleType(BundleType type) {
 521         bundleType = type;
 522     }
 523 
 524     public BundleType getBundleType() {
 525         return bundleType;
 526     }
 527 
 528     public void setTargetFormat(String t) {
 529         targetFormat = t;
 530     }
 531 
 532     public String getTargetFormat() {
 533         return targetFormat;
 534     }
 535 
 536     private String getArch() {
 537         String arch = System.getProperty("os.arch").toLowerCase();
 538 
 539         if ("x86".equals(arch) || "i386".equals(arch) || "i486".equals(arch)
 540                 || "i586".equals(arch) || "i686".equals(arch)) {
 541             arch = "x86";
 542         } else if ("x86_64".equals(arch) || "amd64".equals("arch")) {
 543             arch = "x86_64";
 544         }
 545 
 546         return arch;
 547     }
 548 
 549     static final Set<String> multi_args = new TreeSet<>(Arrays.asList(
 550             StandardBundlerParam.JVM_PROPERTIES.getID(),
 551             StandardBundlerParam.JVM_OPTIONS.getID(),
 552             StandardBundlerParam.USER_JVM_OPTIONS.getID(),
 553             StandardBundlerParam.ARGUMENTS.getID(),
 554             JLinkBundlerHelper.LINK_MODULE_PATH.getID(),
 555             JLinkBundlerHelper.APP_MODULE_PATH.getID(),
 556             JLinkBundlerHelper.ADD_MODULES.getID(),
 557             JLinkBundlerHelper.LIMIT_MODULES.getID()
 558     ));
 559 
 560     @SuppressWarnings("unchecked")
 561     public void addBundleArgument(String key, Object value) {
 562         // special hack for multi-line arguments
 563         if (multi_args.contains(key) && value instanceof String) {
 564             Object existingValue = bundlerArguments.get(key);
 565             if (existingValue instanceof String) {
 566                 bundlerArguments.put(key, existingValue + "\n\n" + value);
 567             } else if (existingValue instanceof List) {
 568                 ((List)existingValue).add(value);
 569             } else if (existingValue instanceof Map && ((String)value).contains("=")) {
 570                 String[] mapValues = ((String)value).split("=", 2);
 571                 ((Map)existingValue).put(mapValues[0], mapValues[1]);
 572             } else {
 573                 bundlerArguments.put(key, value);
 574             }
 575         } else {
 576             bundlerArguments.put(key, value);
 577         }
 578     }
 579 
 580     public BundleParams getBundleParams() {
 581         BundleParams bundleParams = new BundleParams();
 582 
 583         //construct app resources
 584         //  relative to output folder!
 585         String currentOS = System.getProperty("os.name").toLowerCase();
 586         String currentArch = getArch();
 587 
 588         for (RelativeFileSet rfs : resources) {
 589             String os = rfs.getOs();
 590             String arch = rfs.getArch();
 591             //skip resources for other OS
 592             // and nativelib jars (we are including raw libraries)
 593             if ((os == null || currentOS.contains(os.toLowerCase())) &&
 594                     (arch == null || currentArch.startsWith(arch.toLowerCase()))
 595                     && rfs.getType() != RelativeFileSet.Type.nativelib) {
 596                 if (rfs.getType() == RelativeFileSet.Type.license) {
 597                     for (String s : rfs.getIncludedFiles()) {
 598                         bundleParams.addLicenseFile(s);
 599                     }
 600                 }
 601             }
 602         }
 603 
 604         bundleParams.setAppResourcesList(resources);
 605 
 606         bundleParams.setIdentifier(id);
 607 
 608         if (javaRuntimeWasSet) {
 609             bundleParams.setRuntime(javaRuntimeToUse);
 610         }
 611         bundleParams.setApplicationClass(applicationClass);
 612         bundleParams.setPrelaoderClass(preloader);
 613         bundleParams.setName(this.appName);
 614         bundleParams.setAppVersion(version);
 615         bundleParams.setType(bundleType);
 616         bundleParams.setBundleFormat(targetFormat);
 617         bundleParams.setVendor(vendor);
 618         bundleParams.setEmail(email);
 619         bundleParams.setShortcutHint(needShortcut);
 620         bundleParams.setMenuHint(needMenu);
 621         putUnlessNull(INSTALL_HINT.getID(), needInstall);
 622         bundleParams.setSystemWide(systemWide);
 623         bundleParams.setServiceHint(serviceHint);
 624         bundleParams.setInstalldirChooser(installdirChooser);
 625         bundleParams.setSignBundle(signBundle);
 626         bundleParams.setCopyright(copyright);
 627         bundleParams.setApplicationCategory(category);
 628         bundleParams.setLicenseType(licenseType);
 629         bundleParams.setDescription(description);
 630         bundleParams.setTitle(title);
 631         if (verbose) bundleParams.setVerbose(true);
 632 
 633         bundleParams.setJvmProperties(properties);
 634         bundleParams.setJvmargs(jvmargs);
 635         bundleParams.setJvmUserArgs(jvmUserArgs);
 636         bundleParams.setArguments(arguments);
 637 
 638         bundleParams.setAddModules(addModules);
 639         bundleParams.setLimitModules(limitModules);
 640         bundleParams.setDetectModules(detectModules);
 641         bundleParams.setAppModulePath(appModulePath);
 642         bundleParams.setLinkModulePath(linkModulePath);
 643 
 644         File appIcon = null;
 645         List<Map<String, ? super Object>> bundlerIcons = new ArrayList<>();
 646         for (Icon ic: icons) {
 647             //NB: in theory we should be paying attention to RunMode but
 648             // currently everything is marked as webstart internally and runmode
 649             // is not publicly documented property
 650             if (/* (ic.mode == RunMode.ALL || ic.mode == RunMode.STANDALONE) && */
 651                 (ic.kind == null || ic.kind.equals("default")))
 652             {
 653                 //could be full path or something relative to the output folder
 654                 appIcon = new File(ic.href);
 655                 if (!appIcon.exists()) {
 656                     com.oracle.tools.packager.Log.debug("Icon [" + ic.href + "] is not valid absolute path. " +
 657                             "Assume it is relative to the output dir.");
 658                     appIcon = new File(outdir, ic.href);
 659                 }
 660             }
 661 
 662             Map<String, ? super Object> iconInfo = new TreeMap<>();
 663             if (ic.href != null) iconInfo.put(ICONS_HREF.getID(), ic.href);
 664             if (ic.kind != null) iconInfo.put(ICONS_KIND.getID(), ic.kind);
 665             if (ic.width > 0)    iconInfo.put(ICONS_WIDTH.getID(), Integer.toString(ic.width));
 666             if (ic.height > 0)   iconInfo.put(ICONS_HEIGHT.getID(), Integer.toString(ic.height));
 667             if (ic.depth > 0)    iconInfo.put(ICONS_DEPTH.getID(), Integer.toString(ic.depth));
 668 
 669             if (!iconInfo.isEmpty()) bundlerIcons.add(iconInfo);
 670         }
 671         putUnlessNullOrEmpty(ICONS.getID(), bundlerIcons);
 672 
 673         bundleParams.setIcon(appIcon);
 674 
 675         Map<String, String> paramsMap = new TreeMap<>();
 676         if (params != null) {
 677             for (Param p : params) {
 678                 paramsMap.put(p.name, p.value);
 679             }
 680         }
 681         putUnlessNullOrEmpty(JNLPBundler.APP_PARAMS.getID(), paramsMap);
 682 
 683         Map<String, String> unescapedHtmlParams = new TreeMap<>();
 684         Map<String, String> escapedHtmlParams = new TreeMap<>();
 685         if (htmlParams != null) {
 686             for (HtmlParam hp : htmlParams) {
 687                 if (hp.needEscape) {
 688                     escapedHtmlParams.put(hp.name, hp.value);
 689                 } else {
 690                     unescapedHtmlParams.put(hp.name, hp.value);
 691                 }
 692             }
 693         }
 694         putUnlessNullOrEmpty(JNLPBundler.APPLET_PARAMS.getID(), unescapedHtmlParams);
 695         putUnlessNullOrEmpty(ESCAPED_APPLET_PARAMS.getID(), escapedHtmlParams);
 696 
 697 
 698         putUnlessNull(WIDTH.getID(), width);
 699         putUnlessNull(HEIGHT.getID(), height);
 700         putUnlessNull(EMBEDDED_WIDTH.getID(), embeddedWidth);
 701         putUnlessNull(EMBEDDED_HEIGHT.getID(), embeddedHeight);
 702 
 703         putUnlessNull(CODEBASE.getID(), codebase);
 704         putUnlessNull(EMBED_JNLP.getID(), embedJNLP);
 705         // embedCertificates
 706         putUnlessNull(ALL_PERMISSIONS.getID(), allPermissions);
 707         putUnlessNull(UPDATE_MODE.getID(), updateMode);
 708         putUnlessNull(EXTENSION.getID(), isExtension);
 709         putUnlessNull(SWING_APP.getID(), isSwingApp);
 710 
 711         putUnlessNull(OUT_FILE.getID(), outfile);
 712         putUnlessNull(INCLUDE_DT.getID(), includeDT);
 713         putUnlessNull(PLACEHOLDER.getID(), placeholder);
 714         putUnlessNull(OFFLINE_ALLOWED.getID(), offlineAllowed);
 715 
 716         Map<String, String> callbacksMap = new TreeMap<>();
 717         if (callbacks != null) {
 718             for (JSCallback callback : callbacks) {
 719                 callbacksMap.put(callback.getName(), callback.getCmd());
 720             }
 721         }
 722         putUnlessNull(JS_CALLBACKS.getID(), callbacksMap);
 723 
 724         Map<File, File> templatesMap = new TreeMap<>();
 725         if (templates != null) {
 726             for (Template template : templates) {
 727                 templatesMap.put(template.in, template.out);
 728             }
 729         }
 730         putUnlessNull(TEMPLATES.getID(), templatesMap);
 731 
 732         putUnlessNull(FX_PLATFORM.getID(), fxPlatform);
 733         putUnlessNull(JRE_PLATFORM.getID(), jrePlatform);
 734 
 735         putUnlessNull(FALLBACK_APP.getID(), fallbackApp);
 736 
 737         // check for collisions
 738         TreeSet<String> keys = new TreeSet<>(bundlerArguments.keySet());
 739         keys.retainAll(bundleParams.getBundleParamsAsMap().keySet());
 740 
 741         if (!keys.isEmpty()) {
 742             throw new RuntimeException("Deploy Params and Bundler Arguments overlap in the following values:" + keys.toString());
 743         }
 744 
 745         bundleParams.addAllBundleParams(bundlerArguments);
 746 
 747         return bundleParams;
 748     }
 749 
 750     public void putUnlessNull(String param, Object value) {
 751         if (value != null) {
 752             bundlerArguments.put(param, value);
 753         }
 754     }
 755 
 756     public void putUnlessNullOrEmpty(String param, Map<?, ?> value) {
 757         if (value != null && !value.isEmpty()) {
 758             bundlerArguments.put(param, value);
 759         }
 760     }
 761 
 762     public void putUnlessNullOrEmpty(String param, Collection<?> value) {
 763         if (value != null && !value.isEmpty()) {
 764             bundlerArguments.put(param, value);
 765         }
 766     }
 767 }