1 /*
   2  * Copyright (c) 2011, 2017, 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.ant;
  27 
  28 import java.io.File;
  29 import java.util.ArrayList;
  30 import java.util.LinkedList;
  31 import java.util.List;
  32 import java.util.Map;
  33 import java.util.stream.Collectors;
  34 
  35 import com.oracle.tools.packager.StandardBundlerParam;
  36 import com.sun.javafx.tools.ant.Platform.Jvmarg;
  37 import com.sun.javafx.tools.ant.Platform.Property;
  38 import com.sun.javafx.tools.packager.DeployParams;
  39 import com.oracle.tools.packager.Log;
  40 import com.sun.javafx.tools.packager.PackagerException;
  41 import com.sun.javafx.tools.packager.PackagerLib;
  42 import com.sun.javafx.tools.packager.bundlers.Bundler;
  43 import com.sun.javafx.tools.packager.bundlers.Bundler.Bundle;
  44 import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType;
  45 import java.util.ResourceBundle;
  46 import org.apache.tools.ant.BuildException;
  47 import org.apache.tools.ant.DynamicAttribute;
  48 import org.apache.tools.ant.Task;
  49 import org.apache.tools.ant.types.DataType;
  50 
  51 /**
  52  * Generates package for Web deployment and redistribution of application.
  53  * Package includes of set of jar files, JNLP file and HTML file.
  54  *
  55  * Minimal example:
  56  * <pre>
  57  *   &lt;fx:deploy width="600" height="400"
  58  *                 outdir="web-dist" outfile="Fish"&gt;
  59  *       &lt;info title="Sample application"/&gt;
  60  *       &lt;fx:application refid="myapp"/&gt;
  61  *       &lt;fx:resources refid="myresources"/&gt;
  62  *   &lt;/fx:deploy&gt;
  63  * </pre>
  64  * Above example will generate HTML/JNLP files into the web-dist directory
  65  * and use "Fish" as prefix for generated files. Details about application and
  66  * its resources are defined elsewhere in the application and resource elements.
  67  * <p>
  68  * Minimal complete example:
  69  * <pre>
  70  *   &lt;fx:deploy width="600" height="400"
  71  *                 outdir="web-dist" outfile="Fish"&gt;
  72  *       &lt;info title="Sample application"/&gt;
  73  *       &lt;fx:application name="SampleApp"
  74  *              mainClass="testapp.MainApp"
  75  *              preloaderClass="testpreloader.Preloader"/&gt;
  76  *       &lt;fx:resources&gt;
  77  *              &lt;fx:fileset requiredFor="preloader" dir="dist"&gt;
  78  *                &lt;include name="preloader.jar"/&gt;
  79  *             &lt;/fx:fileset&gt;
  80  *              &lt;fx:fileset dir="dist"&gt;
  81  *                &lt;include name="helloworld.jar"/&gt;
  82  *             &lt;/fx:fileset&gt;
  83  *       &lt;/fx:resources&gt;
  84  *   &lt;/fx:deploy&gt;
  85  * </pre>
  86  * Same as above but here application and resource details are defined in place.
  87  * Note that using references helps with reducing code duplication as fx:jar need
  88  * to be used for double clickable jars.
  89  *
  90  * @ant.task name="deploy" category="javafx"
  91  */
  92 public class DeployFXTask extends Task implements DynamicAttribute {
  93 
  94     private static final ResourceBundle I18N =
  95             ResourceBundle.getBundle(DeployFXTask.class.getName());
  96 
  97     private String width = null;
  98     private String height = null;
  99     private String embeddedWidth = null;
 100     private String embeddedHeight = null;
 101     private String outfile = null;
 102     private String outdir = null;
 103     private boolean embedJNLP;
 104     private boolean isExtension = false;
 105     private Boolean signBundle;
 106 
 107     //Before FCS default is to include DT files with app
 108     // to ensure tests are using latest and compatible.
 109     //After FCS default is to use shared copy.
 110     private boolean includeDT = false;
 111 
 112     private String updateMode="background";
 113     private Info appInfo = null;
 114     private Application app = null;
 115     private Resources resources = null;
 116     private Preferences prefs = null;
 117     private String codebase = null;
 118 
 119     //container to embed application into
 120     //could be either string id or js code. If it is string id then it needs to
 121     //be escaped
 122     private String placeholder;
 123 
 124     private PackagerLib packager;
 125     private DeployParams deployParams;
 126 
 127     private Callbacks callbacks;
 128 
 129     boolean offlineAllowed = true;
 130 
 131     //default native bundle settings
 132     // use NONE to avoid large disk space and build time overhead
 133     BundleType nativeBundles = BundleType.NONE;
 134     String bundleFormat = null;
 135     boolean versionCheck = true;
 136 
 137     private boolean verbose = false;
 138     public void setVerbose(boolean v) {
 139         verbose = v;
 140     }
 141 
 142     public void setCodebase(String str) {
 143         codebase = str;
 144     }
 145 
 146     public DeployFXTask() {
 147         packager = new PackagerLib();
 148         deployParams = new DeployParams();
 149     }
 150 
 151     @Override
 152     public void execute() {
 153         boolean isModular = (app.getModule() != null) && !app.getModule().isEmpty();
 154         deployParams.setOutfile(outfile);
 155         deployParams.setOutdir(new File(outdir));
 156 
 157         if (versionCheck) {
 158             if (!com.sun.javafx.tools.ant.VersionCheck.isSameVersion()) {
 159                 throw new BuildException(I18N.getString("message.java.version.mismatch"));
 160             }
 161         }
 162 
 163         if (!isModular &&
 164             (nativeBundles == BundleType.JNLP ||
 165              nativeBundles == BundleType.ALL ||
 166              nativeBundles == BundleType.NONE)) {
 167 
 168             setAppInfo();
 169 
 170             deployParams.setOfflineAllowed(offlineAllowed);
 171             deployParams.setVerbose(verbose);
 172             deployParams.setCodebase(codebase);
 173             deployParams.setSignBundle(signBundle);
 174 
 175             if (app != null && app.getModule() == null) {
 176                 deployParams.setApplicationClass(app.get().mainClass);
 177             }
 178 
 179             if (width != null) {
 180                 deployParams.setWidth(Integer.valueOf(width));
 181             }
 182 
 183             if (height != null) {
 184                 deployParams.setHeight(Integer.valueOf(height));
 185             }
 186 
 187             if (embeddedWidth != null && embeddedHeight != null) {
 188                 deployParams.setEmbeddedDimensions(embeddedWidth, embeddedHeight);
 189             }
 190 
 191             deployParams.setEmbedJNLP(embedJNLP);
 192             if (perms != null) {
 193                deployParams.setAllPermissions(perms.getElevated());
 194             }
 195 
 196             deployParams.setUpdateMode(updateMode);
 197             deployParams.setExtension(isExtension);
 198             deployParams.setIncludeDT(includeDT);
 199 
 200             if (callbacks != null) {
 201                 for (Callback cb: callbacks.callbacks) {
 202                     deployParams.addCallback(cb.getName(), cb.getCmd());
 203                 }
 204             }
 205 
 206             setPlatform();
 207             setPreferences();
 208 
 209             for (Template t: templateList) {
 210                 deployParams.addTemplate(t.infile, t.outfile);
 211             }
 212         }
 213 
 214         if (isModular &&
 215             (nativeBundles == BundleType.NATIVE ||
 216              nativeBundles == BundleType.IMAGE ||
 217              nativeBundles == BundleType.INSTALLER ||
 218              nativeBundles == BundleType.ALL)) {
 219             if (app != null) {
 220                 if (app.getModule() == null) {
 221                     deployParams.setApplicationClass(app.get().mainClass);
 222                 }
 223                 else {
 224                     int index = app.getModule().indexOf("/");
 225 
 226                     if (index > 0) {
 227                         deployParams.setModule(app.getModule());
 228                     }
 229                     else {
 230                         deployParams.setModule(app.getModule() + "/" + app.get().mainClass);
 231                     }
 232                 }
 233 
 234                 deployParams.setPreloader(app.get().preloaderClass);
 235                 deployParams.setAppId(app.get().id);
 236                 deployParams.setAppName(app.get().name);
 237                 deployParams.setParams(app.get().parameters);
 238                 deployParams.setArguments(app.get().getArguments());
 239                 deployParams.setHtmlParams(app.get().htmlParameters);
 240                 deployParams.setFallback(app.get().fallbackApp);
 241                 deployParams.setSwingAppWithEmbeddedJavaFX(app.get().embeddedIntoSwing);
 242                 deployParams.setVersion(app.get().version);
 243                 deployParams.setId(app.get().id);
 244                 deployParams.setServiceHint(app.get().daemon);
 245 
 246                 setRuntime();
 247             }
 248 
 249             setAppInfo();
 250 
 251             setPlatform();
 252             setPreferences();
 253         }
 254 
 255         if (!isModular &&
 256             (nativeBundles == BundleType.NATIVE ||
 257              nativeBundles == BundleType.IMAGE ||
 258              nativeBundles == BundleType.INSTALLER ||
 259              nativeBundles == BundleType.ALL)) {
 260             if (app != null) {
 261                 deployParams.setApplicationClass(app.get().mainClass);
 262                 deployParams.setPreloader(app.get().preloaderClass);
 263                 deployParams.setAppId(app.get().id);
 264                 deployParams.setAppName(app.get().name);
 265                 deployParams.setParams(app.get().parameters);
 266                 deployParams.setArguments(app.get().getArguments());
 267                 deployParams.setHtmlParams(app.get().htmlParameters);
 268                 deployParams.setFallback(app.get().fallbackApp);
 269                 deployParams.setSwingAppWithEmbeddedJavaFX(app.get().embeddedIntoSwing);
 270                 deployParams.setVersion(app.get().version);
 271                 deployParams.setId(app.get().id);
 272                 deployParams.setServiceHint(app.get().daemon);
 273 
 274                 setRuntime();
 275             }
 276 
 277             setAppInfo();
 278 
 279             setPlatform();
 280             setPreferences();
 281         }
 282 
 283         for (BundleArgument ba : bundleArgumentList) {
 284             deployParams.addBundleArgument(ba.arg, ba.value);
 285         }
 286 
 287         deployParams.setPlaceholder(placeholder);
 288 
 289         if (resources != null) {
 290             for (FileSet fs: resources.getResources()) {
 291                 Utils.addResources(deployParams, fs);
 292             }
 293         }
 294 
 295         List<Map<String, ? super Object>> launchersAsMap = new ArrayList<>();
 296         for (SecondaryLauncher sl : secondaryLaunchers) {
 297             launchersAsMap.add(sl.createLauncherMap());
 298         }
 299 
 300         deployParams.addBundleArgument(
 301                 StandardBundlerParam.SECONDARY_LAUNCHERS.getID(),
 302                 launchersAsMap);
 303 
 304         deployParams.setBundleType(nativeBundles);
 305         deployParams.setTargetFormat(bundleFormat);
 306 
 307         Log.setLogger(new AntLog(this.getProject()));
 308 
 309         try {
 310             packager.generateDeploymentPackages(deployParams);
 311         } catch (PackagerException pe) {
 312             if (pe.getCause() != null) {
 313                throw new BuildException(pe.getCause().getMessage(), pe.getCause());
 314             } else {
 315                 throw new BuildException(pe.getMessage(), pe);
 316             }
 317         } catch (Exception e) {
 318             throw new BuildException(e.getMessage(), e);
 319         } finally {
 320             Log.setLogger(null);
 321         }
 322     }
 323 
 324     private void setRuntime() {
 325         if (runtime != null) {
 326             for (String s : runtime.getAddModules()) {
 327                 deployParams.addAddModule(s);
 328             }
 329 
 330             for (String s : runtime.getLimitModules()) {
 331                 deployParams.addLimitModule(s);
 332             }
 333 
 334             deployParams.setModulePath(runtime.getModulePath());
 335 
 336             Boolean stripNativeCommands = runtime.getStripNativeCommands();
 337 
 338             if (stripNativeCommands != null) {
 339                 deployParams.setStripNativeCommands(stripNativeCommands);
 340             }
 341 
 342             Boolean detectModules = runtime.getDetectModules();
 343 
 344             if (detectModules != null) {
 345                 deployParams.setDetectModules(detectModules);
 346             }
 347         }
 348     }
 349 
 350     /**
 351      * Set to true if we are generating an 'extension' JNLP.
 352      *
 353      * @ant.not-required Default is false.
 354      */
 355     public void setExtension(boolean v) {
 356         isExtension = v;
 357     }
 358 
 359     public void setNativeBundles(String v) {
 360         Bundle bundle = Bundler.stringToBundle(v);
 361         this.nativeBundles = bundle.type;
 362         this.bundleFormat = bundle.format;
 363     }
 364 
 365     public void setVersionCheck(String value) {
 366         this.versionCheck = Boolean.valueOf(value);
 367     }
 368 
 369     /**
 370      * Indicates the preferences for when checks for application updates
 371      * are performed. Supported modes are always, timeout and background.
 372      *
 373      * @ant.not-required Default is background.
 374      */
 375     public void setUpdateMode(String v) {
 376         String l = v.toLowerCase();
 377         if ("eager".equals(l)) {
 378             //workaround for doc bug in 2.0
 379             l="always";
 380         }
 381         if (!"always".equals(l) && !"background".equals(l)
 382                 && !"timeout".equals(l)) {
 383             throw new BuildException("Unknown update mode: ["+l+"]." +
 384                     "Supported modes are: 'always', 'timeout' and 'background'");
 385         }
 386         updateMode = l;
 387     }
 388 
 389     /**
 390      * Indicates if the application can be launched offline.
 391      *
 392      * If application is already downloaded and update mode is eager then
 393      * the check will timeout after a few seconds, in which case the cached
 394      * application will be launched instead.
 395      *
 396      * Given a reasonably fast server connection,
 397      * the latest version of the application will usually be run,
 398      * but it is not guaranteed. The application, however, can be run offline.
 399      *
 400      * @ant.not-required Default is true.
 401      */
 402     public void setOfflineAllowed(boolean v) {
 403         offlineAllowed = v;
 404     }
 405 
 406     /**
 407      * Application width for embedding application into Web page
 408      *
 409      * @ant.optional
 410      */
 411     public void setEmbeddedWidth(String w) {
 412         embeddedWidth = w;
 413     }
 414 
 415     /**
 416      * Application width. Used for webstart and embedded applications
 417      * unless emdeddedWidth is specified
 418      *
 419      * @ant.required
 420      */
 421     public void setWidth(String v) {
 422         width = v;
 423     }
 424 
 425     /**
 426      * Application width for embedding application into Web page
 427      *
 428      * @ant.optional
 429      */
 430     public void setEmbeddedHeight(String w) {
 431         embeddedHeight = w;
 432     }
 433 
 434     /**
 435      * Application height. Used for webstart and embedded applications
 436      * unless emdeddedHeight is specified
 437      *
 438      * @ant.required
 439      */
 440     public void setHeight(String v) {
 441         height = v;
 442     }
 443 
 444     /**
 445      * Enable embedding JNLP descriptor into Web page.
 446      * Reduces number of network connections to be made on startup and
 447      * help to improve startup time.
 448      *
 449      * @ant.not-required Default is false.
 450      */
 451     public void setEmbedJNLP(boolean v) {
 452         embedJNLP = v;
 453     }
 454 
 455     /**
 456      * Directory where application package will be saved.
 457      *
 458      * @ant.required
 459      */
 460     public void setOutdir(String v) {
 461         outdir = v;
 462     }
 463 
 464     /**
 465      * Prefix to be used for new generated files.
 466      *
 467      * @ant.required
 468      */
 469     public void setOutfile(String v) {
 470         outfile = v;
 471     }
 472 
 473     /**
 474      * If true then web deployment is done using javascript files
 475      * on java.com. Otherwise copy of javascript file is included into
 476      * application package.
 477      *
 478      * @ant.not-required Before FCS default is false. For FCS default is true.
 479      */
 480     public void setIncludeDT(Boolean v) {
 481         includeDT = v;
 482     }
 483 
 484     /**
 485      * Placeholder in the web page where application will be embedded.
 486      * This is expected to be Javascript DOM object.
 487      *
 488      * @ant.required Either reference or id of placeholder is required.
 489      */
 490     public void setPlaceholderRef(String p) {
 491         this.placeholder = p;
 492     }
 493 
 494     /**
 495      * Id of the placeholder in the web page where application will be embedded.
 496      * Javascript's document.getElementById() is expected to be able to resolve it.
 497      *
 498      * @ant.required Either reference or id of placeholder is required.
 499      */
 500     public void setPlaceholderId(String id) {
 501         //raw id of the placeholder, need to escape it
 502         this.placeholder = "'"+id+"'";
 503     }
 504 
 505     public void setSignBundle(boolean signBundle) {
 506         this.signBundle = signBundle;
 507     }
 508 
 509     public Info createInfo() {
 510         appInfo = new Info();
 511         return appInfo;
 512     }
 513 
 514     public Application createApplication() {
 515         app = new Application();
 516         return app;
 517     }
 518 
 519     public Preferences createPreferences() {
 520         prefs = new Preferences();
 521         return prefs;
 522     }
 523 
 524     public Callbacks createCallbacks() {
 525         if (callbacks != null) {
 526             throw new BuildException("Only one callbacks element is supported.");
 527         }
 528         callbacks = new Callbacks();
 529         return callbacks;
 530     }
 531 
 532     public Resources createResources() {
 533         if (resources != null) {
 534             throw new BuildException("Only one resources element is supported.");
 535         }
 536         resources = new Resources();
 537         return resources;
 538     }
 539 
 540     List<Template> templateList = new LinkedList<>();
 541 
 542     public Template createTemplate() {
 543         Template t = new Template();
 544         templateList.add(t);
 545         return t;
 546     }
 547 
 548     Platform platform;
 549 
 550     public Platform createPlatform() {
 551         platform = new Platform();
 552         return platform;
 553     }
 554 
 555     private Permissions perms = null;
 556 
 557     public Permissions createPermissions() {
 558         perms = new Permissions();
 559         return perms;
 560     }
 561 
 562     List<BundleArgument> bundleArgumentList = new LinkedList<>();
 563 
 564     public BundleArgument createBundleArgument() {
 565         BundleArgument ba = new BundleArgument();
 566         bundleArgumentList.add(ba);
 567         return ba;
 568     }
 569 
 570     private List<SecondaryLauncher> secondaryLaunchers = new ArrayList<>();
 571 
 572     public SecondaryLauncher createSecondaryLauncher() {
 573         SecondaryLauncher sl = new SecondaryLauncher();
 574         secondaryLaunchers.add(sl);
 575         return sl;
 576     }
 577 
 578     private Runtime runtime = null;
 579 
 580     public Runtime createRuntime() {
 581         runtime = new Runtime();
 582         return runtime;
 583     }
 584 
 585     @Override
 586     public void setDynamicAttribute(String name, String value) throws BuildException {
 587         //Use qName and value - can't really validate anything until we know which bundlers we have, so this has
 588         //to done (way) downstream
 589         bundleArgumentList.add(new BundleArgument(name, value));
 590     }
 591 
 592     private void setPlatform() {
 593         if (platform != null) {
 594             Platform pl = platform.get();
 595             if (pl.j2se != null) {
 596                 deployParams.setJRE(pl.j2se);
 597             }
 598             if (pl.javafx != null) {
 599                 deployParams.setJavafx(pl.javafx);
 600             }
 601 
 602             //only pass it further if it was explicitly set
 603             // as we do not want to override default
 604             if (pl.javaRoot != null) {
 605                 if (Platform.USE_SYSTEM_JRE.equals(pl.javaRoot)) {
 606                     deployParams.setJavaRuntimeSource(null);
 607                 } else {
 608                     deployParams.setJavaRuntimeSource(new File(pl.javaRoot));
 609                 }
 610             }
 611 
 612             for (Property p: pl.properties) {
 613                 deployParams.addJvmProperty(p.name, p.value);
 614             }
 615             for (Jvmarg a: pl.jvmargs) {
 616                 deployParams.addJvmArg(a.value);
 617             }
 618             for (Property a: pl.jvmUserArgs) {
 619                 deployParams.addJvmUserArg(a.name, a.value);
 620             }
 621         }
 622     }
 623 
 624     private void setPreferences() {
 625         if (prefs != null) {
 626             deployParams.setNeedShortcut(prefs.getShortcut());
 627             deployParams.setNeedInstall(prefs.getInstall());
 628             deployParams.setNeedMenu(prefs.getMenu());
 629             deployParams.setSystemWide(prefs.getSystemInstall());
 630             deployParams.setInstalldirChooser(prefs.getInstalldirChooser());
 631         }
 632     }
 633 
 634     private void setAppInfo() {
 635         if (appInfo != null) {
 636             deployParams.setTitle(appInfo.title);
 637             deployParams.setVendor(appInfo.vendor);
 638             deployParams.setDescription(appInfo.appDescription);
 639             deployParams.setCategory(appInfo.category);
 640             deployParams.setLicenseType(appInfo.licenseType);
 641             deployParams.setCopyright(appInfo.copyright);
 642             deployParams.setEmail(appInfo.email);
 643 
 644             for (Info.Icon i: appInfo.icons) {
 645                 if (i instanceof Info.Splash) {
 646                    deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth,
 647                         ((Info.Splash) i).mode);
 648                 } else {
 649                    deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth,
 650                         DeployParams.RunMode.WEBSTART);
 651                 }
 652             }
 653 
 654             deployParams.addBundleArgument(StandardBundlerParam.FILE_ASSOCIATIONS.getID(),
 655                     appInfo.fileAssociations.stream()
 656                         .map(FileAssociation::createLauncherMap)
 657                         .collect(Collectors.toList()));
 658         }
 659     }
 660 
 661     /**
 662      * Template to preprocess.
 663      * <p>
 664      * Template is the HTML file containing markers to be replaced with
 665      * javascript or HTML snippets needed to deploy JavaFX application on the
 666      * Web page. This allows to deploy application into "real" Web pages
 667      * and simplify development process if application is tightly
 668      * integrated with the page (e.g. uses javascript to communicate to it).
 669      * <p>
 670      * Marker has the form of #XXX# or #XXX(id)#. Where id is identifier
 671      * of an application and XXX is one of following:
 672      * <ul>
 673      *   <li>DT.SCRIPT.URL - location of dtjava.js
 674      *   <li>DT.SCRIPT.CODE - script element to include dtjava.js
 675      *   <li>DT.EMBED.CODE.DYNAMIC - code to embed application into given placeholder
 676      *         It is expected it will be wrapped into function()
 677      *   <li>DT.EMBED.CODE.ONLOAD - all code needed to embed application into Web page
 678      *               using onload hook (except inclusion of dtjava.js)
 679      *   <li>DT.LAUNCH.CODE - code need to launch application.
 680      *          Expected to be wrappend into function().
 681      * </ul>
 682      *
 683      * Page with multiple different applications can be processed multiple times
 684      * - one per application. To avoid confusion markers need to use
 685      * application ids  (alphanumeric string no spaces).
 686      * <p>
 687      * If input and output files are the same then template is processed in place.
 688      * <p>
 689      * Example:
 690      * <pre>
 691      *     &lt;template file="App_template.html" tofile="App.html"/&gt;
 692      * </pre>
 693      *
 694      * @ant.type name="Template" category="javafx"
 695      */
 696     public static class Template extends DataType {
 697         File infile = null;
 698         File outfile = null;
 699 
 700         /**
 701          * Input file.
 702          *
 703          * @ant.required
 704          */
 705         public void setFile(File f) {
 706             infile = f;
 707         }
 708 
 709         /**
 710          * Output file (after preprocessing).
 711          *
 712          * @ant.not-required Default is the same as input file.
 713          */
 714         public void setTofile(File f) {
 715             outfile = f;
 716         }
 717     }
 718 
 719     /**
 720      * An argument to be passed off to the bundlers.
 721      *
 722      * Each bundler uses a set of arguments that may be shared across
 723      * the different bundlers or it may be specific to each bundler.
 724      *
 725      * Some bundlers declare argument types that are not known to the JDK
 726      * and may be specific to the particular bundler (such as Mac App Store
 727      * categories).  These arguments allow you to set and adjust these a
 728      * rguments.
 729      *
 730      * @ant.type name="BundleArgument" category="javafx"
 731      */
 732     public static class BundleArgument extends DataType {
 733         String arg = null;
 734         String value = null;
 735 
 736         BundleArgument() {
 737 
 738         }
 739 
 740         BundleArgument(String arg, String value) {
 741             this.arg = arg;
 742             this.value = value;
 743         }
 744 
 745         /**
 746          * Name of the bundle argument.
 747          *
 748          * @ant.required
 749          */
 750         public void setArg(String arg) {
 751             this.arg = arg;
 752         }
 753 
 754         /**
 755          * Value for the bundle argument.
 756          *
 757          * @ant.not-required Default is a literal null
 758          */
 759         public void setValue(String value) {
 760             this.value = value;
 761         }
 762     }
 763 }