8198472: Add support for creating bundles from JNLP files JDK-8200758-branch
authorherrick
Fri, 12 Oct 2018 19:00:51 -0400
branchJDK-8200758-branch
changeset 56963 eaca4369b068
parent 56962 a769ad2d40d6
child 56982 e094d5483bd6
8198472: Add support for creating bundles from JNLP files Submitten-by: almatvee Reviewed-by: herrick, kcr, prr, asemenyuk
make/CompileDemos.gmk
src/demo/share/jpackager/JNLPConverter/README.txt
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/HTTPHelper.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/HTTPHelperException.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/JNLPConverter.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Log.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Main.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Options.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Platform.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourceType.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/VersionID.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/VersionString.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java
src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java
src/demo/share/nbproject/jpackager/JNLPConverter/build.xml
src/demo/share/nbproject/jpackager/JNLPConverter/manifest.mf
src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/build-impl.xml
src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/genfiles.properties
src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/project.properties
src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/project.xml
--- a/make/CompileDemos.gmk	Fri Oct 12 18:58:40 2018 -0400
+++ b/make/CompileDemos.gmk	Fri Oct 12 19:00:51 2018 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -219,6 +219,11 @@
     MAIN_CLASS := transparentruler.Ruler, \
 ))
 
+$(eval $(call SetupBuildDemo, JNLPConverter, \
+    DEMO_SUBDIR := jpackager, \
+    MAIN_CLASS := jnlp.converter.Main, \
+))
+
 ################################################################################
 # Copy html and README files.
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/README.txt	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,23 @@
+About JNLPConverter
+===================
+
+JNLPConverter is a standalone tool which uses jpackager to create bundles from
+Java Web Start(TM) Applications and helps to migrate from JNLP to jpackager.
+JNLPConverter will locate and use the jpackager tool from the same JDK as used
+to run JNLPConverter. JNLPConverter supports HTTP/HTTPS and FILE protocol.
+
+Running JNLPConverter
+=====================
+
+To run the JNLPConverter:
+
+  java -jar JNLPConverter.jar <mode> <options>
+
+To get help on JNLPConverter options:
+
+  java -jar JNLPConverter.jar --help
+
+These instructions assume that this installation's version of the java command
+is in your path. If it isn't, then you should either specify the complete path
+to the java command or update your PATH environment variable as described
+in the installation instructions for the Java(TM) SE Development Kit.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/HTTPHelper.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jnlp.converter.parser.GeneralUtil;
+
+public class HTTPHelper {
+
+    public static final int BUFFER_SIZE = 4096;
+
+    public static String downloadFile(String url, String destFolder, String destFileName) throws MalformedURLException, IOException {
+        HttpURLConnection connection = null;
+        String destFile = null;
+
+        try {
+            if (url.contains(" ")) {
+                url = url.replace(" ", "%20");
+            }
+            if (url.contains("\\")) {
+                url = url.replace("\\", "/");
+            }
+
+            URL resource = new URL(url);
+            connection = (HttpURLConnection) resource.openConnection();
+
+            int responseCode = connection.getResponseCode();
+            if (responseCode == HttpURLConnection.HTTP_OK) {
+                destFile = destFolder + File.separator + destFileName;
+                Log.verbose("Downloading " + url + " to " + destFile);
+
+                try (InputStream inputStream = connection.getInputStream();
+                     OutputStream outputStream = new FileOutputStream(destFile)) {
+                    byte[] buffer = new byte[BUFFER_SIZE];
+
+                    int length;
+                    do {
+                        length = inputStream.read(buffer);
+                        if (length > 0) {
+                            outputStream.write(buffer, 0, length);
+                        }
+                    } while (length > 0);
+                }
+            } else {
+                HTTPHelperException e = new HTTPHelperException("Error: Cannot download " + url + ". Server response code: " + responseCode);
+                e.setResponseCode(responseCode);
+                throw e;
+            }
+        } catch (IOException e) {
+            if (e instanceof HTTPHelperException) {
+                throw e;
+            } else {
+                throw new HTTPHelperException("Error: Cannot download " + url + ". " + e.getClass().getSimpleName() + ": " + e.getMessage());
+            }
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+
+        return destFile;
+    }
+
+    public static String copyFile(String url, String destFolder, String destFileName) throws Exception {
+        if (url.contains(" ")) {
+            url = url.replace(" ", "%20");
+        }
+
+        URI sourceURI = new URI(url);
+
+        String sourceFile = sourceURI.getPath();
+        File file = new File(sourceFile);
+        if (!file.exists()) {
+            throw new FileNotFoundException("Error: " + sourceFile + " does not exist.");
+        }
+
+        String destFile = destFolder + File.separator + destFileName;
+        file = new File(destFile);
+        if (file.exists()) {
+            file.delete();
+        }
+
+        Path sourcePath = Paths.get(sourceURI);
+        Path destPath = Paths.get(destFile);
+        Log.verbose("Copying " + url + " to " + destFile);
+        Files.copy(sourcePath, destPath);
+
+        return destFile;
+    }
+
+    public static boolean isHTTPUrl(String url) {
+        return (url.startsWith("http://") || url.startsWith("https://"));
+    }
+
+    public static byte[] getJNLPBits(String versionedJNLP, String jnlp) throws Exception {
+        String jnlpFilePath = null;
+        byte[] bits = null;
+
+        if (isHTTPUrl(jnlp)) {
+            try {
+                jnlpFilePath = downloadFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+            } catch (HTTPHelperException ex) {
+                if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND &&
+                       !versionedJNLP.equals(jnlp)) {
+                    Log.warning("Downloading versioned JNLP from " + versionedJNLP + " failed.");
+                    Log.warning(ex.getMessage());
+                    Log.warning("Downloading " + jnlp + " instead.");
+                    jnlpFilePath = downloadFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+                } else {
+                    throw ex;
+                }
+            }
+            JNLPConverter.markFileToDelete(jnlpFilePath);
+        } else {
+            try {
+                jnlpFilePath = copyFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+            } catch (FileNotFoundException ex) {
+                System.out.println("Error copying versioned JNLP from " + versionedJNLP);
+                System.out.println(ex.getMessage());
+                System.out.println("Copying " + jnlp + " instead.");
+                jnlpFilePath = HTTPHelper.copyFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+            }
+            JNLPConverter.markFileToDelete(jnlpFilePath);
+        }
+
+        File jnlpFile = new File(jnlpFilePath);
+        if (jnlpFile.exists()) {
+            bits = GeneralUtil.readBytes(new FileInputStream(jnlpFile), jnlpFile.length());
+        }
+
+        return bits;
+    }
+
+    public static String getFileNameFromURL(String url) throws IOException {
+        int index;
+        int index1 = url.lastIndexOf('/');
+        int index2 = url.lastIndexOf('\\');
+
+        if (index1 >= index2) {
+            index = index1;
+        } else {
+            index = index2;
+        }
+
+        if (index != -1) {
+            String name = url.substring(index + 1, url.length());
+            name = name.replace("%20", " ");
+            if (name.endsWith(".jnlp") || name.endsWith(".jar")) { // JNLP or JAR
+                return name;
+            } else if (name.endsWith(".ico")) { // Icons
+                return name;
+            } else {
+                throw new IOException("Error: Unsupported file extension for " + url);
+            }
+        } else {
+            throw new IOException("Error: URL (" + url + ") should end with file name.");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/HTTPHelperException.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.IOException;
+
+public class HTTPHelperException extends IOException {
+    private int responseCode = -1;
+
+    public HTTPHelperException(String msg) {
+        super(msg);
+    }
+
+    public void setResponseCode(int code) {
+        responseCode = code;
+    }
+
+    public int getResponseCode() {
+        return responseCode;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/JNLPConverter.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,865 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import jnlp.converter.parser.JNLPDesc;
+import jnlp.converter.parser.JNLPDesc.AssociationDesc;
+import jnlp.converter.parser.JNLPDesc.IconDesc;
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.XMLFormat;
+
+public class JNLPConverter {
+
+    private final Options options;
+    private JNLPDesc jnlpd = null;
+    private final List<String> launchArgs = new ArrayList<>();
+
+    private String downloadFolder = null;
+    private String jnlpDownloadFolder = null;
+    private static String jnlpDownloadFolderStatic;
+    private String jarDownloadFolder = null;
+    private String iconDownloadFolder = null;
+    private String propDownloadFolder = null;
+
+    private static String jpackagerPath = null;
+
+    private static boolean markFileToDelete = false;
+
+    private static final String FA_EXTENSIONS = "extension";
+    private static final String FA_CONTENT_TYPE = "mime-type";
+    private static final String FA_DESCRIPTION = "description";
+    private static final String FA_ICON = "icon";
+
+    public JNLPConverter(Options options) {
+        this.options = options;
+        jnlpDownloadFolderStatic = getJnlpDownloadFolder();
+        markFileToDelete = (options.keep() == null);
+    }
+
+    public String [] getLaunchArgs() {
+        return launchArgs.toArray(new String[0]);
+    }
+
+    public void convert() {
+        try {
+            loadJNLPDesc();
+            downloadResources();
+            validate();
+            buildLaunchArgs();
+            saveLaunchArgs();
+            runJPackager();
+        } catch (Exception ex) {
+            Log.error(ex.getLocalizedMessage());
+        }
+    }
+
+    private JNLPDesc getJNLPD(String jnlp) throws Exception {
+        URL codebase = getCodeBase(jnlp);
+        byte[] bits = HTTPHelper.getJNLPBits(jnlp, jnlp);
+        return XMLFormat.parse(bits, codebase, jnlp);
+    }
+
+    private void loadJNLPDesc() throws Exception {
+        String jnlp = options.getJNLP();
+        jnlpd = getJNLPD(jnlp);
+
+        // Check for required options in case of FX
+        if (jnlpd.isFXApp()) {
+            if (!options.isRuntimeImageSet()) {
+                throw new Exception("This is a JavaFX Web-Start application which requires a runtime image capable of running JavaFX applications, which can be specified by the jpackager option --runtime-image (using --jpackager-options).");
+            }
+        }
+
+        // Check href. It can be same as URL we provided or new one
+        // if JNLP has different href or codebase. We assume that
+        // XMLFormat.parse() will handle any errors in href and codebase
+        // correctly.
+        String href = jnlpd.getHref();
+        if (href != null && !href.equalsIgnoreCase(jnlp)) {
+            if (href.startsWith("file:")) {
+                URI hrefURI = new URI(href);
+                URI jnlpURI = new URI(jnlp);
+
+                String hrefPath = hrefURI.getPath();
+                String jnlpPath = jnlpURI.getPath();
+
+                if (!hrefPath.equalsIgnoreCase(jnlpPath)) {
+                    jnlp = href;
+                    jnlpd = getJNLPD(jnlp);
+                }
+            } else {
+                jnlp = href;
+                jnlpd = getJNLPD(jnlp);
+            }
+        }
+
+        if (jnlpd.getName() == null) {
+            jnlpd.setName(getNameFromURL(jnlp));
+        }
+    }
+
+    private static String getNameFromURL(String url) throws IOException {
+        int index;
+        int index1 = url.lastIndexOf('/');
+        int index2 = url.lastIndexOf('\\');
+
+        if (index1 >= index2) {
+            index = index1;
+        } else {
+            index = index2;
+        }
+
+        if (index != -1) {
+            String name = url.substring(index + 1, url.length());
+            if (name.endsWith(".jnlp")) {
+                return name.substring(0, name.length() - 5);
+            }
+        }
+
+        return null;
+    }
+
+    private URL getCodeBase(String jnlp) throws Exception {
+        int index = jnlp.lastIndexOf('/');
+        if (index != -1) {
+            if (HTTPHelper.isHTTPUrl(jnlp)) {
+                return new URL(jnlp.substring(0, index + 1));
+            } else {
+                String codeBasePath = jnlp.substring(0, index);
+                if (!codeBasePath.endsWith("/")) {
+                    codeBasePath += "/";
+                }
+                return new URI(codeBasePath).toURL();
+            }
+        }
+
+        return null;
+    }
+
+    public static void markFileToDelete(String file) {
+        if (file == null || file.isEmpty()) {
+            return;
+        }
+
+        if (markFileToDelete) {
+            try {
+                File f = new File(file);
+                f.deleteOnExit();
+            } catch (Exception e) {
+                // Print exception, but do not fail conversion.
+                Log.warning(e.getLocalizedMessage());
+            }
+        }
+    }
+
+    public static void deleteFile(String file) {
+        try {
+            File f = new File(file);
+            f.delete();
+        } catch (Exception e) {
+            Log.warning(e.getLocalizedMessage());
+        }
+    }
+
+    private void downloadResources() throws Exception {
+        List<JARDesc> jars = jnlpd.getResources();
+        for (JARDesc jar : jars) {
+            if (jar.getVersion() != null) {
+                if (!jnlpd.isVersionEnabled()) {
+                    throw new Exception("Error: Version based download protocol is not supported without -Djnlp.versionEnabled=true.");
+                }
+            }
+
+            String destFile = null;
+            if (HTTPHelper.isHTTPUrl(jar.getLocation().toString())) {
+                if (jar.getVersion() != null) {
+                    try {
+                        destFile = HTTPHelper.downloadFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                    } catch (HTTPHelperException ex) {
+                        if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+                            System.out.println("Error downloading versioned JAR from " + jar.getVersionLocation());
+                            System.out.println(ex.getMessage());
+                            System.out.println("Downloading " + jar.getLocation() + " instead.");
+                            destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                        } else {
+                            throw ex;
+                        }
+                    }
+                } else {
+                    destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                }
+                markFileToDelete(destFile);
+            } else {
+                if (jar.getVersion() != null) {
+                    try {
+                        destFile = HTTPHelper.copyFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                    } catch (FileNotFoundException ex) {
+                        System.out.println("Error copying versioned JAR from " + jar.getVersionLocation());
+                        System.out.println(ex.getMessage());
+                        System.out.println("Copying " + jar.getLocation() + " instead.");
+                        destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                    }
+                } else {
+                    destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                }
+                markFileToDelete(destFile);
+            }
+
+            if (jar.isNativeLib()) {
+                unpackNativeLib(destFile);
+                deleteFile(destFile);
+            } else {
+                jnlpd.addFile(jar.getName());
+            }
+        }
+
+        IconDesc icon = jnlpd.getIcon();
+        if (icon != null) {
+            String destFile;
+
+            if (HTTPHelper.isHTTPUrl(icon.getLocation())) {
+                destFile = HTTPHelper.downloadFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
+            } else {
+                destFile = HTTPHelper.copyFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
+            }
+
+            markFileToDelete(destFile);
+            icon.setLocalLocation(destFile);
+        }
+
+        AssociationDesc [] associations = jnlpd.getAssociations();
+        if (associations != null) {
+            for (AssociationDesc association : associations) {
+                if (association.getIconUrl() != null) {
+                    String destFile;
+                    if (HTTPHelper.isHTTPUrl(association.getIconUrl())) {
+                        destFile = HTTPHelper.downloadFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
+                    } else {
+                        destFile = HTTPHelper.copyFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
+                    }
+
+                    markFileToDelete(destFile);
+                    association.setIconLocalLocation(destFile);
+                }
+            }
+        }
+    }
+
+    public void unpackNativeLib(String file) throws IOException {
+        try (JarFile jarFile = new JarFile(file)) {
+            Enumeration entries = jarFile.entries();
+
+            while (entries.hasMoreElements()) {
+                JarEntry entry = (JarEntry) entries.nextElement();
+
+                // Skip directories
+                if (entry.isDirectory()) {
+                    continue;
+                }
+
+                String entryName = entry.getName();
+                // Skip anything in sub-directories
+                if (entryName.contains("\\") || entryName.contains("/")) {
+                    continue;
+                }
+
+                // Skip anything not ending with .dll, .dylib or .so
+                if (!entryName.endsWith(".dll") && !entryName.endsWith(".dylib") && !entryName.endsWith(".so")) {
+                    continue;
+                }
+
+                File destFile = new File(getJarDownloadFolder(), entryName);
+                if (destFile.exists()) {
+                    Log.warning(destFile.getAbsolutePath() + " already exist and will not be overwriten by native library from " + file + ".");
+                    continue;
+                }
+
+                InputStream inputStream = jarFile.getInputStream(entry);
+                FileOutputStream outputStream = new FileOutputStream(destFile);
+
+                byte[] buffer = new byte[HTTPHelper.BUFFER_SIZE];
+                int length;
+                do {
+                    length = inputStream.read(buffer);
+                    if (length > 0) {
+                        outputStream.write(buffer, 0, length);
+                    }
+                } while (length > 0);
+
+                jnlpd.addFile(entryName);
+            }
+        }
+    }
+
+    private void validate() {
+        if (jnlpd.getMainJar() == null) {
+            Log.error("Cannot find main jar");
+        }
+
+        if (jnlpd.getMainClass() == null) {
+            Log.error("Cannot find main class");
+        }
+    }
+
+    private void addLaunchArg(String arg, List<String> launchArgs) {
+        if (arg != null && !arg.isEmpty()) {
+            if (!options.isOptionPresent(arg)){
+                launchArgs.add(arg);
+            } else {
+                Log.info(arg + " generated by JNLPConverter is dropped, since it is overwriten via --jpackager-options");
+            }
+        }
+    }
+
+    private void addLaunchArg(String arg, String value, List<String> launchArgs) {
+        if (arg != null && !arg.isEmpty() && value != null && !value.isEmpty()) {
+            if (!options.isOptionPresent(arg)){
+                launchArgs.add(arg);
+                launchArgs.add(value);
+            } else {
+                Log.info(arg + "=" + value +" generated by JNLPConverter is dropped, since it is overwriten via --jpackager-options");
+            }
+        }
+    }
+
+    private void displayLaunchArgs() {
+        if (Log.isVerbose()) {
+            System.out.println();
+            System.out.println("jpackager launch arguments (each argument starts on new line):");
+            launchArgs.forEach((arg) -> {
+                System.out.println(arg);
+            });
+        }
+    }
+
+    private static int fileAssociationsCount = 0;
+    private String getFileAssociationsFile() {
+        String file = getPropDownloadFolder();
+        file += File.separator;
+        file += "fileAssociation";
+        file += String.valueOf(fileAssociationsCount);
+        file += ".properties";
+
+        fileAssociationsCount++;
+
+        return file;
+    }
+
+    private void buildLaunchArgs() {
+        if (options.createImage()) {
+            addLaunchArg("create-image", launchArgs);
+        } else if (options.createInstaller()) {
+            if (options.getInstallerType() == null) {
+                addLaunchArg("create-installer", launchArgs);
+            } else {
+                addLaunchArg("create-installer", options.getInstallerType(), launchArgs);
+            }
+        }
+
+        // Set verbose for jpackager if it is set for us.
+        if (options.verbose()) {
+            addLaunchArg("--verbose", launchArgs);
+        }
+
+        addLaunchArg("--input", getJarDownloadFolder(), launchArgs);
+        addLaunchArg("--output", options.getOutput(), launchArgs);
+        addLaunchArg("--name", jnlpd.getName(), launchArgs);
+        addLaunchArg("--version", jnlpd.getVersion(), launchArgs);
+        addLaunchArg("--vendor", jnlpd.getVendor(), launchArgs);
+        addLaunchArg("--description", jnlpd.getDescription(), launchArgs);
+        addLaunchArg("--icon", jnlpd.getIconLocation(), launchArgs);
+        addLaunchArg("--main-jar", jnlpd.getMainJar(), launchArgs);
+        addLaunchArg("--class", jnlpd.getMainClass(), launchArgs);
+
+        addFiles(launchArgs);
+        addArguments(launchArgs);
+        addJVMArgs(launchArgs);
+
+        if (jnlpd.isDesktopHint()) {
+            if (Platform.isWindows()) {
+                addLaunchArg("--win-shortcut", launchArgs);
+            } else {
+                Log.warning("Ignoring shortcut hint, since it is not supported on current platform.");
+            }
+        }
+
+        if (jnlpd.isMenuHint()) {
+            if (Platform.isWindows()) {
+                addLaunchArg("--win-menu", launchArgs);
+                addLaunchArg("--win-menu-group", jnlpd.getSubMenu(), launchArgs);
+            } else {
+                Log.warning("Ignoring menu hint, since it is not supported on current platform.");
+            }
+        }
+
+        AssociationDesc [] associations = jnlpd.getAssociations();
+        if (associations != null) {
+            for (AssociationDesc association : associations) {
+                String file = getFileAssociationsFile();
+                markFileToDelete(file);
+
+                try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+                    if (association.getExtensions() != null && association.getMimeType() != null) {
+                        out.println(FA_EXTENSIONS + "=" + quote(association.getExtensions()));
+                        out.println(FA_CONTENT_TYPE + "=" + quote(association.getMimeType()));
+
+                        if (association.getMimeDescription() != null) {
+                            out.println(FA_DESCRIPTION + "=" + association.getMimeDescription());
+                        }
+
+                        if (association.getIconLocalLocation() != null) {
+                            out.println(FA_ICON + "=" + quote(association.getIconLocalLocation()));
+                        }
+
+                        addLaunchArg("--file-associations", file, launchArgs);
+                    }
+                } catch (Exception ex) {
+                    Log.warning(ex.toString());
+                    if (association.getExtensions() != null) {
+                        Log.warning("File assoication for " + association.getExtensions() + " will be ignored due to exception above.");
+                    }
+                }
+            }
+        }
+
+        // Add options from --jpackager-options
+        List<String> jpackagerOptions = options.getJPackagerOptions();
+        jpackagerOptions.forEach((option) -> {
+            launchArgs.add(option);
+        });
+
+        displayLaunchArgs();
+    }
+
+    private String getCommandFileName() {
+        Platform platform = Platform.getPlatform();
+        switch (platform) {
+            case WINDOWS:
+                return "run_jpackager.bat";
+            case LINUX:
+                return "run_jpackager.sh";
+            case MAC:
+                return "run_jpackager.sh";
+            default:
+                Log.error("Cannot determine platform type.");
+                return "";
+        }
+    }
+
+    private void saveLaunchArgs() {
+        if (options.keep() != null) {
+            File keepFolder = new File(options.keep());
+            String cmdFile = keepFolder.getAbsolutePath() + File.separator + getCommandFileName();
+            try (PrintWriter out = new PrintWriter(cmdFile)) {
+                out.print(getJPackagerPath());
+                launchArgs.forEach((arg) -> {
+                    out.print(" ");
+
+                    if (arg.contains(" ")) {
+                        int len = arg.length();
+                        if (len >= 1) {
+                            if (arg.charAt(0) != '"' && arg.charAt(len - 1) != '"') {
+                                out.print("\"" + arg + "\"");
+                            } else {
+                                if (Platform.isWindows()) {
+                                    out.print(arg);
+                                } else {
+                                    arg = escapeQuote(arg);
+                                    out.print("\"" + arg + "\"");
+                                }
+                            }
+                        }
+                    } else {
+                        out.print(arg);
+                    }
+                });
+            } catch (FileNotFoundException ex) {
+                Log.error("Cannot save file with command line: " + ex.getLocalizedMessage());
+            }
+        }
+    }
+
+    private void runJPackager() {
+        List<String> command = new ArrayList<>();
+        command.add(getJPackagerPath());
+        command.addAll(launchArgs);
+
+        ProcessBuilder builder = new ProcessBuilder();
+        builder.inheritIO();
+        builder.command(command);
+
+        try {
+            Process process = builder.start();
+            int exitCode = process.waitFor();
+            if (exitCode != 0) {
+                Log.warning("jpackager retrun non zero code: " + exitCode);
+            }
+        } catch (IOException | InterruptedException ex) {
+            Log.error(ex.getMessage());
+        }
+    }
+
+    private void addFileList(String arg, List<String> filesToAdd, List<String> launchArgs) {
+        if (filesToAdd.isEmpty()) {
+            return;
+        }
+
+        String filesArg = "";
+        for (int i = 0; i < filesToAdd.size(); i++) {
+            filesArg += quote(filesToAdd.get(i));
+            if ((i + 1) != filesToAdd.size()) {
+                filesArg += File.pathSeparator;
+            }
+        }
+
+        launchArgs.add(arg);
+        launchArgs.add(filesArg);
+    }
+
+    private void addFiles(List<String> launchArgs) {
+        addFileList("--files", jnlpd.getFiles(), launchArgs);
+    }
+
+    private void addArguments(List<String> launchArgs) {
+        List<String> arguments = jnlpd.getArguments();
+        if (arguments.isEmpty()) {
+            return;
+        }
+
+        String argsStr = "";
+        for (int i = 0; i < arguments.size(); i++) {
+            String arg = arguments.get(i);
+            argsStr += quote(arg);
+            if ((i + 1) != arguments.size()) {
+                argsStr += " ";
+            }
+        }
+
+        launchArgs.add("--arguments");
+        if (Platform.isWindows()) {
+            if (argsStr.contains(" ")) {
+                if (argsStr.contains("\"")) {
+                    argsStr = escapeQuote(argsStr);
+                }
+                argsStr = "\"" + argsStr + "\"";
+            }
+        }
+        launchArgs.add(argsStr);
+    }
+
+    private void addJVMArgs(List<String> launchArgs) {
+        List<String> jvmArgs = jnlpd.getVMArgs();
+        if (jvmArgs.isEmpty()) {
+            return;
+        }
+
+        String jvmArgsStr = "";
+        for (int i = 0; i < jvmArgs.size(); i++) {
+            String arg = jvmArgs.get(i);
+            jvmArgsStr += quote(arg);
+            if ((i + 1) != jvmArgs.size()) {
+                jvmArgsStr += " ";
+            }
+        }
+
+        launchArgs.add("--jvm-args");
+        if (Platform.isWindows()) {
+            if (jvmArgsStr.contains(" ")) {
+                if (jvmArgsStr.contains("\"")) {
+                    jvmArgsStr = escapeQuote(jvmArgsStr);
+                }
+                jvmArgsStr = "\"" + jvmArgsStr + "\"";
+            }
+        }
+        launchArgs.add(jvmArgsStr);
+    }
+
+    private String quote(String in) {
+        if (in == null) {
+            return null;
+        }
+
+        if (in.isEmpty()) {
+            return "";
+        }
+
+        if (!in.contains("=")) {
+            // Not a property
+            if (in.contains(" ")) {
+                in = escapeQuote(in);
+                return "\"" + in + "\"";
+            }
+            return in;
+        }
+
+        if (!in.contains(" ")) {
+            return in; // No need to quote
+        }
+
+        int paramIndex = in.indexOf("=");
+        if (paramIndex <= 0) {
+            return in; // Something wrong, just skip quoting
+        }
+
+        String param = in.substring(0, paramIndex);
+        String value = in.substring(paramIndex + 1);
+
+        if (value.length() == 0) {
+            return in; // No need to quote
+        }
+
+        value = escapeQuote(value);
+
+        return param + "=" + "\"" + value + "\"";
+    }
+
+    private String escapeQuote(String in) {
+        if (in == null) {
+            return null;
+        }
+
+        if (in.isEmpty()) {
+            return "";
+        }
+
+        if (in.contains("\"")) {
+            // Use code points to preserve non-ASCII chars
+            StringBuilder sb = new StringBuilder();
+            int codeLen = in.codePointCount(0, in.length());
+            for (int i = 0; i < codeLen; i++) {
+                int code = in.codePointAt(i);
+                // Note: No need to escape '\' on Linux or OS X.
+                // jpackager expects us to pass arguments and properties with quotes and spaces as a map
+                // with quotes being escaped with additional \ for internal quotes.
+                // So if we want two properties below:
+                // -Djnlp.Prop1=Some "Value" 1
+                // -Djnlp.Prop2=Some Value 2
+                // jpackager will need:
+                // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\""
+                // but since we using ProcessBuilder to run jpackager we will need to escape
+                // our escape symbols as well, so we will need to pass string below to ProcessBuilder:
+                // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\""
+                switch (code) {
+                    case '"':
+                        // " -> \" -> \\\"
+                        if (i == 0 || in.codePointAt(i - 1) != '\\') {
+                            if (Platform.isWindows()) {
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                            }
+                            sb.appendCodePoint('\\');
+                            sb.appendCodePoint(code);
+                        }
+                        break;
+                    case '\\':
+                        // We need to escape already escaped symbols as well
+                        if ((i + 1) < codeLen) {
+                            int nextCode = in.codePointAt(i + 1);
+                            if (nextCode == '"') {
+                                // \" -> \\\"
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint(nextCode);
+                            } else {
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint(code);
+                            }
+                        } else {
+                            if (Platform.isWindows()) {
+                                sb.appendCodePoint('\\');
+                            }
+                            sb.appendCodePoint(code);
+                        }
+                        break;
+                    default:
+                        sb.appendCodePoint(code);
+                        break;
+                }
+            }
+            return sb.toString();
+        }
+
+        return in;
+    }
+
+    public synchronized String getDownloadFolder() {
+        if (downloadFolder == null) {
+            try {
+                File file;
+                if (options.keep() == null) {
+                    Path path = Files.createTempDirectory("JNLPConverter");
+                    file = path.toFile();
+                    file.deleteOnExit();
+                } else {
+                    file = new File(options.keep());
+                    if (!file.exists()) {
+                        file.mkdir();
+                    }
+                }
+
+                downloadFolder = file.getAbsolutePath();
+            } catch (IOException e) {
+                Log.error(e.getLocalizedMessage());
+            }
+        }
+
+        return downloadFolder;
+    }
+
+    public final synchronized String getJnlpDownloadFolder() {
+        if (jnlpDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "jnlp");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "jnlp");
+            jnlpDownloadFolder = file.getAbsolutePath();
+        }
+
+        return jnlpDownloadFolder;
+    }
+
+    public static String getJnlpDownloadFolderStatic() {
+        return jnlpDownloadFolderStatic;
+    }
+
+    public synchronized String getJarDownloadFolder() {
+        if (jarDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "jar");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "jar");
+            jarDownloadFolder = file.getAbsolutePath();
+        }
+
+        return jarDownloadFolder;
+    }
+
+    public synchronized String getIconDownloadFolder() {
+        if (iconDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "icon");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "icon");
+            iconDownloadFolder = file.getAbsolutePath();
+        }
+
+        return iconDownloadFolder;
+    }
+
+    public synchronized String getPropDownloadFolder() {
+        if (propDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "prop");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "prop");
+            propDownloadFolder = file.getAbsolutePath();
+        }
+
+        return propDownloadFolder;
+    }
+
+    public synchronized static String getJPackagerPath() {
+        if (jpackagerPath == null) {
+            jpackagerPath = System.getProperty("java.home");
+            jpackagerPath += File.separator;
+            jpackagerPath += "bin";
+            jpackagerPath += File.separator;
+
+            Platform platform = Platform.getPlatform();
+            switch (platform) {
+                case WINDOWS:
+                    jpackagerPath += "jpackager.exe";
+                    break;
+                case LINUX:
+                    jpackagerPath += "jpackager";
+                    break;
+                case MAC:
+                    jpackagerPath += "jpackager";
+                    break;
+                default:
+                    Log.error("Cannot determine platform type.");
+                    break;
+            }
+
+            Log.verbose("jpackager: " + jpackagerPath);
+        }
+
+        return jpackagerPath;
+    }
+
+    public static String getIconFormat(String icon) {
+        // GIF, JPEG, ICO, or PNG
+        if (icon.toLowerCase().endsWith(".gif")) {
+            return "GIF";
+        } else if (icon.toLowerCase().endsWith(".jpg")) {
+            return "JPEG";
+        } else if (icon.toLowerCase().endsWith(".ico")) {
+            return "ICO";
+        } else if (icon.toLowerCase().endsWith(".png")) {
+            return "PNG";
+        }
+
+        return "UNKNOWN";
+    }
+
+    public static boolean isIconSupported(String icon) {
+        Platform platform = Platform.getPlatform();
+        switch (platform) {
+            case WINDOWS:
+                if (icon.endsWith(".ico")) {
+                    return true;
+                } else {
+                    Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Windows for file " + icon + ".");
+                    return false;
+                }
+            case LINUX:
+                if (icon.endsWith(".png")) {
+                    return true;
+                } else {
+                    Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Linux for file " + icon + ".");
+                    return false;
+                }
+            case MAC:
+                Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on OS X for file " + icon + ".");
+                return false;
+        }
+
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Log.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+package jnlp.converter;
+
+public class Log {
+    private static boolean verbose = false;
+
+    public static void setVerbose(boolean verbose) {
+        Log.verbose = verbose;
+    }
+
+    public static boolean isVerbose() {
+        return verbose;
+    }
+
+    public static void verbose(String msg) {
+        if (verbose) {
+            System.out.println(msg);
+        }
+    }
+
+    public static void info(String msg) {
+        System.out.println("Info: " + msg);
+    }
+
+    public static void warning(String msg) {
+        System.err.println("Warning: " + msg);
+    }
+
+    public static void error(String msg) {
+        System.err.println("Error: " + msg);
+        System.exit(1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Main.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.File;
+
+public class Main {
+
+    private static void showHelp() {
+        Options.showHelp();
+    }
+
+    private static void showVersion() {
+        System.out.println("Version: 1.0");
+    }
+
+    private static void createBundle(Options options) {
+        Log.verbose("Creating bundle for JNLP: " + options.getJNLP());
+        Log.verbose("Output folder: " + options.getOutput());
+
+        JNLPConverter converter = new JNLPConverter(options);
+        converter.convert();
+    }
+
+    private static void validateJDK() {
+        String jpackagerPath = JNLPConverter.getJPackagerPath();
+        File file = new File(jpackagerPath);
+        if (!file.exists()) {
+            Log.error("Cannot find " + jpackagerPath + ". Make sure you running JNLPConverter with supported JDK version.");
+        }
+    }
+
+    public static void main(String[] args) {
+        Options options = Options.parseArgs(args); // Only valid options will be returned
+
+        Log.setVerbose(options.verbose());
+
+        validateJDK();
+
+        if (options.help()) {
+            showHelp();
+        } else if (options.version()) {
+            showVersion();
+        } else {
+            createBundle(options);
+        }
+
+        System.exit(0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Options.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Options {
+
+    private boolean createImage = false;
+    private boolean createInstaller = false;
+    private String installerType = null;
+    private String jnlp = null;
+    private String output = null;
+    private String keep = null;
+    private boolean help = false;
+    private boolean verbose = false;
+    private boolean version = false;
+    private final List<String> jpackagerOptions = new ArrayList<>();
+    private boolean isRuntimeImageSet = false;
+
+    private static final String JNLP_OPTION_PREFIX = "--jnlp=";
+    private static final String OUTPUT_OPTION_PREFIX = "--output=";
+    private static final String KEEP_OPTION_PREFIX = "--keep=";
+    private static final String JNLP_OPTION_SHORT_PREFIX = "-j";
+    private static final String OUTPUT_OPTION_SHORT_PREFIX = "-o";
+    private static final String KEEP_OPTION_SHORT_PREFIX = "-k";
+
+    private static final String [] INSTALLER_TYPES = {"msi", "rpm", "deb",
+                                                      "dmg", "pkg", "pkg-app-store"};
+
+    // --output, -o, --input, -i, --files, -f, --main-jar, -j, --class, -c
+    private static final String [] BLOCKED_JPACKAGER_OPTIONS = {"--output", "-o", "--input", "-i",
+                                                                "--files", "-f", "--main-jar",
+                                                                "-j", "--class", "-c"};
+
+    private static final String RUNTIME_IMAGE_OPTION = "--runtime-image";
+
+    private static final String ERR_UNKNOWN_OPTION = "Unknown option: ";
+    private static final String ERR_MISSING_VALUE = "Value is required for option ";
+    private static final String ERR_MISSING_MODE = "Error: create-image or create-installer mode is required";
+    private static final String ERR_MISSING_JNLP = "Error: --jnlp is required";
+    private static final String ERR_MISSING_OUTPUT = "Error: --output is required";
+    private static final String ERR_OUTPUT_EXISTS = "Error: output folder already exists";
+    private static final String ERR_KEEP_EXISTS = "Error: folder for --keep argument already exists";
+    private static final String ERR_INVALID_PROTOCOL_JNLP = "Error: Invalid protocol for JNLP file. Only HTTP, HTTPS and FILE protocols are supported.";
+
+    public boolean createImage() {
+        return createImage;
+    }
+
+    public boolean createInstaller() {
+        return createInstaller;
+    }
+
+    public String getInstallerType() {
+        return installerType;
+    }
+
+    public String getJNLP() {
+        return jnlp;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public String keep() {
+        return keep;
+    }
+
+    public boolean help() {
+        return help;
+    }
+
+    public boolean verbose() {
+        return verbose;
+    }
+
+    public boolean version() {
+        return version;
+    }
+
+    public List<String> getJPackagerOptions() {
+        return jpackagerOptions;
+    }
+
+    public boolean isRuntimeImageSet() {
+        return isRuntimeImageSet;
+    }
+
+    // Helper method to dump all options
+    private void display() {
+        System.out.println("Options:");
+        System.out.println("createImage: " + createImage);
+        System.out.println("createInstaller: " + createInstaller);
+        System.out.println("installerType: " + installerType);
+        System.out.println("jnlp: " + jnlp);
+        System.out.println("output: " + output);
+        System.out.println("keep: " + keep);
+        System.out.println("help: " + help);
+        System.out.println("verbose: " + verbose);
+        System.out.println("version: " + version);
+        for (int i = 0; i < jpackagerOptions.size(); i++) {
+            System.out.println("jpackagerOptions[" + i + "]: " + jpackagerOptions.get(i));
+        }
+    }
+
+    private void validate() {
+        if (help || version) {
+            return;
+        }
+
+        if (!createImage && !createInstaller) {
+            optionError(ERR_MISSING_MODE);
+        }
+
+        if (jnlp == null) {
+            optionError(ERR_MISSING_JNLP);
+        } else {
+            int index = jnlp.indexOf(":");
+            if (index == -1 || index == 0) {
+                optionError(ERR_INVALID_PROTOCOL_JNLP);
+            } else {
+                String protocol = jnlp.substring(0, index);
+                if (!protocol.equalsIgnoreCase("http") &&
+                    !protocol.equalsIgnoreCase("https") &&
+                    !protocol.equalsIgnoreCase("file")) {
+                    optionError(ERR_INVALID_PROTOCOL_JNLP);
+                }
+            }
+        }
+
+        if (output == null) {
+            optionError(ERR_MISSING_OUTPUT);
+        } else {
+            File file = new File(output);
+            if (file.exists()) {
+                optionErrorNoHelp(ERR_OUTPUT_EXISTS);
+            }
+        }
+
+        if (keep != null) {
+            File file = new File(keep);
+            if (file.exists()) {
+                optionErrorNoHelp(ERR_KEEP_EXISTS);
+            }
+        }
+
+        jpackagerOptions.forEach((option) -> {
+            if (isBlockedOption(option)) {
+                Log.error(option + " is not allowed via --jpackager-options, since it will conflict with "
+                        + "same option generated by JNLPConverter.");
+            }
+        });
+    }
+
+    public boolean isOptionPresent(String option) {
+        for (String jpackagerOption : jpackagerOptions) {
+            if (jpackagerOption.equalsIgnoreCase(option)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean isBlockedOption(String option) {
+        for (String blockedOption : BLOCKED_JPACKAGER_OPTIONS) {
+            if (blockedOption.equalsIgnoreCase(option)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static void showHelp() {
+//      System.out.println("********* Help should not be longer then 80 characters as per JEP-293 *********");
+        System.out.println("Usage: java -jar JNLPConverter.jar <mode> <options>");
+        System.out.println("");
+        System.out.println("where mode is one of:");
+        System.out.println("  create-image");
+        System.out.println("          Generates a platform-specific application image.");
+        System.out.println("  create-installer <type>");
+        System.out.println("          Generates a platform-specific installer for the application.");
+        System.out.println("          Valid values for \"type\" are \"msi\", \"rpm\", \"deb\", \"dmg\", \"pkg\",");
+        System.out.println("          \"pkg-app-store\". If \"type\" is omitted, all supported types of installable");
+        System.out.println("          packages for current platform will be generated.");
+        System.out.println("");
+        System.out.println("Possible options include:");
+        System.out.println("  -j, --jnlp <path>");
+        System.out.println("          Full path to JNLP file. Supported protocols are HTTP/HTTPS/FILE.");
+        System.out.println("  -o, --output <path>");
+        System.out.println("          Name of the directory where generated output files are placed.");
+        System.out.println("  -k, --keep <path>");
+        System.out.println("          Keep JNLP, JARs and command line arguments for jpackager");
+        System.out.println("          in directory provided.");
+        System.out.println("      --jpackager-options <options>");
+        System.out.println("          Specify additional jpackager options or overwrite provided by JNLPConverter.");
+        System.out.println("          All jpackager options can be specified except: --output -o, --input -i,");
+        System.out.println("          --files -f, --main-jar -j and --class -c.");
+        System.out.println("  -h, --help, -?");
+        System.out.println("          Print this help message");
+        System.out.println("  -v, --verbose");
+        System.out.println("          Enable verbose output.");
+        System.out.println("      --version");
+        System.out.println("          Version information.");
+        System.out.println("To specify an argument for a long option, you can use --<name>=<value> or");
+        System.out.println("--<name> <value>.");
+        System.out.println("To specify proxy server use standard Java properties http.proxyHost and http.proxyPort.");
+    }
+
+    private static boolean isInstallerType(String type) {
+        for (String installerType : INSTALLER_TYPES) {
+            if (installerType.equals(type)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static Options parseArgs(String[] args) {
+        Options options = new Options();
+
+        int index = 0;
+        if (args.length >= 1) {
+            switch (args[0]) {
+                case "create-image":
+                    options.createImage = true;
+                    index = 1;
+                    break;
+                case "create-installer":
+                    options.createInstaller = true;
+                    index = 1;
+                    if (args.length >= 2) {
+                        if (isInstallerType(args[1])) {
+                            options.installerType = args[1];
+                            index = 2;
+                        }
+                    }
+                    break;
+                case "-h":
+                case "--help":
+                case "-?":
+                case "--version":
+                    break;
+                default:
+                    optionError(Options.ERR_MISSING_MODE);
+                    break;
+            }
+        }
+
+        for (int i = index; i < args.length; i++) {
+            String arg = args[i];
+
+            if (arg.equals("--jnlp")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "--jnlp");
+                }
+                options.jnlp = args[i];
+            }  else if (arg.startsWith(JNLP_OPTION_PREFIX)) {
+                options.jnlp = arg.substring(JNLP_OPTION_PREFIX.length());
+            } else if (arg.equals("--output")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "--output");
+                }
+                options.output = args[i];
+            } else if (arg.startsWith(OUTPUT_OPTION_PREFIX)) {
+                options.output = arg.substring(OUTPUT_OPTION_PREFIX.length());
+            } else if (arg.equals("--keep")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "--keep");
+                }
+                options.keep = args[i];
+            } else if (arg.startsWith(KEEP_OPTION_PREFIX)) {
+                options.keep = arg.substring(KEEP_OPTION_PREFIX.length());
+            } else if (arg.equals("--help")) {
+                options.help = true;
+            } else if (arg.equals("--verbose")) {
+                options.verbose = true;
+            } else if (arg.equals("--version")) {
+                options.version = true;
+            } else if (arg.equals("-j")) { // short options
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "-j");
+                }
+                options.jnlp = args[i];
+            } else if (arg.startsWith(JNLP_OPTION_SHORT_PREFIX)) {
+                options.jnlp = arg.substring(JNLP_OPTION_SHORT_PREFIX.length());
+            } else if (arg.equals("-o")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "-o");
+                }
+                options.output = args[i];
+            } else if (arg.startsWith(OUTPUT_OPTION_SHORT_PREFIX)) {
+                options.output = arg.substring(OUTPUT_OPTION_SHORT_PREFIX.length());
+            } else if (arg.equals("-k")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "-k");
+                }
+                options.keep = args[i];
+            } else if (arg.startsWith(KEEP_OPTION_SHORT_PREFIX)) {
+                options.keep = arg.substring(KEEP_OPTION_SHORT_PREFIX.length());
+            } else if (arg.equals("-h") || arg.equals("-?")) {
+                options.help = true;
+            } else if (arg.equals("-v")) {
+                options.verbose = true;
+            } else if (arg.equals("--jpackager-options")) {
+                for (i = (i + 1); i < args.length; i++) {
+                    if (!options.isRuntimeImageSet) {
+                        if (args[i].equals(RUNTIME_IMAGE_OPTION)) {
+                            options.isRuntimeImageSet = true;
+                        }
+                    }
+                    options.jpackagerOptions.add(args[i]);
+                }
+            } else {
+                optionError(ERR_UNKNOWN_OPTION, arg);
+            }
+        }
+
+        //options.display(); // For testing only
+        options.validate();
+
+        return options;
+    }
+
+    private static void optionErrorNoHelp(String msg) {
+        System.out.println(msg);
+        System.exit(1);
+    }
+
+    private static void optionError(String msg) {
+        System.out.println(msg);
+        System.out.println();
+        showHelp();
+        System.exit(1);
+    }
+
+    private static void optionError(String msg, String option) {
+        System.out.println(msg + option);
+        System.out.println();
+        showHelp();
+        System.exit(1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/Platform.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.util.regex.Pattern;
+
+public enum Platform {
+    UNKNOWN, WINDOWS, LINUX, MAC;
+    private static final Platform platform;
+    private static final int majorVersion;
+    private static final int minorVersion;
+
+    static {
+        String os = System.getProperty("os.name").toLowerCase();
+
+        if (os.contains("win")) {
+            platform = Platform.WINDOWS;
+        } else if (os.contains("nix") || os.contains("nux")) {
+            platform = Platform.LINUX;
+        } else if (os.contains("mac")) {
+            platform = Platform.MAC;
+        } else {
+            platform = Platform.UNKNOWN;
+        }
+
+        String version = System.getProperty("os.version");
+        String[] parts = version.split(Pattern.quote("."));
+
+        if (parts.length > 0) {
+            majorVersion = Integer.parseInt(parts[0]);
+
+            if (parts.length > 1) {
+                minorVersion = Integer.parseInt(parts[0]);
+            } else {
+                minorVersion = -1;
+            }
+        } else {
+            majorVersion = -1;
+            minorVersion = -1;
+        }
+    }
+
+    private Platform() {
+    }
+
+    public static Platform getPlatform() {
+        return platform;
+    }
+
+    public static boolean isWindows() {
+        return (platform == Platform.WINDOWS);
+    }
+
+    public static int getMajorVersion() {
+        return majorVersion;
+    }
+
+    public static int getMinorVersion() {
+        return minorVersion;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Handy class to add some utility methods for dealing with property matching
+ * etc.
+ */
+public class GeneralUtil {
+
+    public static boolean prefixMatchStringList(String[] prefixList, String target) {
+        // No prefixes matches everything
+        if (prefixList == null) {
+            return true;
+        }
+        // No target, but a prefix list does not match anything
+        if (target == null) {
+            return false;
+        }
+        for (String prefix : prefixList) {
+            if (target.startsWith(prefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static String getOSArch() {
+        return System.getProperty("os.arch");
+    }
+
+    public static boolean prefixMatchArch(String[] prefixList) {
+        // No prefixes matches everything
+        if (prefixList == null) {
+            return true;
+        }
+
+        // check for the current arch
+        String arch = getOSArch();
+        for (String prefix : prefixList) {
+            if (arch.startsWith(prefix)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Converts a space delimited string to a list of strings
+     */
+    public static String[] getStringList(String str) {
+        if (str == null) {
+            return null;
+        }
+        ArrayList<String> list = new ArrayList<>();
+        int i = 0;
+        int length = str.length();
+        StringBuffer sb = null;
+        while (i < length) {
+            char ch = str.charAt(i);
+            switch (ch) {
+                case ' ':
+                    // A space was hit. Add string to list
+                    if (sb != null) {
+                        list.add(sb.toString());
+                        sb = null;
+                    }
+                    break;
+                case '\\':
+                    // It is a delimiter. Add next character
+                    if (i + 1 < length) {
+                        ch = str.charAt(++i);
+                        if (sb == null) {
+                            sb = new StringBuffer();
+                        }
+                        sb.append(ch);
+                    }
+                    break;
+                default:
+                    if (sb == null) {
+                        sb = new StringBuffer();
+                    }   sb.append(ch);
+                    break;
+            }
+            i++; // Next character
+        }
+        // Make sure to add the last part to the list too
+        if (sb != null) {
+            list.add(sb.toString());
+        }
+        if (list.isEmpty()) {
+            return null;
+        }
+        String[] results = new String[list.size()];
+        return list.toArray(results);
+    }
+
+    /**
+     * Checks if string list matches default locale
+     */
+    public static boolean matchLocale(String[] localeList, Locale locale) {
+        // No locale specified matches everything
+        if (localeList == null) {
+            return true;
+        }
+        for (String localeList1 : localeList) {
+            if (matchLocale(localeList1, locale)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if string matches default locale
+     */
+    public static boolean matchLocale(String localeStr, Locale locale) {
+        if (localeStr == null || localeStr.length() == 0) {
+            return true;
+        }
+
+        // Compare against default locale
+        String language;
+        String country;
+        String variant;
+
+        // The locale string is of the form language_country_variant
+        StringTokenizer st = new StringTokenizer(localeStr, "_", false);
+        if (st.hasMoreElements() && locale.getLanguage().length() > 0) {
+            language = st.nextToken();
+            if (!language.equalsIgnoreCase(locale.getLanguage())) {
+                return false;
+            }
+        }
+        if (st.hasMoreElements() && locale.getCountry().length() > 0) {
+            country = st.nextToken();
+            if (!country.equalsIgnoreCase(locale.getCountry())) {
+                return false;
+            }
+        }
+        if (st.hasMoreElements() && locale.getVariant().length() > 0) {
+            variant = st.nextToken();
+            if (!variant.equalsIgnoreCase(locale.getVariant())) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static long heapValToLong(String heapValue) {
+        if (heapValue == null) {
+            return -1;
+        }
+        long multiplier = 1;
+        if (heapValue.toLowerCase().lastIndexOf('m') != -1) {
+            // units are megabytes, 1 megabyte = 1024 * 1024 bytes
+            multiplier = 1024 * 1024;
+            heapValue = heapValue.substring(0, heapValue.length() - 1);
+        } else if (heapValue.toLowerCase().lastIndexOf('k') != -1) {
+            // units are kilobytes, 1 kilobyte = 1024 bytes
+            multiplier = 1024;
+            heapValue = heapValue.substring(0, heapValue.length() - 1);
+        }
+        long theValue;
+        try {
+            theValue = Long.parseLong(heapValue);
+            theValue = theValue * multiplier;
+        } catch (NumberFormatException e) {
+            theValue = -1;
+        }
+        return theValue;
+    }
+
+    public static byte[] readBytes(InputStream is, long size) throws IOException {
+        // Sanity on file size (restrict to 1M)
+        if (size > 1024 * 1024) {
+            throw new IOException("File too large");
+        }
+
+        BufferedInputStream bis;
+        if (is instanceof BufferedInputStream) {
+            bis = (BufferedInputStream) is;
+        } else {
+            bis = new BufferedInputStream(is);
+        }
+
+        if (size <= 0) {
+            size = 10 * 1024; // Default to 10K
+        }
+        byte[] b = new byte[(int) size];
+        int n;
+        int bytesRead = 0;
+        n = bis.read(b, bytesRead, b.length - bytesRead);
+        while (n != -1) {
+            bytesRead += n;
+            // Still room in array
+            if (b.length == bytesRead) {
+                byte[] bb = new byte[b.length * 2];
+                System.arraycopy(b, 0, bb, 0, b.length);
+                b = bb;
+            }
+            // Read next line
+            n = bis.read(b, bytesRead, b.length - bytesRead);
+        }
+        bis.close();
+        is.close();
+
+        if (bytesRead != b.length) {
+            byte[] bb = new byte[bytesRead];
+            System.arraycopy(b, 0, bb, 0, bytesRead);
+            b = bb;
+        }
+        return b;
+    }
+
+    public static String getOSFullName() {
+        return System.getProperty("os.name");
+    }
+
+    /**
+     * Makes sure a URL is a path URL, i.e., ends with '/'
+     */
+    public static URL asPathURL(URL url) {
+        if (url == null) {
+            return null;
+        }
+
+        String path = url.getFile();
+        if (path != null && !path.endsWith("/")) {
+            try {
+                return new URL(url.getProtocol(),
+                        url.getHost(),
+                        url.getPort(),
+                        url.getFile() + "/");
+            } catch (MalformedURLException mue) {
+                // Should not happen
+            }
+        }
+        // Just return same URl
+        return url;
+    }
+
+    public static Locale getDefaultLocale() {
+        return Locale.getDefault();
+    }
+
+    public static String toNormalizedString(URL u) {
+        if (u == null) {
+            return "";
+        }
+
+        try {
+            if (u.getPort() == u.getDefaultPort()) {
+                u = new URL(u.getProtocol().toLowerCase(),
+                        u.getHost().toLowerCase(), -1, u.getFile());
+            } else {
+                u = new URL(u.getProtocol().toLowerCase(),
+                        u.getHost().toLowerCase(), u.getPort(), u.getFile());
+            }
+        } catch (MalformedURLException ex) {
+        }
+        return u.toExternalForm();
+    }
+
+    public static boolean sameURLs(URL u1, URL u2) {
+        if (u1 == null || u2 == null || (u1 == u2)) {
+            return (u1 == u2);
+        }
+        //NB: do not use URL.sameFile() as it will do DNS lookup
+        // Also, do quick check before slow string comparisons
+        String f1 = u1.getFile();
+        String f2 = u2.getFile();
+        return (f1.length() == f2.length()) && sameBase(u1, u2)
+                && f1.equalsIgnoreCase(f2);
+    }
+
+    public static boolean sameBase(URL u1, URL u2) {
+        return u1 != null && u2 != null &&
+                sameHost(u1, u2) && samePort(u1, u2) && sameProtocol(u1, u2);
+    }
+
+    private static boolean sameProtocol(URL u1, URL u2) {
+        //protocols are known to be lowercase
+        return u1.getProtocol().equals(u2.getProtocol());
+    }
+
+    private static boolean sameHost(URL u1, URL u2) {
+        String host = u1.getHost();
+        String otherHost = u2.getHost();
+        if (host == null || otherHost == null) {
+            return (host == null && otherHost == null);
+        } else {
+            //avoid slow comparison for strings of different length
+            return ((host.length() == otherHost.length())
+                       && host.equalsIgnoreCase(otherHost));
+        }
+    }
+
+    private static boolean samePort(URL u1, URL u2) {
+        return getPort(u1) == getPort(u2);
+    }
+
+    public static int getPort(URL u) {
+        if (u.getPort() != -1) {
+            return u.getPort();
+        } else {
+            return u.getDefaultPort();
+        }
+    }
+
+    public static URL getBase(URL url) {
+        if (url == null) return null;
+        String file = url.getFile();
+        if (file != null) {
+            int idx = file.lastIndexOf('/');
+            if (idx != -1 ) {
+                file = file.substring(0, idx + 1);
+            }
+            try {
+                return new URL(
+                    url.getProtocol(),
+                    url.getHost(),
+                    url.getPort(),
+                    file);
+            } catch(MalformedURLException mue) {
+                System.err.println(mue.getMessage());
+            }
+        }
+        // Just return same URL
+        return url;
+    }
+
+    private static String getEmbeddedVersionPath(String path, String version) {
+        int index = path.lastIndexOf("/");
+        String filename = path.substring(index + 1);
+        path = path.substring(0, index + 1);
+
+        String ext = null;
+        index = filename.lastIndexOf(".");
+        if (index != -1) {
+            ext = filename.substring(index + 1);
+            filename = filename.substring(0, index);
+        }
+
+        StringBuilder filenameSB = new StringBuilder(filename);
+        if (version != null) {
+            filenameSB.append("__V");
+            filenameSB.append(version);
+        }
+        if (ext != null) {
+            filenameSB.append(".");
+            filenameSB.append(ext);
+        }
+
+        path += filenameSB.toString();
+        return path;
+    }
+
+    public static URL getEmbeddedVersionURL(URL u, String version) throws Exception {
+        if (u == null) {
+            return null;
+        }
+
+        if (version == null || version.indexOf("*") != -1
+                || version.indexOf("+") != -1) {
+            // Do not support * or + in version string
+            return u;
+        }
+
+        URL versionURL = null;
+
+        String protocol = u.getProtocol();
+        String host = u.getHost();
+        int port = u.getPort();
+        String path = u.getPath();
+
+        path = getEmbeddedVersionPath(path, version);
+
+        versionURL = new URL(protocol, host, port, path);
+
+        return versionURL;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,617 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import jnlp.converter.Log;
+
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+
+public class JNLPDesc {
+    private String specVersion = null;
+    private String codebase = null;
+    private String version = null;
+    private String href = null;
+    private String name = null;
+    private String title = null;
+    private String vendor = null;
+    private String mainJar = null;
+    private String [] descriptions = null;
+    private IconDesc [] icons = null;
+    private ShortcutDesc shortcuts = null;
+    private AssociationDesc [] associations = null;
+    private String mainClass = null;
+    private final List<String> arguments  = new ArrayList<>();
+    private final List<String> files = new ArrayList<>();
+    private final List<JARDesc> resources = new ArrayList<>();
+    private final List<String> vmArgs = new ArrayList<>();
+    private boolean isLibrary = false;
+    private boolean isInstaller = false;
+    private boolean isJRESet = false;
+    private ResourcesDesc resourcesDesc;
+    private boolean isVersionEnabled = false;
+    private boolean isSandbox = true;
+    private boolean isFXApp = false;
+
+    public void setSpecVersion(String specVersion) {
+        this.specVersion = specVersion;
+
+        // Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0, 8.20, 9 or a wildcard such as 1.0+.
+        if (!specVersion.startsWith("1.0") &&
+            !specVersion.startsWith("1.5") &&
+            !specVersion.startsWith("6.0") &&
+            !specVersion.startsWith("6.0.10") &&
+            !specVersion.startsWith("6.0.18") &&
+            !specVersion.startsWith("7.0") &&
+            !specVersion.startsWith("8.20") &&
+            !specVersion.startsWith("9")) {
+                System.out.println("Warning: Invalid version of the JNLP specification found: "
+                        + specVersion + ". Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0,"
+                        + " 8.20, 9 or a wildcard such as 1.0+.");
+        }
+    }
+
+    public String getSpecVersion() {
+        return specVersion;
+    }
+
+    public void setCodebase(String codebase) {
+        this.codebase = codebase;
+    }
+
+    public String getCodebase() {
+        return codebase;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setHref(String href) {
+        this.href = href;
+    }
+
+    public String getHref() {
+        return href;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setVendor(String vendor) {
+        this.vendor = vendor;
+    }
+
+    public String getVendor() {
+        return vendor;
+    }
+
+    public void setMainJar(String mainJar) {
+        if (this.mainJar == null) {
+            this.mainJar = mainJar;
+        } else {
+            Log.warning("Main jar already set to '" + this.mainJar + "'. "
+                    + "Attempt to set main jar to '" + mainJar + "' will be ignored.");
+        }
+    }
+
+    public String getMainJar() {
+        return mainJar;
+    }
+
+    public void setDescriptions(String [] descriptions) {
+        this.descriptions = descriptions;
+    }
+
+    public String getDescription() {
+        String description = null;
+        if (descriptions != null) {
+            if (descriptions[InformationDesc.DESC_DEFAULT] != null) {
+                description = descriptions[InformationDesc.DESC_DEFAULT];
+            } else if (descriptions[InformationDesc.DESC_SHORT] != null) {
+                description = descriptions[InformationDesc.DESC_SHORT];
+            } else if (descriptions[InformationDesc.DESC_ONELINE] != null) {
+                description = descriptions[InformationDesc.DESC_ONELINE];
+            } else if (descriptions[InformationDesc.DESC_TOOLTIP] != null) {
+                description = descriptions[InformationDesc.DESC_TOOLTIP];
+            }
+
+            if (description != null) {
+                if (description.contains("\r") || description.contains("\n")) {
+                    Log.warning("Multiple lines of text in description is not supported and description will be converted to single line by replacing new lines with spaces.");
+                    Log.warning("Original description:");
+                    Log.warning(description);
+                    String descs[] = description.split("\n");
+                    description = "";
+                    for (String desc : descs) {
+                        desc = desc.trim();
+                        if (desc.endsWith("\r")) { // In case new line was \r\n
+                            if (desc.length() != 1) {
+                                desc = desc.substring(0, desc.length() - 1);
+                            } else {
+                                continue;
+                            }
+                        }
+
+                        if (desc.isEmpty()) {
+                            continue;
+                        }
+
+                        if (!description.isEmpty()) {
+                            description += " ";
+                        }
+
+                        description += desc;
+                    }
+                    Log.warning("Converted description:");
+                    Log.warning(description);
+                }
+            }
+        }
+
+        return description;
+    }
+
+    public void setIcons(IconDesc [] icons) {
+        this.icons = icons;
+    }
+
+    public IconDesc getIcon() {
+        for (IconDesc icon : icons) {
+            if (icon.getKind() == IconDesc.ICON_KIND_DEFAULT) {
+                return icon;
+            }
+        }
+
+        for (IconDesc icon : icons) {
+            if (icon.getKind() == IconDesc.ICON_KIND_SHORTCUT) {
+                return icon;
+            }
+        }
+
+        return null;
+    }
+
+    public String getIconLocation() {
+        IconDesc icon = getIcon();
+        if (icon != null) {
+            return icon.getLocalLocation();
+        }
+
+        return null;
+    }
+
+    public void setShortcuts(ShortcutDesc shortcuts) {
+        this.shortcuts = shortcuts;
+    }
+
+    public boolean isDesktopHint() {
+        if (shortcuts != null) {
+            return shortcuts.getDesktop();
+        }
+
+        return false;
+    }
+
+    public boolean isMenuHint() {
+        if (shortcuts != null) {
+            return shortcuts.getMenu();
+        }
+
+        return false;
+    }
+
+    public String getSubMenu() {
+        if (shortcuts != null) {
+            return shortcuts.getSubmenu();
+        }
+
+        return null;
+    }
+
+    public void setAssociations(AssociationDesc [] associations) {
+        this.associations = associations;
+    }
+
+    public AssociationDesc [] getAssociations() {
+         return associations;
+    }
+
+    public void setMainClass(String mainClass, boolean isJavafxDesc) {
+        if (isJavafxDesc) {
+            this.mainClass = mainClass;
+        } else if (this.mainClass == null) {
+            this.mainClass = mainClass;
+        }
+    }
+
+    public String getMainClass() {
+        return mainClass;
+    }
+
+    public void addArguments(String argument) {
+        if (argument != null && !argument.isEmpty()) {
+            arguments.add(argument);
+        }
+    }
+
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    public void setProperty(String name, String value) {
+        if (name.equalsIgnoreCase("jnlp.versionEnabled") && value.equalsIgnoreCase("true")) {
+            isVersionEnabled = true;
+            return;
+        }
+
+        addVMArg("-D" + name + "=" + value);
+    }
+
+    public boolean isVersionEnabled() {
+        return isVersionEnabled;
+    }
+
+    public boolean isSandbox() {
+        return isSandbox;
+    }
+
+    public void setIsSandbox(boolean value) {
+        isSandbox = value;
+    }
+
+    public boolean isFXApp() {
+        return isFXApp;
+    }
+
+    public void setIsFXApp(boolean value) {
+        isFXApp = value;
+    }
+
+    public void addFile(String file) {
+        if (file != null) {
+            files.add(file);
+        }
+    }
+
+    public List<String> getFiles() {
+        return files;
+    }
+
+    private boolean isResourceExists(JARDesc resource) {
+        for (JARDesc r : resources) {
+            if (r.getLocation().equals(resource.getLocation())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public void addResource(JARDesc resource) {
+        if (resource != null) {
+            if (isResourceExists(resource)) {
+                Log.warning("Ignoring repeated resource " + resource.getLocation());
+                return;
+            }
+            resources.add(resource);
+        }
+    }
+
+    public List<JARDesc> getResources() {
+        return resources;
+    }
+
+    public void addVMArg(String arg) {
+        if (arg != null) {
+            vmArgs.add(arg);
+        }
+    }
+
+    public List<String> getVMArgs() {
+        return vmArgs;
+    }
+
+    public void setIsLibrary(boolean isLibrary) {
+        this.isLibrary = isLibrary;
+    }
+
+    public boolean isLibrary() {
+        return isLibrary;
+    }
+
+    public void setIsInstaller(boolean isInstaller) {
+        this.isInstaller = isInstaller;
+    }
+
+    public boolean isInstaller() {
+        return isInstaller;
+    }
+
+    public void setIsJRESet(boolean isJRESet) {
+        this.isJRESet = isJRESet;
+    }
+
+    public boolean isJRESet() {
+        return isJRESet;
+    }
+
+    public void setResourcesDesc(ResourcesDesc resourcesDesc) {
+        this.resourcesDesc = resourcesDesc;
+    }
+
+    public ResourcesDesc getResourcesDesc() {
+        return resourcesDesc;
+    }
+
+    public void parseResourceDesc() throws Exception {
+        if (resourcesDesc != null && !resourcesDesc.isEmpty()) {
+            setMainJar(resourcesDesc.getMainJar().getName());
+
+            JARDesc[] jars = resourcesDesc.getAllJarDescs();
+            for (JARDesc jar : jars) {
+                addResource(jar);
+            }
+
+            JREDesc jreDesc = resourcesDesc.getJreDesc();
+            if (jreDesc != null) {
+                String [] args = jreDesc.getVmArgsList();
+                if (args != null) {
+                    for (String arg : args) {
+                        addVMArg(arg);
+                    }
+                }
+
+                if (jreDesc.getMinHeap() != -1) {
+                    addVMArg("-Xms" + jreDesc.getMinHeap());
+                }
+
+                if (jreDesc.getMaxHeap() != -1) {
+                    addVMArg("-Xmx" + jreDesc.getMaxHeap());
+                }
+            }
+
+            Properties props = resourcesDesc.getResourceProperties();
+            Enumeration e = props.propertyNames();
+            while (e.hasMoreElements()) {
+                String key = (String) e.nextElement();
+                String value = props.getProperty(key);
+                setProperty(key, value);
+            }
+        }
+    }
+
+    public static class InformationDesc {
+
+        private final String _title;
+        private final String _vendor;
+        private final String[] _descriptions;
+        private final IconDesc[] _icons;
+        private ShortcutDesc _shortcutHints;
+        private AssociationDesc[] _associations;
+
+        public InformationDesc(String title, String vendor,
+                String[] descriptions,
+                IconDesc[] icons,
+                ShortcutDesc shortcutHints,
+                AssociationDesc[] associations) {
+            _title = (title == null) ? "" : title;
+            _vendor = (vendor == null) ? "" : vendor;
+            if (descriptions == null) {
+                descriptions = new String[NOF_DESC];
+            }
+            _descriptions = descriptions;
+            _icons = icons;
+            _shortcutHints = shortcutHints;
+            _associations = associations;
+        }
+
+        /**
+         * Constants for the getInfoDescription
+         */
+        final public static int DESC_DEFAULT = 0;
+        final public static int DESC_SHORT = 1;
+        final public static int DESC_ONELINE = 2;
+        final public static int DESC_TOOLTIP = 3;
+        final public static int NOF_DESC = 4;
+
+        /**
+         * Information
+         */
+        public String getTitle() {
+            return _title;
+        }
+
+        public String getVendor() {
+            return _vendor;
+        }
+
+        public IconDesc[] getIcons() {
+            return _icons;
+        }
+
+        public ShortcutDesc getShortcut() {
+            if (_shortcutHints == null) {
+                return null;
+            }
+            return new ShortcutDesc(_shortcutHints.getDesktop(), _shortcutHints.getMenu(), _shortcutHints.getSubmenu());
+        }
+
+        public AssociationDesc[] getAssociations() {
+            return _associations;
+        }
+
+        /**
+         * Sets new shortcut hints.
+         *
+         * @param shortcutDesc the new shortcut hints to set
+         */
+        public void setShortcut(ShortcutDesc shortcut) {
+            _shortcutHints = shortcut;
+        }
+
+        /**
+         * Sets new associations.
+         *
+         * @param assoc the association to set
+         */
+        public void setAssociation(AssociationDesc assoc) {
+            if (assoc == null) {
+                _associations = null;
+            } else {
+                _associations = new AssociationDesc[]{assoc};
+            }
+        }
+
+        /**
+         * Returns the description of the given kind. will return null if none
+         * there
+         */
+        public String getDescription(int kind) {
+            return _descriptions[kind];
+        }
+
+        public String[] getDescription() {
+            return _descriptions;
+        }
+    }
+
+    public static class IconDesc {
+
+        private final String _location;
+        private String _localLocation;
+        private final int _kind;
+
+        final public static int ICON_KIND_DEFAULT = 0;
+        final public static int ICON_KIND_SHORTCUT = 5;
+
+        public IconDesc(URL location, int kind) {
+            _location = location.toExternalForm();
+            _kind = kind;
+        }
+
+        public String getLocation() {
+            return _location;
+        }
+
+        public void setLocalLocation(String localLocation) {
+            _localLocation = localLocation;
+        }
+
+        public String getLocalLocation() {
+            return _localLocation;
+        }
+
+        public int getKind() {
+            return _kind;
+        }
+    }
+
+    public static class ShortcutDesc {
+
+        private final boolean _desktop;
+        private final boolean _menu;
+        private final String _submenu;
+
+        public ShortcutDesc(boolean desktop, boolean menu, String submenu) {
+            _desktop = desktop;
+            _menu = menu;
+            _submenu = submenu;
+        }
+
+        public boolean getDesktop() {
+            return _desktop;
+        }
+
+        public boolean getMenu() {
+            return _menu;
+        }
+
+        public String getSubmenu() {
+            return _submenu;
+        }
+    }
+
+    public static class AssociationDesc {
+
+        private final String _extensions;
+        private final String _mimeType;
+        private final String _description;
+        private final String _icon;
+        private String _iconLocalLocation;
+
+        public AssociationDesc(String extensions, String mimeType, String description, URL icon) {
+            _extensions = extensions;
+            _mimeType = mimeType;
+            _description = description;
+            _icon = (icon != null) ? icon.toExternalForm() : null;
+        }
+
+        public void setIconLocalLocation(String localLocation) {
+            _iconLocalLocation = localLocation;
+        }
+
+        public String getIconLocalLocation() {
+            return _iconLocalLocation;
+        }
+
+        public String getExtensions() {
+            return _extensions;
+        }
+
+        public String getMimeType() {
+            return _mimeType;
+        }
+
+        public String getMimeDescription() {
+            return _description;
+        }
+
+        public String getIconUrl() {
+            return _icon;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourceType.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+/*
+ * Public super class for all resource entries
+ */
+interface ResourceType {
+    /** Visit this specific type */
+    void visit(ResourceVisitor visitor) throws Exception;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.PropertyDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+import jnlp.converter.parser.ResourcesDesc.ExtensionDesc;
+
+/**
+ * A visitor class for the various ResourceType objects
+ * with dummy visit methods for all types.
+ */
+public class ResourceVisitor {
+
+    public void visitJARDesc(JARDesc jad) {
+    }
+
+    public void visitPropertyDesc(PropertyDesc prd) {
+    }
+
+    public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+    }
+
+    public void visitJREDesc(JREDesc jrd) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.Properties;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import jnlp.converter.HTTPHelper;
+
+/**
+ * This class contains information about the codebase and properties, i.e., how
+ * to locate the classes and optional-packages
+ */
+public class ResourcesDesc implements ResourceType {
+
+    private final List<ResourceType> _list;
+    private volatile JNLPDesc _parent = null;
+
+    /**
+     * Create empty resource list
+     */
+    public ResourcesDesc() {
+        _list = new CopyOnWriteArrayList<>();
+    }
+
+    public JNLPDesc getParent() {
+        return _parent;
+    }
+
+    void setParent(JNLPDesc parent) {
+        _parent = parent;
+        for (int i = 0; i < _list.size(); i++) {
+            Object o = _list.get(i);
+            if (o instanceof JREDesc) {
+                JREDesc jredesc = (JREDesc) o;
+                if (jredesc.getNestedResources() != null) {
+                    jredesc.getNestedResources().setParent(parent);
+                }
+            }
+        }
+    }
+
+    public void addResource(ResourceType rd) {
+        if (rd != null) {
+            _list.add(rd);
+        }
+    }
+
+    boolean isEmpty() {
+        return _list.isEmpty();
+    }
+
+    public JARDesc[] getLocalJarDescs() {
+        ArrayList<JARDesc> jds = new ArrayList<>(_list.size());
+        for (ResourceType rt : _list) {
+            if (rt instanceof JARDesc) {
+                jds.add((JARDesc) rt);
+            }
+        }
+        return jds.toArray(new JARDesc[jds.size()]);
+    }
+
+    public JREDesc getJreDesc() {
+        for (ResourceType rt : _list) {
+            if (rt instanceof JREDesc) {
+                return (JREDesc)rt;
+            }
+        }
+
+        return null;
+    }
+
+    public ExtensionDesc[] getExtensionDescs() throws Exception {
+        final ArrayList<ExtensionDesc> extList = new ArrayList<>();
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+              // add all extensiondesc recursively
+                addExtToList(extList);
+            }
+        });
+        return extList.toArray(new ExtensionDesc[extList.size()]);
+    }
+
+    public JARDesc[] getAllJarDescs() throws Exception {
+        List<JARDesc> jarList = new ArrayList<>();
+        addJarsToList(jarList);
+        return jarList.toArray(new JARDesc[jarList.size()]);
+    }
+
+    /**
+     * Add to a list of all the ExtensionDesc. This method goes recusivly through
+     * all ExtensionDesc
+     */
+    private void addExtToList(final List<ExtensionDesc> list) throws Exception {
+        // Iterate through list an add ext jnlp to the list.
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+                if (ed.getExtensionDesc() != null) {
+                    ed.getExtensionDesc().getMainJar();
+                    ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc();
+                    if (rd != null) {
+                        rd.addExtToList(list);
+                    }
+                }
+                list.add(ed);
+            }
+        });
+    }
+
+    private void addJarsToList(final List<JARDesc> list) throws Exception {
+
+        // Iterate through list an add resources to the list.
+        // The ordering of resources are preserved
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitJARDesc(JARDesc jd) {
+                list.add(jd);
+            }
+
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+                if (ed.getExtensionDesc() != null) {
+                    ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc();
+                    if (rd != null) {
+                        rd.addJarsToList(list);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Get all the resources needed when a specific resource is requested.
+     * Returns null if no resource was found
+     */
+    public JARDesc[] getResource(final URL location) throws Exception {
+        final JARDesc[] resources = new JARDesc[1];
+        // Find the given resource
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitJARDesc(JARDesc jd) {
+                if (GeneralUtil.sameURLs(jd.getLocation(), location)) {
+                    resources[0] = jd;
+                }
+            }
+        });
+
+        // Found no resource?
+        if (resources[0] == null) {
+            return null;
+        }
+
+        // No part, so just one resource
+        return resources;
+    }
+
+    /* Returns the Expected Main Jar
+     *    first jar with attribute main="true"
+     *    else first jar if none has that attribute
+     *    will look in extensions, and nested resource blocks if matching
+     */
+    protected JARDesc getMainJar() throws Exception {
+        // Normal trick to get around final arguments to inner classes
+        final JARDesc[] results = new JARDesc[2];
+
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitJARDesc(JARDesc jd) {
+                if (jd.isJavaFile()) {
+                    // Keep track of first Java File
+                    if (results[0] == null || results[0].isNativeLib()) {
+                        results[0] = jd;
+                    }
+                    // Keep tack of Java File marked main
+                    if (jd.isMainJarFile()) {
+                        results[1] = jd;
+                    }
+                } else if (jd.isNativeLib()) {
+                    // if jnlp extension has only native lib
+                    if (results[0] == null) {
+                        results[0] = jd;
+                    }
+                }
+            }
+
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+            // only check if no main yet and it is not an installer
+                if (results[1] == null && !ed.isInstaller()) {
+                    JNLPDesc extLd = ed.getExtensionDesc();
+                    if (extLd != null && extLd.isLibrary()) {
+                        ResourcesDesc rd = extLd.getResourcesDesc();
+                        if (rd != null) {
+                          // look for main jar in extension resource
+                            rd.visit(this);
+                        }
+                    }
+                }
+            }
+        });
+
+        // Default is the first, if none is specified as main. This might
+        // return NULL if there is no JAR resources.
+        JARDesc first = results[0];
+        JARDesc main = results[1];
+
+        // if main is null then return first;
+        // libraries have no such thing as a main jar, so return first;
+        // otherwise return main
+        // only returns null if there are no jars.
+        return (main == null) ? first : main;
+    }
+
+    /*
+     *  Get the properties defined for this object
+     */
+    public Properties getResourceProperties() throws Exception {
+        final Properties props = new Properties();
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitPropertyDesc(PropertyDesc pd) {
+                props.setProperty(pd.getKey(), pd.getValue());
+            }
+
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+                JNLPDesc jnlpd = ed.getExtensionDesc();
+                ResourcesDesc rd = jnlpd.getResourcesDesc();
+                if (rd != null) {
+                    Properties extProps = rd.getResourceProperties();
+                    Enumeration e = extProps.propertyNames();
+                    while (e.hasMoreElements()) {
+                        String key = (String) e.nextElement();
+                        String value = extProps.getProperty(key);
+                        props.setProperty(key, value);
+                    }
+                }
+            }
+        });
+        return props;
+    }
+
+    /*
+     *  Get the properties defined for this object, in the right order.
+     */
+    public List<Property> getResourcePropertyList() throws Exception {
+        final LinkedList<Property> propList = new LinkedList<>();
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitPropertyDesc(PropertyDesc pd) {
+                propList.add(new Property(pd.getKey(), pd.getValue()));
+            }
+        });
+        return propList;
+    }
+
+    /**
+     * visitor dispatch
+     */
+    @Override
+    public void visit(ResourceVisitor rv) throws Exception {
+        for (int i = 0; i < _list.size(); i++) {
+            ResourceType rt = _list.get(i);
+            rt.visit(rv);
+        }
+    }
+
+    public void addNested(ResourcesDesc nested) throws Exception {
+        if (nested != null) {
+            nested.visit(new ResourceVisitor() {
+                @Override
+                public void visitJARDesc(JARDesc jd) {
+                    _list.add(jd);
+                }
+
+                @Override
+                public void visitPropertyDesc(PropertyDesc pd) {
+                    _list.add(pd);
+                }
+
+                @Override
+                public void visitExtensionDesc(ExtensionDesc ed) {
+                    _list.add(ed);
+                }
+            });
+        }
+
+    }
+
+    public static class JARDesc implements ResourceType {
+
+        private URL _location;
+        private String _locationString;
+        private String _version;
+        private boolean _isNativeLib;
+        private boolean _isMainFile;  // Only used for Java JAR files (a main JAR file is implicitly eager)
+        private ResourcesDesc _parent;   // Back-pointer to the Resources that contains this JAR
+
+        public JARDesc(URL location, String version, boolean isMainFile, boolean isNativeLib, ResourcesDesc parent) {
+            _location = location;
+            _locationString = GeneralUtil.toNormalizedString(location);
+            _version = version;
+            _isMainFile = isMainFile;
+            _isNativeLib = isNativeLib;
+            _parent = parent;
+        }
+
+        /**
+         * Type of JAR resource
+         */
+        public boolean isNativeLib() {
+            return _isNativeLib;
+        }
+
+        public boolean isJavaFile() {
+            return !_isNativeLib;
+        }
+
+        /**
+         * Returns URL/version for JAR file
+         */
+        public URL getVersionLocation() throws Exception {
+            if (getVersion() == null) {
+                return _location;
+            } else {
+                return GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion());
+            }
+        }
+
+        public URL getLocation() {
+            return _location;
+        }
+
+        public String getVersion() {
+            return _version;
+        }
+
+        public String getName() {
+            // File can be separated by '/' or '\\'
+            int index;
+            int index1 = _locationString.lastIndexOf('/');
+            int index2 = _locationString.lastIndexOf('\\');
+
+            if (index1 >= index2) {
+                index = index1;
+            } else {
+                index = index2;
+            }
+
+            if (index != -1) {
+                return _locationString.substring(index + 1, _locationString.length());
+            }
+
+            return null;
+        }
+
+        /**
+         * Returns if this is the main JAR file
+         */
+        public boolean isMainJarFile() {
+            return _isMainFile;
+        }
+
+        /**
+         * Get parent LaunchDesc
+         */
+        public ResourcesDesc getParent() {
+            return _parent;
+        }
+
+        /**
+         * Visitor dispatch
+         */
+        public void visit(ResourceVisitor rv) {
+            rv.visitJARDesc(this);
+        }
+    }
+
+    public static class PropertyDesc implements ResourceType {
+
+        private String _key;
+        private String _value;
+
+        public PropertyDesc(String key, String value) {
+            _key = key;
+            _value = value;
+        }
+
+        // Accessors
+        public String getKey() {
+            return _key;
+        }
+
+        public String getValue() {
+            return _value;
+        }
+
+        /**
+         * Visitor dispatch
+         */
+        public void visit(ResourceVisitor rv) {
+            rv.visitPropertyDesc(this);
+        }
+
+    }
+
+    public static class JREDesc implements ResourceType {
+
+        private String _version;
+        private long _maxHeap;
+        private long _minHeap;
+        private String _vmargs;
+        private ResourcesDesc _resourceDesc;
+        private JNLPDesc _extensioDesc;
+        private String _archList;
+
+        /*
+         * Constructor to create new instance based on the requirements from JNLP file.
+         */
+        public JREDesc(String version, long minHeap, long maxHeap, String vmargs,
+                       ResourcesDesc resourcesDesc, String archList) {
+
+            _version = version;
+            _maxHeap = maxHeap;
+            _minHeap = minHeap;
+            _vmargs = vmargs;
+            _resourceDesc = resourcesDesc;
+            _extensioDesc = null;
+            _archList = archList;
+        }
+
+        public String[] getArchList() {
+            return GeneralUtil.getStringList(_archList);
+        }
+
+        public String getVersion() {
+            return _version;
+        }
+
+        public long getMinHeap() {
+            return _minHeap;
+        }
+
+        public long getMaxHeap() {
+            return _maxHeap;
+        }
+
+        public String getVmArgs() {
+            return _vmargs;
+        }
+
+        public String[] getVmArgsList() {
+            return GeneralUtil.getStringList(_vmargs);
+        }
+
+        public ResourcesDesc getNestedResources() {
+            return _resourceDesc;
+        }
+
+        public JNLPDesc getExtensionDesc() {
+            return _extensioDesc;
+        }
+
+        public void setExtensionDesc(JNLPDesc ld) {
+            _extensioDesc = ld;
+        }
+
+        /* visitor dispatch */
+        public void visit(ResourceVisitor rv) {
+            rv.visitJREDesc(this);
+        }
+    }
+
+    public static class Property implements Cloneable {
+
+        public static final String JNLP_VERSION_ENABLED = "jnlp.versionEnabled";
+
+        String key;
+        String value;
+
+        public Property(String spec) {
+            spec = spec.trim();
+            if (!spec.startsWith("-D") || spec.length() < 3) {
+                throw new IllegalArgumentException("Property invalid");
+            }
+
+            int endKey = spec.indexOf("=");
+            if (endKey < 0) {
+                // it's legal to have no assignment
+                this.key = spec.substring(2); // skip "-D"
+                this.value = "";
+            } else {
+                this.key = spec.substring(2, endKey);
+                this.value = spec.substring(endKey + 1);
+            }
+        }
+
+        public static Property createProperty(String spec) {
+            Property prop = null;
+            try {
+                prop = new Property(spec);
+            } catch (IllegalArgumentException iae) {
+            }
+            return prop;
+        }
+
+        public Property(String key, String value) {
+            this.key = key;
+            if (value != null) {
+                this.value = value;
+            } else {
+                this.value = "";
+            }
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        // @return String representation, unquoted, unified presentation
+        public String toString() {
+            if (value.length() == 0) {
+                return "-D" + key;
+            }
+            return "-D" + key + "=" + value;
+        }
+
+        public void addTo(Properties props) {
+            props.setProperty(key, value);
+        }
+
+        // Hash Object
+        public boolean equals(Object o) {
+            if (!(o instanceof Property)) {
+                return false;
+            }
+            Property op = (Property) o;
+            int hashTheirs = op.hashCode();
+            int hashThis = hashCode();
+            return hashTheirs == hashThis;
+        }
+
+        public int hashCode() {
+            return key.hashCode();
+        }
+
+        private static List<Object> jnlpProps = Arrays.asList(new Object[]{
+            JNLP_VERSION_ENABLED
+        });
+
+        public static boolean isJnlpProperty(String spec) {
+            try {
+                Property p = new Property(spec);
+                return isJnlpPropertyKey(p.getKey());
+            } catch (Exception e) {
+                return false;
+            }
+        }
+
+        public static boolean isJnlpPropertyKey(String key) {
+            return key != null && jnlpProps.contains(key);
+        }
+    }
+
+    public static class ExtensionDesc implements ResourceType {
+        // Tag elements
+
+        private final URL _location;
+        private final String _locationString;
+        private final String _version;
+        private final URL _codebase;
+
+        // Link to launchDesc
+        private JNLPDesc _extensionLd; // Link to launchDesc for extension
+
+        public ExtensionDesc(URL location, String version) {
+            _location = location;
+            _locationString = GeneralUtil.toNormalizedString(location);
+            _version = version;
+            _codebase = GeneralUtil.asPathURL(GeneralUtil.getBase(location));
+            _extensionLd = null;
+        }
+
+        public boolean isInstaller() throws Exception {
+            if (getExtensionDesc() != null) {
+                return _extensionLd.isInstaller();
+            }
+            return false;
+        }
+
+        public URL getLocation() {
+            return _location;
+        }
+
+        public String getVersionLocation() throws Exception {
+            if (getVersion() == null) {
+                return _locationString;
+            } else {
+                return GeneralUtil.toNormalizedString(GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion()));
+            }
+        }
+
+        public String getVersion() {
+            return _version;
+        }
+
+        public URL getCodebase() {
+            return _codebase;
+        }
+
+        /*
+         * Information about the resources
+         */
+        public JNLPDesc getExtensionDesc() throws Exception {
+            if (_extensionLd == null) {
+                byte[] bits = HTTPHelper.getJNLPBits(getVersionLocation(), _locationString);
+                _extensionLd = XMLFormat.parse(bits, getCodebase(), getVersionLocation());
+            }
+            return _extensionLd;
+        }
+
+        public void setExtensionDesc(JNLPDesc desc) {
+            _extensionLd = desc;
+        }
+
+        /**
+         * Visitor dispatch
+         */
+        public void visit(ResourceVisitor rv) throws Exception {
+            rv.visitExtensionDesc(this);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/VersionID.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ *  VersionID contains a JNLP version ID.
+ *
+ *  The VersionID also contains a prefix indicator that can
+ *  be used when stored with a VersionString
+ *
+ */
+public class VersionID implements Comparable<VersionID> {
+    private final String[] _tuple;           // Array of Integer or String objects
+    private final boolean  _usePrefixMatch;  // star (*) prefix
+    private final boolean  _useGreaterThan;  // plus (+) greather-than
+    private final boolean  _isCompound;      // and (&) operator
+    private final VersionID _rest;           // remaining part after the &
+
+    /**
+     * Creates a VersionID object from a given <code>String</code>.
+     * @param str version string to parse
+     */
+    public VersionID(String str) {
+        if (str == null || str.length() == 0) {
+            _tuple = new String[0];
+            _useGreaterThan = false;
+            _usePrefixMatch = false;
+            _isCompound = false;
+            _rest = null;
+            return;
+        }
+
+        // Check for compound
+        int amp = str.indexOf("&");
+        if (amp >= 0) {
+            _isCompound = true;
+            VersionID firstPart = new VersionID(str.substring(0, amp));
+            _rest = new VersionID(str.substring(amp+1));
+            _tuple = firstPart._tuple;
+            _usePrefixMatch = firstPart._usePrefixMatch;
+            _useGreaterThan = firstPart._useGreaterThan;
+        } else {
+            _isCompound = false;
+            _rest = null;
+            // Check for postfix
+            if (str.endsWith("+")) {
+                _useGreaterThan = true;
+                _usePrefixMatch = false;
+                str = str.substring(0, str.length() - 1);
+            } else if (str.endsWith("*")) {
+                _useGreaterThan = false;
+                _usePrefixMatch = true;
+                str = str.substring(0, str.length() - 1);
+            } else {
+                _useGreaterThan = false;
+                _usePrefixMatch = false;
+            }
+
+            ArrayList<String> list = new ArrayList<>();
+            int start = 0;
+            for (int i = 0; i < str.length(); i++) {
+                // Split at each separator character
+                if (".-_".indexOf(str.charAt(i)) != -1) {
+                    if (start < i) {
+                        String value = str.substring(start, i);
+                        list.add(value);
+                    }
+                    start = i + 1;
+                }
+            }
+            if (start < str.length()) {
+                list.add(str.substring(start, str.length()));
+            }
+            _tuple = list.toArray(new String[0]);
+        }
+    }
+
+    /** @return true if no flags are set */
+    public boolean isSimpleVersion() {
+        return !_useGreaterThan && !_usePrefixMatch && !_isCompound;
+    }
+
+    /** Match 'this' versionID against vid.
+     *  The _usePrefixMatch/_useGreaterThan flag is used to determine if a
+     *  prefix match of an exact match should be performed
+     *  if _isCompound, must match _rest also.
+     */
+    public boolean match(VersionID vid) {
+        if (_isCompound) {
+            if (!_rest.match(vid)) {
+                return false;
+            }
+        }
+        return (_usePrefixMatch) ? this.isPrefixMatchTuple(vid) :
+            (_useGreaterThan) ? vid.isGreaterThanOrEqualTuple(this) :
+                matchTuple(vid);
+    }
+
+    /** Compares if two version IDs are equal */
+    @Override
+    public boolean equals(Object o) {
+        if (matchTuple(o)) {
+             VersionID ov = (VersionID) o;
+             if (_rest == null || _rest.equals(ov._rest)) {
+                if ((_useGreaterThan == ov._useGreaterThan) &&
+                    (_usePrefixMatch == ov._usePrefixMatch)) {
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Computes a hash code for a VersionID */
+    @Override
+    public int hashCode() {
+        boolean first = true;
+        int hashCode = 0;
+        for (String tuple : _tuple) {
+            if (first) {
+                first = false;
+                hashCode = tuple.hashCode();
+            } else {
+                hashCode = hashCode ^ tuple.hashCode();
+            }
+        }
+        return hashCode;
+    }
+
+    /** Compares if two version IDs are equal */
+    private boolean matchTuple(Object o) {
+        // Check for null and type
+        if (o == null || !(o instanceof VersionID)) {
+            return false;
+        }
+        VersionID vid = (VersionID) o;
+
+        // Normalize arrays
+        String[] t1 = normalize(_tuple, vid._tuple.length);
+        String[] t2 = normalize(vid._tuple, _tuple.length);
+
+        // Check contents
+        for (int i = 0; i < t1.length; i++) {
+            Object o1 = getValueAsObject(t1[i]);
+            Object o2 = getValueAsObject(t2[i]);
+            if (!o1.equals(o2)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private Object getValueAsObject(String value) {
+        if (value.length() > 0 && value.charAt(0) != '-') {
+            try {
+                return Integer.valueOf(value);
+            } catch (NumberFormatException nfe) {
+                /* fall through */
+            }
+        }
+        return value;
+    }
+
+    public boolean isGreaterThan(VersionID vid) {
+        if (vid == null) {
+            return false;
+        }
+        return isGreaterThanOrEqualHelper(vid, false, true);
+    }
+
+    public boolean isGreaterThanOrEqual(VersionID vid) {
+        if (vid == null) {
+            return false;
+        }
+        return isGreaterThanOrEqualHelper(vid, true, true);
+    }
+
+    boolean isGreaterThanOrEqualTuple(VersionID vid) {
+        return isGreaterThanOrEqualHelper(vid, true, false);
+    }
+
+    /** Compares if 'this' is greater than vid */
+    private boolean isGreaterThanOrEqualHelper(VersionID vid,
+        boolean allowEqual, boolean useRest) {
+
+        if (useRest && _isCompound) {
+            if (!_rest.isGreaterThanOrEqualHelper(vid, allowEqual, true)) {
+                return false;
+            }
+        }
+        // Normalize the two strings
+        String[] t1 = normalize(_tuple, vid._tuple.length);
+        String[] t2 = normalize(vid._tuple, _tuple.length);
+
+        for (int i = 0; i < t1.length; i++) {
+            // Compare current element
+            Object e1 = getValueAsObject(t1[i]);
+            Object e2 = getValueAsObject(t2[i]);
+            if (e1.equals(e2)) {
+                // So far so good
+            } else {
+                if (e1 instanceof Integer && e2 instanceof Integer) {
+                    // if both can be parsed as ints, compare ints
+                    return ((Integer)e1).intValue() > ((Integer)e2).intValue();
+                } else {
+                    if (e1 instanceof Integer)  {
+                        return false; // e1 (numeric) < e2 (non-numeric)
+                    } else if (e2 instanceof Integer) {
+                        return true; // e1 (non-numeric) > e2 (numeric)
+                    }
+
+                    String s1 = t1[i];
+                    String s2 = t2[i];
+
+                    return s1.compareTo(s2) > 0;
+                }
+
+            }
+        }
+        // If we get here, they are equal
+        return allowEqual;
+    }
+
+    /** Checks if 'this' is a prefix of vid */
+    private boolean isPrefixMatchTuple(VersionID vid) {
+
+        // Make sure that vid is at least as long as the prefix
+        String[] t2 = normalize(vid._tuple, _tuple.length);
+
+        for (int i = 0; i < _tuple.length; i++) {
+            Object e1 = _tuple[i];
+            Object e2 = t2[i];
+            if (e1.equals(e2)) {
+                // So far so good
+            } else {
+                // Not a prefix
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Normalize an array to a certain lengh */
+    private String[] normalize(String[] list, int minlength) {
+        if (list.length < minlength) {
+            // Need to do padding
+            String[] newlist = new String[minlength];
+            System.arraycopy(list, 0, newlist, 0, list.length);
+            Arrays.fill(newlist, list.length, newlist.length, "0");
+            return newlist;
+        } else {
+            return list;
+        }
+    }
+
+    @Override
+    public int compareTo(VersionID o) {
+        if (o == null || !(o instanceof VersionID)) {
+            return -1;
+        }
+        VersionID vid = o;
+        return equals(vid) ? 0 : (isGreaterThanOrEqual(vid) ? 1 : -1);
+    }
+
+    /** Show it as a string */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < _tuple.length - 1; i++) {
+            sb.append(_tuple[i]);
+            sb.append('.');
+        }
+        if (_tuple.length > 0) {
+            sb.append(_tuple[_tuple.length - 1]);
+        }
+        if (_useGreaterThan) {
+            sb.append('+');
+        }
+        if (_usePrefixMatch) {
+            sb.append('*');
+        }
+        if (_isCompound) {
+            sb.append("&");
+            sb.append(_rest);
+        }
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/VersionString.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/*
+ * Utility class that knows to handle version strings
+ * A version string is of the form:
+ *
+ *  (version-id ('+'?) ' ') *
+ *
+ */
+public class VersionString {
+    private final ArrayList<VersionID> _versionIds;
+
+    /** Constructs a VersionString object from string */
+    public VersionString(String vs) {
+        _versionIds = new ArrayList<>();
+        if (vs != null) {
+            StringTokenizer st = new StringTokenizer(vs, " ", false);
+            while (st.hasMoreElements()) {
+                // Note: The VersionID class takes care of a postfixed '+'
+                _versionIds.add(new VersionID(st.nextToken()));
+            }
+        }
+    }
+
+    public VersionString(VersionID id) {
+        _versionIds = new ArrayList<>();
+        if (id != null) {
+            _versionIds.add(id);
+        }
+    }
+
+    public boolean isSimpleVersion() {
+        if (_versionIds.size() == 1) {
+            return _versionIds.get(0).isSimpleVersion();
+        }
+        return false;
+    }
+
+    /** Check if this VersionString object contains the VersionID m */
+    public boolean contains(VersionID m) {
+        for (int i = 0; i < _versionIds.size(); i++) {
+            VersionID vi = _versionIds.get(i);
+            boolean check = vi.match(m);
+            if (check) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Check if this VersionString object contains the VersionID m, given as a string */
+    public boolean contains(String versionid) {
+        return contains(new VersionID(versionid));
+    }
+
+    /** Check if this VersionString object contains anything greater than m */
+    public boolean containsGreaterThan(VersionID m) {
+        for (int i = 0; i < _versionIds.size(); i++) {
+            VersionID vi = _versionIds.get(i);
+            boolean check = vi.isGreaterThan(m);
+            if (check) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Check if this VersionString object contains anything greater than the VersionID m, given as a string */
+    public boolean containsGreaterThan(String versionid) {
+        return containsGreaterThan(new VersionID(versionid));
+    }
+
+    /** Check if the versionString 'vs' contains the VersionID 'vi' */
+    public static boolean contains(String vs, String vi) {
+        return (new VersionString(vs)).contains(vi);
+    }
+
+    /** Pretty-print object */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < _versionIds.size(); i++) {
+            sb.append(_versionIds.get(i).toString());
+            sb.append(' ');
+        }
+        return sb.toString();
+    }
+
+    public List<VersionID> getAllVersionIDs() {
+        return _versionIds;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.ArrayList;
+import jnlp.converter.JNLPConverter;
+import jnlp.converter.parser.exception.MissingFieldException;
+import jnlp.converter.parser.exception.BadFieldException;
+import jnlp.converter.parser.exception.JNLParseException;
+import jnlp.converter.parser.xml.XMLEncoding;
+import jnlp.converter.parser.xml.XMLParser;
+import jnlp.converter.parser.xml.XMLNode;
+import jnlp.converter.parser.JNLPDesc.AssociationDesc;
+import jnlp.converter.parser.JNLPDesc.IconDesc;
+import jnlp.converter.parser.JNLPDesc.InformationDesc;
+import jnlp.converter.parser.JNLPDesc.ShortcutDesc;
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+import jnlp.converter.Log;
+import jnlp.converter.parser.ResourcesDesc.ExtensionDesc;
+import jnlp.converter.parser.ResourcesDesc.PropertyDesc;
+import org.xml.sax.SAXParseException;
+
+public class XMLFormat {
+
+    public static XMLNode parseBits(byte[] bits) throws JNLParseException {
+        return parse(decode(bits));
+    }
+
+    private static String decode(byte[] bits) throws JNLParseException {
+        try {
+            return XMLEncoding.decodeXML(bits);
+        } catch (Exception e) {
+            throw new JNLParseException(e,
+                "exception determining encoding of jnlp file", 0);
+        }
+    }
+
+    private static XMLNode parse(String source) throws JNLParseException {
+        try {
+            return (new XMLParser(source).parse());
+        } catch (SAXParseException spe) {
+            throw new JNLParseException(spe,
+                        "exception parsing jnlp file", spe.getLineNumber());
+        } catch (Exception e) {
+            throw new JNLParseException(e,
+                        "exception parsing jnlp file", 0);
+        }
+    }
+
+    /**
+     * thisCodebase, if set, is used to determine the codebase,
+     *     if JNLP codebase is not absolute.
+     *
+     * @param thisCodebase base URL of this JNLPDesc location
+     */
+    public static JNLPDesc parse(byte[] bits, URL thisCodebase, String jnlp)
+            throws Exception {
+
+        JNLPDesc jnlpd = new JNLPDesc();
+        String source = decode(bits).trim();
+        XMLNode root = parse(source);
+
+        if (root == null || root.getName() == null) {
+            throw new JNLParseException(null, null, 0);
+        }
+
+        // Check that root element is a <jnlp> tag
+        if (!root.getName().equals("jnlp")) {
+            throw (new MissingFieldException(source, "<jnlp>"));
+        }
+
+        // Read <jnlp> attributes (path is empty, i.e., "")
+        // (spec, version, codebase, href)
+        String specVersion = XMLUtils.getAttribute(root, "", "spec", "1.0+");
+        jnlpd.setSpecVersion(specVersion);
+        String version = XMLUtils.getAttribute(root, "", "version");
+        jnlpd.setVersion(version);
+
+        // Make sure the codebase URL ends with a '/'.
+        //
+        // Regarding the JNLP spec,
+        // the thisCodebase is used to determine the codebase.
+        //      codebase = new URL(thisCodebase, codebase)
+        URL codebase = GeneralUtil.asPathURL(XMLUtils.getAttributeURL(source,
+            thisCodebase, root, "", "codebase"));
+        if (codebase == null && thisCodebase != null) {
+            codebase = thisCodebase;
+        }
+        jnlpd.setCodebase(codebase.toExternalForm());
+
+        // Get href for JNLP file
+        URL href = XMLUtils.getAttributeURL(source, codebase, root, "", "href");
+        jnlpd.setHref(href.toExternalForm());
+
+        // Read <security> attributes
+        if (XMLUtils.isElementPath(root, "<security><all-permissions>")) {
+            jnlpd.setIsSandbox(false);
+        } else if (XMLUtils.isElementPath(root,
+                "<security><j2ee-application-client-permissions>")) {
+            jnlpd.setIsSandbox(false);
+        }
+
+        // We can be fxapp, and also be applet, or application, or neither
+        boolean isFXApp = false;
+        boolean isApplet = false;
+        if (XMLUtils.isElementPath(root, "<javafx-desc>")) {
+            // no new type for javafx-desc - needs one of the others
+            buildFXAppDesc(source, root, "<javafx-desc>", jnlpd);
+            jnlpd.setIsFXApp(true);
+            isFXApp = true;
+        }
+
+        /*
+         * Note - the jnlp specification says there must be exactly one of
+         * the descriptor types.  This code has always violated (or at least
+         * not checked for) that condition.
+         * Instead it uses precedent order app, component, installer, applet
+         * and ignores any other descriptors given.
+         */
+        if (XMLUtils.isElementPath(root, "<application-desc>")) {
+            buildApplicationDesc(source, root, jnlpd);
+        } else if (XMLUtils.isElementPath(root, "<component-desc>")) {
+            jnlpd.setIsLibrary(true);
+        } else if (XMLUtils.isElementPath(root, "<installer-desc>")) {
+            Log.warning("<installer-desc> is not supported and will be ignored in " + jnlp);
+            jnlpd.setIsInstaller(true);
+        } else if (XMLUtils.isElementPath(root, "<applet-desc>")) {
+            isApplet = true;
+        } else {
+            if (!isFXApp) {
+                throw (new MissingFieldException(source,
+                    "<jnlp>(<application-desc>|<applet-desc>|" +
+                    "<installer-desc>|<component-desc>)"));
+            }
+        }
+
+        if (isApplet && !isFXApp) {
+            Log.error("Applet based applications deployed with <applet-desc> element are not supported.");
+        }
+
+        if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) {
+            buildInformationDesc(source, codebase, root, jnlpd);
+        }
+
+        if (!jnlpd.isInstaller()) {
+            buildResourcesDesc(source, codebase, root, false, jnlpd);
+        }
+
+        if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) {
+            jnlpd.parseResourceDesc();
+        }
+
+        if (!jnlpd.isInstaller()) {
+            if (jnlpd.isSandbox()) {
+                if (jnlpd.isLibrary()) {
+                    Log.warning(jnlp + " is sandbox extension. JNLPConverter does not support sandbox environment and converted application will run without security manager.");
+                } else {
+                    Log.warning("This is sandbox Web-Start application. JNLPConverter does not support sandbox environment and converted application will run without security manager.");
+                }
+            }
+        }
+
+        return jnlpd;
+    }
+
+    /**
+     * Create a combine informationDesc in the two informationDesc.
+     * The information present in id1 overwrite the information present in id2
+     */
+    private static InformationDesc combineInformationDesc(
+                                   InformationDesc id1, InformationDesc id2) {
+        if (id1 == null) {
+            return id2;
+        }
+        if (id2 == null) {
+            return id1;
+        }
+
+        String t1 = id1.getTitle();
+        String title  = (t1 != null && t1.length() > 0) ?
+            t1 : id2.getTitle();
+        String v1 = id1.getVendor();
+        String vendor = (v1 != null && v1.length() > 0) ?
+            v1 : id2.getVendor();
+
+        /** Copy descriptions */
+        String[] descriptions = new String[InformationDesc.NOF_DESC];
+        for (int i = 0; i < descriptions.length; i++) {
+            descriptions[i] = (id1.getDescription(i) != null)
+                    ? id1.getDescription(i) : id2.getDescription(i);
+        }
+
+        /** Icons */
+        ArrayList<IconDesc> iconList = new ArrayList<>();
+        if (id2.getIcons() != null) {
+            iconList.addAll(Arrays.asList(id2.getIcons()));
+        }
+        if (id1.getIcons() != null) {
+            iconList.addAll(Arrays.asList(id1.getIcons()));
+        }
+        IconDesc[] icons = new IconDesc[iconList.size()];
+        icons = iconList.toArray(icons);
+
+        ShortcutDesc hints = (id1.getShortcut() != null) ?
+                             id1.getShortcut() : id2.getShortcut();
+
+        AssociationDesc[] asd = ( AssociationDesc[] ) addArrays(
+            (Object[])id1.getAssociations(), (Object[])id2.getAssociations());
+
+        return new InformationDesc(title,
+                                   vendor,
+                                   descriptions,
+                                   icons,
+                                   hints,
+                                   asd);
+    }
+
+    /** Extract data from <information> tag */
+    private static void buildInformationDesc(final String source, final URL codebase, XMLNode root, JNLPDesc jnlpd)
+        throws MissingFieldException, BadFieldException {
+        final ArrayList<InformationDesc> list = new ArrayList<>();
+
+        // Iterates over all <information> nodes ignoring the type
+        XMLUtils.visitElements(root,
+            "<information>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws
+                BadFieldException, MissingFieldException {
+
+                // Check for right os, arch, and locale
+                String[] os = GeneralUtil.getStringList(
+                            XMLUtils.getAttribute(e, "", "os", null));
+                String[] arch = GeneralUtil.getStringList(
+                            XMLUtils.getAttribute(e, "", "arch", null));
+                String[] locale = GeneralUtil.getStringList(
+                            XMLUtils.getAttribute(e, "", "locale", null));
+                if (GeneralUtil.prefixMatchStringList(
+                                os, GeneralUtil.getOSFullName()) &&
+                    GeneralUtil.prefixMatchArch(arch) &&
+                    matchDefaultLocale(locale))
+                {
+                    // Title, vendor
+                    String title = XMLUtils.getElementContents(e, "<title>");
+                    String vendor = XMLUtils.getElementContents(e, "<vendor>");
+
+                    // Descriptions
+                    String[] descriptions =
+                                new String[InformationDesc.NOF_DESC];
+                    descriptions[InformationDesc.DESC_DEFAULT] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "", null);
+                    descriptions[InformationDesc.DESC_ONELINE] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "one-line", null);
+                    descriptions[InformationDesc.DESC_SHORT] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "short", null);
+                    descriptions[InformationDesc.DESC_TOOLTIP] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "tooltip", null);
+
+                    // Icons
+                    IconDesc[] icons = getIconDescs(source, codebase, e);
+
+                    // Shortcut hints
+                    ShortcutDesc shortcuts = getShortcutDesc(e);
+
+                    // Association hints
+                    AssociationDesc[] associations = getAssociationDesc(
+                                                        source, codebase, e);
+
+                    list.add(new InformationDesc(
+                        title, vendor, descriptions, icons,
+                        shortcuts, associations));
+                }
+            }
+        });
+
+        /* Combine all information desc. information in a single one for
+         * the current locale using the following priorities:
+         *   1. locale == language_country_variant
+         *   2. locale == lauguage_country
+         *   3. locale == lauguage
+         *   4. no or empty locale
+         */
+        InformationDesc normId = new InformationDesc(null, null, null, null, null, null);
+        for (InformationDesc id : list) {
+            normId = combineInformationDesc(id, normId);
+        }
+
+        jnlpd.setTitle(normId.getTitle());
+        jnlpd.setVendor(normId.getVendor());
+        jnlpd.setDescriptions(normId.getDescription());
+        jnlpd.setIcons(normId.getIcons());
+        jnlpd.setShortcuts(normId.getShortcut());
+        jnlpd.setAssociations(normId.getAssociations());
+    }
+
+    private static Object[] addArrays (Object[] a1, Object[] a2) {
+        if (a1 == null) {
+            return a2;
+        }
+        if (a2 == null) {
+            return a1;
+        }
+        ArrayList<Object> list = new ArrayList<>();
+        int i;
+        for (i=0; i<a1.length; list.add(a1[i++]));
+        for (i=0; i<a2.length; list.add(a2[i++]));
+        return list.toArray(a1);
+    }
+
+    public static boolean matchDefaultLocale(String[] localeStr) {
+        return GeneralUtil.matchLocale(localeStr, GeneralUtil.getDefaultLocale());
+    }
+
+    /** Extract data from <resources> tag. There is only one. */
+    static void buildResourcesDesc(final String source,
+            final URL codebase, XMLNode root, final boolean ignoreJres, JNLPDesc jnlpd)
+            throws MissingFieldException, BadFieldException {
+        // Extract classpath directives
+        final ResourcesDesc rdesc = new ResourcesDesc();
+
+        // Iterate over all entries
+        XMLUtils.visitElements(root, "<resources>",
+                new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e)
+                    throws MissingFieldException, BadFieldException {
+                // Check for right os, archictecture, and locale
+                String[] os = GeneralUtil.getStringList(
+                        XMLUtils.getAttribute(e, "", "os", null));
+                final String arch = XMLUtils.getAttribute(e, "", "arch", null);
+                String[] locale = GeneralUtil.getStringList(
+                        XMLUtils.getAttribute(e, "", "locale", null));
+                if (GeneralUtil.prefixMatchStringList(
+                        os, GeneralUtil.getOSFullName())
+                        && matchDefaultLocale(locale)) {
+                    // Now visit all children in this node
+                    XMLUtils.visitChildrenElements(e,
+                            new XMLUtils.ElementVisitor() {
+                        @Override
+                        public void visitElement(XMLNode e2)
+                                throws MissingFieldException, BadFieldException {
+                            handleResourceElement(source, codebase,
+                                    e2, rdesc, ignoreJres, arch, jnlpd);
+                        }
+                    });
+                }
+            }
+        });
+
+        if (!rdesc.isEmpty()) {
+            jnlpd.setResourcesDesc(rdesc);
+        }
+    }
+
+    private static IconDesc[] getIconDescs(final String source,
+            final URL codebase, XMLNode e)
+            throws MissingFieldException, BadFieldException {
+        final ArrayList<IconDesc> answer = new ArrayList<>();
+        XMLUtils.visitElements(e, "<icon>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode icon) throws
+                    MissingFieldException, BadFieldException {
+                String kindStr = XMLUtils.getAttribute(icon, "", "kind", "");
+                URL href = XMLUtils.getRequiredURL(source, codebase, icon, "", "href");
+
+                if (href != null) {
+                    if (!JNLPConverter.isIconSupported(href.toExternalForm())) {
+                        return;
+                    }
+                }
+
+                int kind;
+                if (kindStr == null || kindStr.isEmpty() || kindStr.equals("default")) {
+                    kind = IconDesc.ICON_KIND_DEFAULT;
+                } else if (kindStr.equals("shortcut")) {
+                    kind = IconDesc.ICON_KIND_SHORTCUT;
+                } else {
+                    Log.warning("Ignoring unsupported icon \"" + href + "\" with kind \"" + kindStr + "\".");
+                    return;
+                }
+
+                answer.add(new IconDesc(href, kind));
+            }
+        });
+        return answer.toArray(new IconDesc[answer.size()]);
+    }
+
+    private static ShortcutDesc getShortcutDesc(XMLNode e)
+                throws MissingFieldException, BadFieldException {
+        final ArrayList<ShortcutDesc> shortcuts = new ArrayList<>();
+
+        XMLUtils.visitElements(e, "<shortcut>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode shortcutNode)
+                throws MissingFieldException, BadFieldException {
+                boolean desktopHinted =
+                    XMLUtils.isElementPath(shortcutNode, "<desktop>");
+                boolean menuHinted =
+                    XMLUtils.isElementPath(shortcutNode, "<menu>");
+                String submenuHinted =
+                    XMLUtils.getAttribute(shortcutNode, "<menu>", "submenu");
+                shortcuts.add(new ShortcutDesc(desktopHinted, menuHinted, submenuHinted));
+            }
+        });
+
+        if (shortcuts.size() > 0) {
+            return shortcuts.get(0);
+        }
+        return null;
+    }
+
+    private static AssociationDesc[] getAssociationDesc(final String source,
+        final URL codebase, XMLNode e)
+                throws MissingFieldException, BadFieldException {
+        final ArrayList<AssociationDesc> answer = new ArrayList<>();
+        XMLUtils.visitElements(e, "<association>",
+            new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode node)
+                throws MissingFieldException, BadFieldException {
+
+                String extensions = XMLUtils.getAttribute(
+                                       node, "", "extensions");
+
+                String mimeType = XMLUtils.getAttribute(
+                                       node, "", "mime-type");
+                String description = XMLUtils.getElementContents(
+                                        node, "<description>");
+
+                URL icon = XMLUtils.getAttributeURL(
+                                source, codebase, node, "<icon>", "href");
+
+                if (!JNLPConverter.isIconSupported(icon.toExternalForm())) {
+                    icon = null;
+                }
+
+                if (extensions == null && mimeType == null) {
+                    throw new MissingFieldException(source,
+                                 "<association>(<extensions><mime-type>)");
+                } else if (extensions == null) {
+                    throw new MissingFieldException(source,
+                                     "<association><extensions>");
+                } else if (mimeType == null) {
+                    throw new MissingFieldException(source,
+                                     "<association><mime-type>");
+                }
+
+                // don't support uppercase extension and mime-type on gnome.
+                if ("gnome".equals(System.getProperty("sun.desktop"))) {
+                    extensions = extensions.toLowerCase();
+                    mimeType = mimeType.toLowerCase();
+                }
+
+                answer.add(new AssociationDesc(extensions, mimeType,
+                                                description, icon));
+            }
+        });
+        return answer.toArray(
+                new AssociationDesc[answer.size()]);
+    }
+
+    /** Handle the individual entries in a resource desc */
+    private static void handleResourceElement(String source, URL codebase,
+        XMLNode e, ResourcesDesc rdesc, boolean ignoreJres, String arch, JNLPDesc jnlpd)
+        throws MissingFieldException, BadFieldException {
+
+        String tag = e.getName();
+
+        boolean matchArch = GeneralUtil.prefixMatchArch(
+            GeneralUtil.getStringList(arch));
+
+
+        if (matchArch && (tag.equals("jar") || tag.equals("nativelib"))) {
+            /*
+             * jar/nativelib elements
+             */
+            URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href");
+            String version = XMLUtils.getAttribute(e, "", "version", null);
+
+            String mainStr = XMLUtils.getAttribute(e, "", "main");
+            boolean isNativeLib = tag.equals("nativelib");
+
+            boolean isMain = "true".equalsIgnoreCase(mainStr);
+
+            JARDesc jd = new JARDesc(href, version, isMain, isNativeLib, rdesc);
+            rdesc.addResource(jd);
+        } else if (matchArch && tag.equals("property")) {
+            /*
+             *  property tag
+             */
+            String name  = XMLUtils.getRequiredAttribute(source, e, "", "name");
+            String value = XMLUtils.getRequiredAttributeEmptyOK(
+                    source, e, "", "value");
+
+            rdesc.addResource(new PropertyDesc(name, value));
+        } else if (matchArch && tag.equals("extension")) {
+            URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href");
+            String version = XMLUtils.getAttribute(e, "", "version", null);
+            rdesc.addResource(new ExtensionDesc(href, version));
+        } else if ((tag.equals("java") || tag.equals("j2se")) && !ignoreJres) {
+            /*
+             * j2se element
+             */
+            String version  =
+                XMLUtils.getRequiredAttribute(source, e, "", "version");
+            String minheapstr =
+                XMLUtils.getAttribute(e, "", "initial-heap-size");
+            String maxheapstr =
+                XMLUtils.getAttribute(e, "", "max-heap-size");
+
+            String vmargs =
+                XMLUtils.getAttribute(e, "", "java-vm-args");
+
+            if (jnlpd.isJRESet()) {
+                if (vmargs == null) {
+                    vmargs = "none";
+                }
+                Log.warning("Ignoring repeated element <" + tag + "> with version " + version +
+                        " and java-vm-args: " + vmargs);
+                return;
+            }
+
+            long minheap = GeneralUtil.heapValToLong(minheapstr);
+            long maxheap = GeneralUtil.heapValToLong(maxheapstr);
+
+            ResourcesDesc cbs = null;
+            buildResourcesDesc(source, codebase, e, true, null);
+
+            // JRE
+            JREDesc jreDesc = new JREDesc(
+                version,
+                minheap,
+                maxheap,
+                vmargs,
+                cbs,
+                arch);
+
+            rdesc.addResource(jreDesc);
+
+            jnlpd.setIsJRESet(true);
+        }
+    }
+
+    /** Extract data from the application-desc tag */
+    private static void buildApplicationDesc(final String source,
+        XMLNode root, JNLPDesc jnlpd) throws MissingFieldException, BadFieldException {
+
+        String mainclass = XMLUtils.getClassName(source, root,
+                           "<application-desc>", "main-class", false);
+        String appType = XMLUtils.getAttribute(root, "<application-desc>",
+                                               "type", "Java");
+        String progressclass  = XMLUtils.getClassName(source, root,
+                                "<application-desc>", "progress-class", false);
+        if (progressclass != null && !progressclass.isEmpty()) {
+            Log.warning("JNLPConverter does not support progress indication. \"" + progressclass + "\" will not be loaded and will be ignored.");
+        }
+
+        if (!("Java".equalsIgnoreCase(appType) ||
+            "JavaFx".equalsIgnoreCase(appType))) {
+            throw new BadFieldException(source, XMLUtils.getPathString(root) +
+                "<application-desc>type", appType);
+        }
+
+        if ("JavaFx".equalsIgnoreCase(appType)) {
+            jnlpd.setIsFXApp(true);
+        }
+
+        XMLUtils.visitElements(root, "<application-desc><argument>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException {
+                String arg = XMLUtils.getElementContents(e, "", null);
+                if (arg == null) {
+                    throw new BadFieldException(source, XMLUtils.getPathString(e), "");
+                }
+                jnlpd.addArguments(arg);
+            }
+        });
+
+        XMLUtils.visitElements(root, "<application-desc><param>",
+            new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException,
+                BadFieldException {
+                String pn = XMLUtils.getRequiredAttribute(
+                            source, e, "", "name");
+                String pv = XMLUtils.getRequiredAttributeEmptyOK(
+                            source, e, "", "value");
+                jnlpd.setProperty(pn, pv);
+            }
+        });
+        jnlpd.setMainClass(mainclass, false);
+    }
+
+    /** Extract data from the javafx-desc tag */
+    private static void buildFXAppDesc(final String source,
+        XMLNode root, String element, JNLPDesc jnlpd)
+             throws MissingFieldException, BadFieldException {
+        String mainclass = XMLUtils.getClassName(source, root, element,
+                                                 "main-class", true);
+        String name = XMLUtils.getRequiredAttribute(source, root,
+                                        "<javafx-desc>", "name");
+
+        /* extract arguments */
+        XMLUtils.visitElements(root, "<javafx-desc><argument>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException {
+                String arg = XMLUtils.getElementContents(e, "", null);
+                if (arg == null) {
+                    throw new BadFieldException(source, XMLUtils.getPathString(e), "");
+                }
+                jnlpd.addArguments(arg);
+            }
+        });
+
+        /* extract parameters */
+        XMLUtils.visitElements(root, "<javafx-desc><param>",
+            new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException,
+                BadFieldException {
+                String pn = XMLUtils.getRequiredAttribute(
+                            source, e, "", "name");
+                String pv = XMLUtils.getRequiredAttributeEmptyOK(
+                            source, e, "", "value");
+                jnlpd.setProperty(pn, pv);
+            }
+        });
+
+        jnlpd.setMainClass(mainclass, true);
+        jnlpd.setName(name);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import jnlp.converter.parser.exception.BadFieldException;
+import jnlp.converter.parser.exception.MissingFieldException;
+import jnlp.converter.parser.xml.XMLNode;
+
+/** Contains handy methods for looking up information
+ *  stored in XMLNodes.
+ */
+public class XMLUtils {
+
+    /** Returns the value of an integer attribute */
+    public static int getIntAttribute(String source, XMLNode root, String path, String name, int defaultvalue)
+            throws BadFieldException {
+        String value = getAttribute(root, path, name);
+        if (value == null) {
+            return defaultvalue;
+        }
+
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException nfe) {
+            throw new BadFieldException(source, getPathString(root) + path + name, value);
+        }
+    }
+
+    /** Returns the value of a given attribute, or null if not set */
+    public static String getAttribute(XMLNode root, String path, String name)
+                                                      throws BadFieldException {
+        return getAttribute(root, path, name, null);
+    }
+
+    /** Returns the value of a given attribute */
+    public static String getRequiredAttributeEmptyOK(String source,
+        XMLNode root, String path, String name) throws MissingFieldException {
+        String value = null;
+        XMLNode elem = findElementPath(root, path);
+        if (elem != null) {
+            value = elem.getAttribute(name);
+        }
+        if (value == null) {
+            throw new MissingFieldException(source,
+                                            getPathString(root)+ path + name);
+        }
+        return value;
+    }
+
+    /*** Returns the value of an attribute, which must be a valid class name */
+    public static String getClassName(String source, XMLNode root,
+        String path, String name, boolean required)
+            throws BadFieldException, MissingFieldException {
+
+        String className;
+        if (required) {
+            className = getRequiredAttribute(source, root, path, name);
+        } else {
+            className = getAttribute(root, path, name);
+        }
+        if (className != null && className.endsWith(".class")) {
+            int i = className.lastIndexOf(".class");
+            String cname = className.substring(0, i);
+            return cname;
+        }
+        return className;
+    }
+
+    /** Returns the value of a given attribute, or null if not set */
+    public static String getRequiredAttribute(String source, XMLNode root,
+            String path, String name) throws MissingFieldException, BadFieldException {
+        String s = getAttribute(root, path, name, null);
+        if (s == null) {
+            throw new MissingFieldException(source, getPathString(root) + path + name);
+        }
+        s = s.trim();
+        return (s.length() == 0) ? null : s;
+    }
+
+    /** Returns the value of a given attribute, or the default value 'def' if not set */
+    public static String getAttribute(XMLNode root, String path, String name,
+            String def) throws BadFieldException {
+        XMLNode elem = findElementPath(root, path);
+        if (elem == null) {
+            return def;
+        }
+        String value = elem.getAttribute(name);
+        return (value == null || value.length() == 0) ? def : value;
+    }
+
+    /** Expands a URL into an absolute URL from a relative URL */
+    public static URL getAttributeURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException {
+        String value = getAttribute(root, path, name);
+        if (value == null) return null;
+        try {
+            if (value.startsWith("jar:")) {
+                int bang = value.indexOf("!/");
+                if (bang > 0) {
+                    String entry = value.substring(bang);
+                    String urlString = value.substring(4, bang);
+                    URL url = (base == null) ?
+                        new URL(urlString) : new URL(base, urlString);
+                    return new URL("jar:" + url.toString() + entry);
+                }
+            }
+            return (base == null) ? new URL(value) : new URL(base, value);
+        } catch(MalformedURLException mue) {
+            if (mue.getMessage().contains("https")) {
+                throw new BadFieldException(source, "<jnlp>", "https");
+            }
+            throw new BadFieldException(source, getPathString(root) + path + name, value);
+        }
+    }
+
+    /** Returns the value of an attribute as a URL or null if not set */
+    public static URL getAttributeURL(String source, XMLNode root, String path, String name) throws BadFieldException {
+        return getAttributeURL(source, null, root, path, name);
+    }
+
+    public static URL getRequiredURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException {
+        URL url = getAttributeURL(source, base, root, path, name);
+        if (url == null) {
+            throw new MissingFieldException(source, getPathString(root) + path + name);
+        }
+        return url;
+    }
+
+    /** Returns the value of an attribute as a URL. Throws a MissingFieldException if the
+     *  attribute is not defined
+     */
+    public static URL getRequiredURL(String source, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException {
+        return getRequiredURL(source, null, root, path, name);
+    }
+
+    /** Returns true if the path exists in the document, otherwise false */
+    public static boolean isElementPath(XMLNode root, String path) {
+        return findElementPath(root, path) != null;
+    }
+
+    public static URL getElementURL(String source, XMLNode root, String path) throws BadFieldException {
+        String value = getElementContents(root, path);
+        try {
+            return new URL(value);
+        } catch(MalformedURLException mue) {
+            throw new BadFieldException(source, getPathString(root) + path, value);
+        }
+    }
+
+    /** Returns a string describing the current location in the DOM */
+    public static String getPathString(XMLNode e) {
+        return (e == null || !(e.isElement())) ? "" : getPathString(e.getParent()) + "<" + e.getName() + ">";
+    }
+
+    /** Returns the contents of an element with the given path and an attribute matching a specific value. Returns
+     *  NULL if not found
+     */
+    public static String getElementContentsWithAttribute(XMLNode root, String path, String attr, String val, String defaultvalue)
+            throws BadFieldException, MissingFieldException {
+        XMLNode e = getElementWithAttribute(root, path, attr, val);
+        if (e == null) {
+            return defaultvalue;
+        }
+        return getElementContents(e, "", defaultvalue);
+    }
+
+    public static URL getAttributeURLWithAttribute(String source, XMLNode root, String path, String attrcond, String val,
+            String name, URL defaultvalue)
+            throws BadFieldException, MissingFieldException {
+        XMLNode e = getElementWithAttribute(root, path, attrcond, val);
+        if (e == null) {
+            return defaultvalue;
+        }
+        URL url = getAttributeURL(source, e, "", name);
+        if (url == null) {
+            return defaultvalue;
+        }
+        return url;
+    }
+
+    /** Returns an element with the given path and an attribute matching a specific value. Returns
+     *  NULL if not found
+     */
+    public static XMLNode getElementWithAttribute(XMLNode root, String path, final String attr, final String val)
+            throws BadFieldException, MissingFieldException {
+        final XMLNode[] result = {null};
+        visitElements(root, path, new ElementVisitor() {
+            public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException {
+                if (result[0] == null && e.getAttribute(attr).equals(val)) {
+                    result[0] = e;
+                }
+            }
+        });
+        return result[0];
+    }
+
+    /** Like getElementContents(...) but with a defaultValue of null */
+    public static String getElementContents(XMLNode root, String path) {
+        return getElementContents(root, path, null);
+    }
+
+    /** Returns the value of the last element tag in the path, e.g.,  <..><tag>value</tag>. The DOM is assumes
+     *  to be normalized. If no value is found, the defaultvalue is returned
+     */
+    public static String getElementContents(XMLNode root, String path, String defaultvalue) {
+        XMLNode e = findElementPath(root, path);
+        if (e == null) {
+            return defaultvalue;
+        }
+        XMLNode n = e.getNested();
+        if (n != null && !n.isElement()) {
+            return n.getName();
+        }
+        return defaultvalue;
+    }
+
+    /** Parses a path string of the form <tag1><tag2><tag3> and returns the specific Element
+     *  node for that tag, or null if it does not exist. If multiple elements exists with same
+     *  path the first is returned
+     */
+    public static XMLNode findElementPath(XMLNode elem, String path) {
+        // End condition. Root null -> path does not exist
+        if (elem == null) {
+            return null;
+        }
+
+        // End condition. String empty, return current root
+        if (path == null || path.length() == 0) {
+            return elem;
+        }
+
+        // Strip of first tag
+        int idx = path.indexOf('>');
+        if (!(path.charAt(0) == '<')) {
+            throw new IllegalArgumentException("bad path. Missing begin tag");
+        }
+        if (idx == -1) {
+            throw new IllegalArgumentException("bad path. Missing end tag");
+        }
+        String head = path.substring(1, idx);
+        String tail = path.substring(idx + 1);
+        return findElementPath(findChildElement(elem, head), tail);
+    }
+
+    /** Returns an child element with the current tag name or null. */
+    public static XMLNode findChildElement(XMLNode elem, String tag) {
+        XMLNode n = elem.getNested();
+        while (n != null) {
+            if (n.isElement() && n.getName().equals(tag)) {
+                return n;
+            }
+            n = n.getNext();
+        }
+        return null;
+    }
+
+    /** Iterator class */
+    public abstract static class ElementVisitor {
+        abstract public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException;
+    }
+
+    /** Visits all elements which matches the <path>. The iteration is only
+     *  done on the last element in the path.
+     */
+    public static void visitElements(XMLNode root, String path, ElementVisitor ev)
+            throws BadFieldException, MissingFieldException {
+        // Get last element in path
+        int idx = path.lastIndexOf('<');
+        if (idx == -1) {
+            throw new IllegalArgumentException(
+                    "bad path. Must contain atleast one tag");
+        }
+        if (path.length() == 0 || path.charAt(path.length() - 1) != '>') {
+            throw new IllegalArgumentException("bad path. Must end with a >");
+        }
+        String head = path.substring(0, idx);
+        String tag = path.substring(idx + 1, path.length() - 1);
+
+        XMLNode elem = findElementPath(root, head);
+        if (elem == null) {
+            return;
+        }
+
+        // Iterate through all child nodes
+        XMLNode n = elem.getNested();
+        while (n != null) {
+            if (n.isElement() && n.getName().equals(tag)) {
+                ev.visitElement(n);
+            }
+            n = n.getNext();
+        }
+    }
+
+    public static void visitChildrenElements(XMLNode elem, ElementVisitor ev)
+            throws BadFieldException, MissingFieldException {
+        // Iterate through all child nodes
+        XMLNode n = elem.getNested();
+        while (n != null) {
+            if (n.isElement()) {
+                ev.visitElement(n);
+            }
+            n = n.getNext();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.exception;
+
+public class BadFieldException extends Exception {
+
+    private final String _field;
+    private final String _value;
+
+    public BadFieldException(String source, String field, String value) {
+        super();
+        _value = value;
+        _field = field;
+    }
+
+    /**
+     * Returns the name of the offending field
+     */
+    public String getField() {
+        return _field;
+    }
+
+    /**
+     * Returns the value of the offending field
+     */
+    public String getValue() {
+        return _value;
+    }
+
+    /**
+     * toString implementation
+     */
+    @Override
+    public String toString() {
+        return "BadFieldException[ " + getField() + "," + getValue() + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.exception;
+
+/**
+ * Exception thrown if a parse error occurred when interpreting
+ * the launch descriptor
+ */
+
+public class JNLParseException extends Exception {
+
+    private final String _msg;
+    private final int _line;
+
+    public JNLParseException(Exception exception, String msg, int line) {
+        super(exception);
+        _msg = msg;
+        _line = line;
+    }
+
+    @Override
+    public String getMessage() {
+        return _msg;
+    }
+
+    private int getLine() {
+        return _line;
+    }
+
+    @Override
+    public String toString() {
+        return "JNLParseException[ " + getMessage() + "] at " + getLine();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.exception;
+
+public class MissingFieldException extends Exception {
+
+    private final String _field;
+
+    public MissingFieldException(String source, String field) {
+        super();
+        _field = field;
+    }
+
+    /**
+     * Returns the name of the offending field
+     */
+    private String getField() {
+        return _field;
+    }
+
+    /**
+     * toString implementation
+     */
+    @Override
+    public String toString() {
+        return "MissingFieldException[ " + getField() + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+/**
+ * Class that contains information about a specific attribute
+ */
+public class XMLAttribute {
+
+    private final String _name;
+    private final String _value;
+    private XMLAttribute _next;
+
+    public XMLAttribute(String name, String value) {
+        _name = XMLNode.stripNameSpace(name);
+        _value = value;
+    }
+
+    public String getName() {
+        return _name;
+    }
+
+    public String getValue() {
+        return _value;
+    }
+
+    public XMLAttribute getNext() {
+        return _next;
+    }
+
+    public void setNext(XMLAttribute next) {
+        _next = next;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+public class XMLEncoding {
+    /**
+     * Decodes a byte stream into a String by testing for a Byte Order Mark
+     * (BOM) or an XML declaration.
+     * <br />
+     * Detection begins by examining the first four octets of the stream for a
+     * BOM. If a BOM is not found, then an encoding declaration is looked for
+     * at the beginning of the stream. If the encoding still can not be
+     * determined at this point, then UTF-8 is assumed.
+     *
+     * @param data  an array of bytes containing an encoded XML document.
+     *
+     * @return A string containing the decoded XML document.
+     */
+    public static String decodeXML(byte [] data) throws IOException {
+        int start = 0;
+        String encoding;
+
+        if (data.length < BOM_LENGTH) {
+            throw (new EOFException("encoding.error.not.xml"));
+        }
+        // no else required; successfully read stream
+        int firstFour = ((0xff000000 & ((int) data[0] << 24)) |
+                         (0x00ff0000 & ((int) data[1] << 16)) |
+                         (0x0000ff00 & ((int) data[2] <<  8)) |
+                         (0x000000ff &  (int) data[3]));
+
+        // start by examining the first four bytes for a BOM
+        switch (firstFour) {
+            case EBCDIC:
+                // examine the encoding declaration
+                encoding = examineEncodingDeclaration(data, IBM037_ENC);
+                break;
+
+            case XML_DECLARATION:
+                // assume UTF-8, but examine the encoding declaration
+                encoding = examineEncodingDeclaration(data, UTF_8_ENC);
+                break;
+
+            case UTF_16BE:
+                encoding = UTF_16BE_ENC;
+                break;
+
+            case UTF_16LE:
+                encoding = UTF_16LE_ENC;
+                break;
+
+            case UNUSUAL_OCTET_1:
+            case UNUSUAL_OCTET_2:
+                throw (new UnsupportedEncodingException("encoding.error.unusual.octet"));
+
+            case UTF_32_BE_BOM:
+            case UTF_32_LE_BOM:
+                encoding = UTF_32_ENC;
+                break;
+
+            default:
+                int firstThree = firstFour & 0xffffff00;
+
+                switch (firstThree) {
+                    case UTF_8_BOM:
+                        // the InputStreamReader class doen't properly handle
+                        // the Byte Order Mark (BOM) in UTF-8 streams, so don't
+                        // putback those 3 bytes.
+                        start    = 3;
+                        encoding = UTF_8_ENC;
+                        break;
+
+                    default:
+                        int firstTwo = firstFour & 0xffff0000;
+
+                        switch (firstTwo) {
+                            case UTF_16_BE_BOM:
+                            case UTF_16_LE_BOM:
+                                encoding = UTF_16_ENC;
+                                break;
+
+                            default:
+                                // this is probably UTF-8 without the encoding
+                                // declaration
+                                encoding = UTF_8_ENC;
+                                break;
+                        }
+                        break;
+                }
+                break;
+        }
+
+        return (new String(data, start, data.length - start, encoding));
+    }
+
+    /**
+     * [3]  S            ::= ( #x20 | #x09 | #x0d | #x0a )
+     * [23] XMLDecl      ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
+     * [24] VersionInfo  ::= S 'version' Eq ( '"' VersionNum '"' |
+     *                                        "'" VersionNum "'" )
+     * [25] Eq           ::= S? '=' S?
+     * [26] VersionNum   ::= ([a-zA-Z0-9_.:] | '-')+
+     * [80] EncodingDecl ::= S 'encoding' Eq ( '"' EncName '"' |
+     *                                         "'" EncName "'" )
+     * [81] EncName      ::= [a-zA-Z] ([a-zA-Z0-9_.] | '-')*
+     */
+    private static String examineEncodingDeclaration(byte [] data,
+                          String    encoding) throws IOException {
+        boolean loop       = false;
+        boolean recognized = false;
+        boolean almost     = false;
+        boolean question   = false;
+        boolean done       = false;
+        boolean found      = false;
+        int     pos        = 0;
+        int     ch         = -1;
+        Reader  reader     = null;
+        String  result     = ((encoding != null) ? encoding : UTF_8_ENC);
+
+        reader = new InputStreamReader(new ByteArrayInputStream(data), result);
+        ch     = reader.read();
+
+        // if this is an XML declaration, it will start with the text '<?xml'
+        for (int i = 0; ((i < XML_DECL_START.length()) && (done == false)); i++) {
+            if (ch != XML_DECL_START.charAt(i)) {
+                // This doesn't look like an XML declaration.  This method
+                // should only be called if the stream contains an XML
+                // declaration in the encoding that is passed into the method.
+                done = true;
+                break;
+            }
+            // no else required; still matches
+            ch = reader.read();
+        }
+
+        // there must be at least one whitespace character next.
+        loop = true;
+        while ((loop == true) && (done == false)) {
+            switch (ch) {
+                case SPACE:
+                case TAB:         // intentional
+                case LINEFEED:    // fall
+                case RETURN:      // through
+                    ch = reader.read();
+                    break;
+
+                case -1:
+                    // unexpected EOF
+                    done = true;
+                    break;
+
+                default:
+                    // non-whitespace
+                    loop = false;
+                    break;
+            }
+        }
+
+        // now look for the text 'encoding', but if the end of the XML
+        // declaration (signified by the text '?>') comes first, then
+        // assume the encoding is UTF-8
+        loop = true;
+        while ((loop == true) && (done == false)) {
+            if (ch == -1) {
+                // unexpected EOF
+                done = true;
+                break;
+            } else if (recognized == true) {
+                // this is the encoding declaration as long as the next few
+                // characters are whitespace and/or the equals ('=') sign
+                switch (ch) {
+                    case SPACE:       // intentional
+                    case TAB:         // fall
+                    case LINEFEED:    // through
+                    case RETURN:
+                        // don't need to do anything
+                        break;
+
+                    case EQUAL:
+                        if (almost == false) {
+                            // got the equal, now find a quote
+                            almost = true;
+                        } else {
+                            // this is not valid XML, so punt
+                            recognized = false;
+                            done       = true;
+                        }
+                        break;
+
+                    case DOUBLE_QUOTE:    // intentional
+                    case SINGLE_QUOTE:    // fall through
+                        if (almost == true) {
+                            // got the quote, so move on to get the value
+                            loop = false;
+                        } else {
+                            // got a quote before the equal; this is not valid
+                            // XML, so punt
+                            recognized = false;
+                            done       = true;
+                        }
+                        break;
+
+                    default:
+                        // non-whitespace
+                        recognized = false;
+                        if (almost == true) {
+                            // this is not valid XML, so punt
+                            done = true;
+                        }
+                        // no else required; this wasn't the encoding
+                        // declaration
+                        break;
+                }
+
+                if (recognized == false) {
+                    // this isn't the encoding declaration, so go back to the
+                    // top without reading the next character
+                    pos = 0;
+                    continue;
+                }
+                // no else required; still looking good
+            } else if (ch == ENCODING_DECL.charAt(pos++)) {
+                if (ENCODING_DECL.length() == pos) {
+                    // this looks like the encoding declaration
+                    recognized = true;
+                }
+                // no else required; this might be the encoding declaration
+            } else if (ch == '?') {
+                question = true;
+                pos      = 0;
+            } else if ((ch == '>') && (question == true)) {
+                // there is no encoding declaration, so assume that the initial
+                // encoding guess was correct
+                done   = true;
+                continue;
+            } else {
+                // still searching for the encoding declaration
+                pos = 0;
+            }
+
+            ch = reader.read();
+        }
+
+        if (done == false) {
+            StringBuilder buffer = new StringBuilder(MAX_ENC_NAME);
+
+            if (((ch >= 'a') && (ch <= 'z')) |
+                ((ch >= 'A') && (ch <= 'Z'))) {
+                // add the character to the result
+                buffer.append((char) ch);
+
+                loop = true;
+                while ((loop == true) && (done == false)) {
+                    ch = reader.read();
+
+                    if (((ch >= 'a') && (ch <= 'z')) ||
+                        ((ch >= 'A') && (ch <= 'Z')) ||
+                        ((ch >= '0') && (ch <= '9')) ||
+                        (ch == '_') || (ch == '.') || (ch == '-')) {
+                        // add the character to the result
+                        buffer.append((char) ch);
+                    } else if ((ch == DOUBLE_QUOTE) || (ch == SINGLE_QUOTE)) {
+                        // finished!
+                        found  = true;
+                        done   = true;
+                        result = buffer.toString();
+                    } else {
+                        // this is not a valid encoding name, so punt
+                        done = true;
+                    }
+                }
+            } else {
+                // this is not a valid encoding name, so punt
+                done = true;
+            }
+        }
+        // no else required; already failed to find the encoding somewhere else
+
+        return (result);
+    }
+
+    private static final int BOM_LENGTH   = 4;
+    private static final int MAX_ENC_NAME = 512;
+
+    private static final int SPACE        = 0x00000020;
+    private static final int TAB          = 0x00000009;
+    private static final int LINEFEED     = 0x0000000a;
+    private static final int RETURN       = 0x0000000d;
+    private static final int EQUAL        = '=';
+    private static final int DOUBLE_QUOTE = '\"';
+    private static final int SINGLE_QUOTE = '\'';
+
+    private static final int UTF_32_BE_BOM   = 0x0000feff;
+    private static final int UTF_32_LE_BOM   = 0xfffe0000;
+    private static final int UTF_16_BE_BOM   = 0xfeff0000;
+    private static final int UTF_16_LE_BOM   = 0xfffe0000;
+    private static final int UTF_8_BOM       = 0xefbbbf00;
+    private static final int UNUSUAL_OCTET_1 = 0x00003c00;
+    private static final int UNUSUAL_OCTET_2 = 0x003c0000;
+    private static final int UTF_16BE        = 0x003c003f;
+    private static final int UTF_16LE        = 0x3c003f00;
+    private static final int EBCDIC          = 0x4c6fa794;
+    private static final int XML_DECLARATION = 0x3c3f786d;
+
+    private static final String UTF_32_ENC   = "UTF-32";
+    private static final String UTF_16_ENC   = "UTF-16";
+    private static final String UTF_16BE_ENC = "UTF-16BE";
+    private static final String UTF_16LE_ENC = "UTF-16LE";
+    private static final String UTF_8_ENC    = "UTF-8";
+    private static final String IBM037_ENC   = "IBM037";
+
+    private static final String XML_DECL_START = "<?xml";
+    private static final String ENCODING_DECL  = "encoding";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Class that contains information about an XML Node
+ */
+public class XMLNode {
+    private final boolean _isElement;     // Element/PCTEXT
+    private final String _name;
+    private final XMLAttribute _attr;
+    private XMLNode _parent;  // Parent Node
+    private XMLNode _nested;  // Nested XML tags
+    private XMLNode _next;    // Following XML tag on the same level
+
+    public final static String WILDCARD = "*";
+
+    /** Creates a PCTEXT node */
+    public XMLNode(String name) {
+        _isElement = false;
+        _name = name;
+        _attr = null;
+        _nested = null;
+        _next = null;
+        _parent = null;
+    }
+
+    /** Creates a ELEMENT node */
+    public XMLNode(String name, XMLAttribute attr) {
+        _isElement = true;
+        _name = stripNameSpace(name);
+        _attr = attr;
+        _nested = null;
+        _next = null;
+        _parent = null;
+    }
+
+    public String getName() {
+        return _name;
+    }
+
+    public XMLAttribute getAttributes() {
+        return _attr;
+    }
+
+    public XMLNode getNested() {
+        return _nested;
+    }
+
+    public XMLNode getNext() {
+        return _next;
+    }
+
+    public boolean isElement() {
+        return _isElement;
+    }
+
+    public void setParent(XMLNode parent) {
+        _parent = parent;
+    }
+
+    public XMLNode getParent() {
+        return _parent;
+    }
+
+    public void setNext(XMLNode next) {
+        _next = next;
+    }
+
+    public void setNested(XMLNode nested) {
+        _nested = nested;
+    }
+
+    public static String stripNameSpace(String name) {
+        if (name != null && !name.startsWith("xmlns:")) {
+            int i = name.lastIndexOf(":");
+            if (i >= 0 && i < name.length()) {
+                return name.substring(i+1);
+            }
+        }
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 83 * hash + (this._name != null ? this._name.hashCode() : 0);
+        hash = 83 * hash + (this._attr != null ? this._attr.hashCode() : 0);
+        hash = 83 * hash + (this._nested != null ? this._nested.hashCode() : 0);
+        hash = 83 * hash + (this._next != null ? this._next.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof XMLNode)) return false;
+        XMLNode other = (XMLNode)o;
+        boolean result =
+            match(_name, other._name) &&
+            match(_attr, other._attr) &&
+            match(_nested, other._nested) &&
+            match(_next, other._next);
+        return result;
+    }
+
+    public String getAttribute(String name) {
+        XMLAttribute cur = _attr;
+        while(cur != null) {
+            if (name.equals(cur.getName())) return cur.getValue();
+            cur = cur.getNext();
+        }
+        return "";
+    }
+
+    private static boolean match(Object o1, Object o2) {
+        if (o1 == null) {
+            return (o2 == null);
+        }
+        return o1.equals(o2);
+    }
+
+    public void printToStream(PrintWriter out) {
+        printToStream(out, false);
+    }
+
+    public void printToStream(PrintWriter out, boolean trim) {
+        printToStream(out, 0, trim);
+    }
+
+    public void printToStream(PrintWriter out, int n, boolean trim) {
+        if (!isElement()) {
+            String value = _name; // value node (where name is data of parent)
+            if (trim && value.length() > 512) {
+                value = "...";
+            }
+            out.print(value);
+        } else {
+            if (_nested == null) {
+                String attrString = (_attr == null) ? "" : (" " + _attr.toString());
+                lineln(out, n, "<" + _name + attrString + "/>");
+            } else {
+                String attrString = (_attr == null) ? "" : (" " + _attr.toString());
+                lineln(out, n, "<" + _name + attrString + ">");
+                _nested.printToStream(out, n + 1, trim);
+                if (_nested.isElement()) {
+                    lineln(out, n, "</" + _name + ">");
+                } else {
+                    out.print("</" + _name + ">");
+                }
+            }
+        }
+        if (_next != null) {
+            _next.printToStream(out, n, trim);
+        }
+    }
+
+    private static void lineln(PrintWriter out, int indent, String s) {
+        out.println("");
+        for(int i = 0; i < indent; i++) {
+            out.print("  ");
+        }
+        out.print(s);
+    }
+
+    @Override
+    public String toString() {
+        return toString(false);
+    }
+
+    public String toString(boolean hideLongElementValue) {
+        StringWriter sw = new StringWriter(1000);
+        PrintWriter pw = new PrintWriter(sw);
+        printToStream(pw, hideLongElementValue);
+        pw.close();
+        return sw.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.StringReader;
+import java.io.IOException;
+import java.util.Stack;
+
+public class XMLParser extends DefaultHandler {
+
+    private XMLNode _root;
+    private final String _source;
+    private Stack<XMLNode> _inProgress;
+    private String _characters;
+
+    // although defined in com.sun.org.apache.xerces.internal.impl.Constants,
+    // we should not be able to access that, so defined here
+    private final static String DTD_DOWNLOAD =
+        "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+    private static final SAXParserFactory SPF = SAXParserFactory.newInstance();
+
+    /*
+     * Construct an <code>XMLParser</code>.
+     *
+     * @param source  - the source text to parse.
+     */
+    public XMLParser(String source) {
+        _source = source.trim();
+    }
+
+    public XMLNode parse() throws SAXException {
+        // normally we parse without validating, but leave option to parse
+        // with validation, possibly controlled by config option.
+        return parse(false);
+    }
+
+    public XMLNode parse(boolean validating) throws SAXException {
+        _root = null;
+        _inProgress = new Stack<>();
+
+        try {
+            InputSource is = new InputSource(new StringReader(_source));
+            SPF.setValidating(validating);
+            // only download dtd file from DOCTYPE if we are doing validation
+            try {
+                SPF.setFeature(DTD_DOWNLOAD, validating);
+            } catch (Exception e) {
+            }
+            SAXParser sp = SPF.newSAXParser();
+            sp.parse(is, this);
+        } catch (ParserConfigurationException | IOException pce) {
+            throw new SAXException(pce);
+        }
+
+        return _root;
+    }
+
+    @Override
+    public void startElement(String uri, String localeName, String qName,
+            Attributes attributes) throws SAXException {
+
+        XMLAttribute first = null;
+        XMLAttribute last = null;
+        int len = attributes.getLength();
+
+        for (int i = 0; i < len; i++) {
+            XMLAttribute att = new XMLAttribute(
+                    // in old implementation attribute names and values were trimmed
+                    attributes.getQName(i).trim(), attributes.getValue(i).trim());
+            if (first == null) {
+                first = att;
+            }
+            if (last != null) {
+                last.setNext(att);
+            }
+            last = att;
+        }
+        _inProgress.push(new XMLNode(qName, first));
+        _characters = null;
+    }
+
+    @Override
+    public void endElement(String uri, String localeName, String elementName)
+            throws SAXException {
+        XMLNode node = _inProgress.pop();
+        // <information>
+        //  <title>Title</title>
+        //  <vendor>Vendor</vendor> "Some whitespaces"
+        // </information>
+        // In example above when we receive end of <information> we will
+        // have _characters set to whitespace and new line and it will be
+        // added as child node to <information>. This will break our cache code
+        // which will think that JNLP file changed on server even if it is not
+        // and thus we might not load properly.
+        //
+        // <application-desc name="HelloWorld" main-class="HelloWorld">
+        //  <argument>  test with whitespaces </argument>
+        // </application-desc>
+        // From example above we want to include whitespaces for <argument>.
+        //
+        // <node1>
+        //  <node2>abc</node2>
+        //  xyz (might be whitespaces)
+        // </node1>
+        // In JNLP spec we do not have cases when node have nested nodes as
+        // well as text which is whitespaces only.
+        //
+        // So to fix it lets check if ending node have nested nodes, then do
+        // not add whitespaces only node.
+        if (node != null && node.getNested() != null && _characters != null) {
+            String trimCharacters = _characters.trim();
+            if ((trimCharacters == null) || (trimCharacters.length() == 0)) {
+                _characters = null; // No need to add whitespaces only
+            }
+        }
+        if ((_characters != null) && (_characters.trim().length() > 0)) {
+            addChild(node, new XMLNode(_characters));
+        }
+
+        if (_inProgress.isEmpty()) {
+            _root = node;
+        } else {
+            addChild(_inProgress.peek(), node);
+        }
+        _characters = null;
+    }
+
+    @Override
+    public void ignorableWhitespace(char[] chars, int start, int length)
+            throws SAXException {
+        String s = new String(chars, start, length);
+        _characters = ((_characters == null) ? s : _characters + s);
+    }
+
+    private void addChild(XMLNode parent, XMLNode child) {
+        child.setParent(parent);
+
+        XMLNode sibling = parent.getNested();
+        if (sibling == null) {
+            parent.setNested(child); // set us as only child
+        } else {
+            while (sibling.getNext() != null) {
+                sibling = sibling.getNext();
+            }
+            sibling.setNext(child); // sets us as youngest child
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackager/JNLPConverter/build.xml	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="JNLPConverter" default="default" basedir=".">
+    <description>Builds, tests, and runs the project JNLPConverter.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be
+    used for execution of your tasks. These targets are usually executed
+    before and after some main targets. They are:
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported
+    nbproject/build-impl.xml file.
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are:
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar:                  JAR building
+      run:                      execution of project
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="JNLPConverter-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on
+    the compile target as the regular run target does. Again, for a list of available
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file.
+
+    -->
+    <target name="-post-jar">
+        <copy file="${dist.jar}" todir="../../../jpackager/jnlpconverter"/>
+    </target>
+
+    <target name="-post-clean">
+        <delete file="../../../jpackager/jnlpconverter/JNLPConverter.jar"/>
+    </target>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackager/JNLPConverter/manifest.mf	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/build-impl.xml	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,1403 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+  - initialization
+  - compilation
+  - jar
+  - execution
+  - debugging
+  - javadoc
+  - test compilation
+  - test execution
+  - test debugging
+  - applet
+  - cleanup
+
+        -->
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="JNLPConverter-impl">
+    <fail message="Please build using Ant 1.8.0 or higher.">
+        <condition>
+            <not>
+                <antversion atleast="1.8.0"/>
+            </not>
+        </condition>
+    </fail>
+    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+    <!--
+                ======================
+                INITIALIZATION SECTION
+                ======================
+            -->
+    <target name="-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init" name="-init-private">
+        <property file="nbproject/private/config.properties"/>
+        <property file="nbproject/private/configs/${config}.properties"/>
+        <property file="nbproject/private/private.properties"/>
+    </target>
+    <target depends="-pre-init,-init-private" name="-init-user">
+        <property file="${user.properties.file}"/>
+        <!-- The two properties below are usually overridden -->
+        <!-- by the active platform. Just a fallback. -->
+        <property name="default.javac.source" value="1.6"/>
+        <property name="default.javac.target" value="1.6"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
+        <property file="nbproject/configs/${config}.properties"/>
+        <property file="nbproject/project.properties"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+        <property name="platform.java" value="${java.home}/bin/java"/>
+        <available file="${manifest.file}" property="manifest.available"/>
+        <condition property="splashscreen.available">
+            <and>
+                <not>
+                    <equals arg1="${application.splash}" arg2="" trim="true"/>
+                </not>
+                <available file="${application.splash}"/>
+            </and>
+        </condition>
+        <condition property="main.class.available">
+            <and>
+                <isset property="main.class"/>
+                <not>
+                    <equals arg1="${main.class}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="profile.available">
+            <and>
+                <isset property="javac.profile"/>
+                <length length="0" string="${javac.profile}" when="greater"/>
+                <matches pattern="((1\.[89])|9)(\..*)?" string="${javac.source}"/>
+            </and>
+        </condition>
+        <condition property="do.archive">
+            <or>
+                <not>
+                    <istrue value="${jar.archive.disabled}"/>
+                </not>
+                <istrue value="${not.archive.disabled}"/>
+            </or>
+        </condition>
+        <condition property="do.mkdist">
+            <and>
+                <isset property="do.archive"/>
+                <isset property="libs.CopyLibs.classpath"/>
+                <not>
+                    <istrue value="${mkdist.disabled}"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="do.archive+manifest.available">
+            <and>
+                <isset property="manifest.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="do.archive+main.class.available">
+            <and>
+                <isset property="main.class.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="do.archive+splashscreen.available">
+            <and>
+                <isset property="splashscreen.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="do.archive+profile.available">
+            <and>
+                <isset property="profile.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="have.tests">
+            <or/>
+        </condition>
+        <condition property="have.sources">
+            <or>
+                <available file="${src.src.dir}"/>
+            </or>
+        </condition>
+        <condition property="netbeans.home+have.tests">
+            <and>
+                <isset property="netbeans.home"/>
+                <isset property="have.tests"/>
+            </and>
+        </condition>
+        <condition property="no.javadoc.preview">
+            <and>
+                <isset property="javadoc.preview"/>
+                <isfalse value="${javadoc.preview}"/>
+            </and>
+        </condition>
+        <property name="run.jvmargs" value=""/>
+        <property name="run.jvmargs.ide" value=""/>
+        <property name="javac.compilerargs" value=""/>
+        <property name="work.dir" value="${basedir}"/>
+        <condition property="no.deps">
+            <and>
+                <istrue value="${no.dependencies}"/>
+            </and>
+        </condition>
+        <property name="javac.debug" value="true"/>
+        <property name="javadoc.preview" value="true"/>
+        <property name="application.args" value=""/>
+        <property name="source.encoding" value="${file.encoding}"/>
+        <property name="runtime.encoding" value="${source.encoding}"/>
+        <property name="manifest.encoding" value="${source.encoding}"/>
+        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+            <and>
+                <isset property="javadoc.encoding"/>
+                <not>
+                    <equals arg1="${javadoc.encoding}" arg2=""/>
+                </not>
+            </and>
+        </condition>
+        <property name="javadoc.encoding.used" value="${source.encoding}"/>
+        <property name="includes" value="**"/>
+        <property name="excludes" value=""/>
+        <property name="do.depend" value="false"/>
+        <condition property="do.depend.true">
+            <istrue value="${do.depend}"/>
+        </condition>
+        <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
+        <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
+            <and>
+                <isset property="endorsed.classpath"/>
+                <not>
+                    <equals arg1="${endorsed.classpath}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="" property="javac.profile.cmd.line.arg" value="-profile ${javac.profile}">
+            <isset property="profile.available"/>
+        </condition>
+        <condition else="false" property="jdkBug6558476">
+            <and>
+                <matches pattern="1\.[56]" string="${java.specification.version}"/>
+                <not>
+                    <os family="unix"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="false" property="javac.fork">
+            <or>
+                <istrue value="${jdkBug6558476}"/>
+                <istrue value="${javac.external.vm}"/>
+            </or>
+        </condition>
+        <property name="jar.index" value="false"/>
+        <property name="jar.index.metainf" value="${jar.index}"/>
+        <property name="copylibs.rebase" value="true"/>
+        <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
+        <condition property="junit.available">
+            <or>
+                <available classname="org.junit.Test" classpath="${run.test.classpath}"/>
+                <available classname="junit.framework.Test" classpath="${run.test.classpath}"/>
+            </or>
+        </condition>
+        <condition property="testng.available">
+            <available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/>
+        </condition>
+        <condition property="junit+testng.available">
+            <and>
+                <istrue value="${junit.available}"/>
+                <istrue value="${testng.available}"/>
+            </and>
+        </condition>
+        <condition else="testng" property="testng.mode" value="mixed">
+            <istrue value="${junit+testng.available}"/>
+        </condition>
+        <condition else="" property="testng.debug.mode" value="-mixed">
+            <istrue value="${junit+testng.available}"/>
+        </condition>
+        <property name="java.failonerror" value="true"/>
+    </target>
+    <target name="-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
+        <fail unless="src.src.dir">Must set src.src.dir</fail>
+        <fail unless="build.dir">Must set build.dir</fail>
+        <fail unless="dist.dir">Must set dist.dir</fail>
+        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+        <fail unless="dist.jar">Must set dist.jar</fail>
+    </target>
+    <target name="-init-macrodef-property">
+        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${@{value}}"/>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <attribute default="${javac.processorpath}" name="processorpath"/>
+            <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="${javac.debug}" name="debug"/>
+            <attribute default="${empty.dir}" name="sourcepath"/>
+            <attribute default="${empty.dir}" name="gensrcdir"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property location="${build.dir}/empty" name="empty.dir"/>
+                <mkdir dir="${empty.dir}"/>
+                <mkdir dir="@{apgeneratedsrcdir}"/>
+                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+                    <src>
+                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+                            <include name="*"/>
+                        </dirset>
+                    </src>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <compilerarg line="${javac.profile.cmd.line.arg}"/>
+                    <compilerarg line="${javac.compilerargs}"/>
+                    <compilerarg value="-processorpath"/>
+                    <compilerarg path="@{processorpath}:${empty.dir}"/>
+                    <compilerarg line="${ap.processors.internal}"/>
+                    <compilerarg line="${annotation.processing.processor.options}"/>
+                    <compilerarg value="-s"/>
+                    <compilerarg path="@{apgeneratedsrcdir}"/>
+                    <compilerarg line="${ap.proc.none.internal}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <attribute default="${javac.processorpath}" name="processorpath"/>
+            <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="${javac.debug}" name="debug"/>
+            <attribute default="${empty.dir}" name="sourcepath"/>
+            <attribute default="${empty.dir}" name="gensrcdir"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property location="${build.dir}/empty" name="empty.dir"/>
+                <mkdir dir="${empty.dir}"/>
+                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+                    <src>
+                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+                            <include name="*"/>
+                        </dirset>
+                    </src>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <compilerarg line="${javac.profile.cmd.line.arg}"/>
+                    <compilerarg line="${javac.compilerargs}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
+        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <sequential>
+                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </depend>
+            </sequential>
+        </macrodef>
+        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <sequential>
+                <fail unless="javac.includes">Must set javac.includes</fail>
+                <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
+                    <path>
+                        <filelist dir="@{destdir}" files="${javac.includes}"/>
+                    </path>
+                    <globmapper from="*.java" to="*.class"/>
+                </pathconvert>
+                <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
+                <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
+                <delete>
+                    <files includesfile="${javac.includesfile.binary}"/>
+                </delete>
+                <delete>
+                    <fileset file="${javac.includesfile.binary}"/>
+                </delete>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${junit.available}" name="-init-macrodef-junit-init">
+        <condition else="false" property="nb.junit.batch" value="true">
+            <and>
+                <istrue value="${junit.available}"/>
+                <not>
+                    <isset property="test.method"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="false" property="nb.junit.single" value="true">
+            <and>
+                <istrue value="${junit.available}"/>
+                <isset property="test.method"/>
+            </and>
+        </condition>
+    </target>
+    <target name="-init-test-properties">
+        <property name="test.binaryincludes" value="&lt;nothing&gt;"/>
+        <property name="test.binarytestincludes" value=""/>
+        <property name="test.binaryexcludes" value=""/>
+    </target>
+    <target if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <batchtest todir="${build.test.results.dir}">
+                        <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
+                            <filename name="${test.binarytestincludes}"/>
+                        </fileset>
+                    </batchtest>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/>
+    <target if="${testng.available}" name="-init-macrodef-testng">
+        <macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}">
+                    <isset property="test.method"/>
+                </condition>
+                <union id="test.set"/>
+                <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/>
+                <testng classfilesetref="test.set" failureProperty="tests.failed" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="JNLPConverter" testname="TestNG tests" workingDir="${work.dir}">
+                    <xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/>
+                    <propertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </propertyset>
+                    <customize/>
+                </testng>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-test-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <echo>No tests executed.</echo>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2seproject3:junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2seproject3:testng excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2seproject3:testng>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test">
+        <macrodef name="test" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <sequential>
+                <j2seproject3:test-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize>
+                        <classpath>
+                            <path path="${run.test.classpath}"/>
+                        </classpath>
+                        <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                        <jvmarg line="${run.jvmargs}"/>
+                        <jvmarg line="${run.jvmargs.ide}"/>
+                    </customize>
+                </j2seproject3:test-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${junit.available}" name="-init-macrodef-junit-debug" unless="${nb.junit.batch}">
+        <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <jvmarg line="${debug-args-line}"/>
+                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-debug-batch">
+        <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <batchtest todir="${build.test.results.dir}">
+                        <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
+                            <filename name="${test.binarytestincludes}"/>
+                        </fileset>
+                    </batchtest>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <jvmarg line="${debug-args-line}"/>
+                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-debug,-init-macrodef-junit-debug-batch" if="${junit.available}" name="-init-macrodef-junit-debug-impl">
+        <macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2seproject3:junit-debug excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2seproject3:junit-debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${testng.available}" name="-init-macrodef-testng-debug">
+        <macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <element name="customize2" optional="true"/>
+            <sequential>
+                <condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}">
+                    <isset property="test.method"/>
+                </condition>
+                <condition else="-suitename JNLPConverter -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}">
+                    <matches pattern=".*\.xml" string="@{testClass}"/>
+                </condition>
+                <delete dir="${build.test.results.dir}" quiet="true"/>
+                <mkdir dir="${build.test.results.dir}"/>
+                <j2seproject3:debug classname="org.testng.TestNG" classpath="${debug.test.classpath}">
+                    <customize>
+                        <customize2/>
+                        <jvmarg value="-ea"/>
+                        <arg line="${testng.debug.mode}"/>
+                        <arg line="-d ${build.test.results.dir}"/>
+                        <arg line="-listener org.testng.reporters.VerboseReporter"/>
+                        <arg line="${testng.cmd.args}"/>
+                    </customize>
+                </j2seproject3:debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl">
+        <macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <element implicit="true" name="customize2" optional="true"/>
+            <sequential>
+                <j2seproject3:testng-debug testClass="@{testClass}" testMethod="@{testMethod}">
+                    <customize2/>
+                </j2seproject3:testng-debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit">
+        <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <sequential>
+                <j2seproject3:test-debug-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize>
+                        <classpath>
+                            <path path="${run.test.classpath}"/>
+                        </classpath>
+                        <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                        <jvmarg line="${run.jvmargs}"/>
+                        <jvmarg line="${run.jvmargs.ide}"/>
+                    </customize>
+                </j2seproject3:test-debug-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng">
+        <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <sequential>
+                <j2seproject3:testng-debug-impl testClass="@{testClass}" testMethod="@{testMethod}">
+                    <customize2>
+                        <syspropertyset>
+                            <propertyref prefix="test-sys-prop."/>
+                            <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                        </syspropertyset>
+                    </customize2>
+                </j2seproject3:testng-debug-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/>
+    <!--
+                pre NB7.2 profiling section; consider it deprecated
+            -->
+    <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/>
+    <target if="profiler.info.jvmargs.agent" name="-profile-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="profiler.info.jvmargs.agent" name="-profile-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile">
+        <macrodef name="resolve">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${env.@{value}}"/>
+            </sequential>
+        </macrodef>
+        <macrodef name="profile">
+            <attribute default="${main.class}" name="classname"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property environment="env"/>
+                <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
+                <java classname="@{classname}" dir="${profiler.info.dir}" failonerror="${java.failonerror}" fork="true" jvm="${profiler.info.jvm}">
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg value="${profiler.info.jvmargs.agent}"/>
+                    <jvmarg line="${profiler.info.jvmargs}"/>
+                    <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+                    <arg line="${application.args}"/>
+                    <classpath>
+                        <path path="${run.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check">
+        <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
+        <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
+    </target>
+    <!--
+                end of pre NB7.2 profiling section
+            -->
+    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
+        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${main.class}" name="name"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <attribute default="" name="stopclassname"/>
+            <sequential>
+                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </nbjpdastart>
+            </sequential>
+        </macrodef>
+        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${build.classes.dir}" name="dir"/>
+            <sequential>
+                <nbjpdareload>
+                    <fileset dir="@{dir}" includes="${fix.classes}">
+                        <include name="${fix.includes}*.class"/>
+                    </fileset>
+                </nbjpdareload>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-debug-args">
+        <property name="version-output" value="java version &quot;${ant.java.version}"/>
+        <condition property="have-jdk-older-than-1.4">
+            <or>
+                <contains string="${version-output}" substring="java version &quot;1.0"/>
+                <contains string="${version-output}" substring="java version &quot;1.1"/>
+                <contains string="${version-output}" substring="java version &quot;1.2"/>
+                <contains string="${version-output}" substring="java version &quot;1.3"/>
+            </or>
+        </condition>
+        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
+            <istrue value="${have-jdk-older-than-1.4}"/>
+        </condition>
+        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
+            <os family="windows"/>
+        </condition>
+        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
+            <isset property="debug.transport"/>
+        </condition>
+    </target>
+    <target depends="-init-debug-args" name="-init-macrodef-debug">
+        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${main.class}" name="classname"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg line="${debug-args-line}"/>
+                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <jvmarg line="${run.jvmargs.ide}"/>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-java">
+        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${main.class}" name="classname"/>
+            <attribute default="${run.classpath}" name="classpath"/>
+            <attribute default="jvm" name="jvm"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <jvmarg line="${run.jvmargs.ide}"/>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-copylibs">
+        <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${manifest.file}" name="manifest"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+                <pathconvert property="run.classpath.without.build.classes.dir">
+                    <path path="${run.classpath}"/>
+                    <map from="${build.classes.dir.resolved}" to=""/>
+                </pathconvert>
+                <pathconvert pathsep=" " property="jar.classpath">
+                    <path path="${run.classpath.without.build.classes.dir}"/>
+                    <chainedmapper>
+                        <flattenmapper/>
+                        <filtermapper>
+                            <replacestring from=" " to="%20"/>
+                        </filtermapper>
+                        <globmapper from="*" to="lib/*"/>
+                    </chainedmapper>
+                </pathconvert>
+                <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
+                <copylibs compress="${jar.compress}" excludeFromCopy="${copylibs.excludes}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" manifestencoding="UTF-8" rebase="${copylibs.rebase}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
+                    <fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
+                    <manifest>
+                        <attribute name="Class-Path" value="${jar.classpath}"/>
+                        <customize/>
+                    </manifest>
+                </copylibs>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-presetdef-jar">
+        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}" manifestencoding="UTF-8">
+                <j2seproject1:fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
+            </jar>
+        </presetdef>
+    </target>
+    <target name="-init-ap-cmdline-properties">
+        <property name="annotation.processing.enabled" value="true"/>
+        <property name="annotation.processing.processors.list" value=""/>
+        <property name="annotation.processing.processor.options" value=""/>
+        <property name="annotation.processing.run.all.processors" value="true"/>
+        <property name="javac.processorpath" value="${javac.classpath}"/>
+        <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
+        <condition property="ap.supported.internal" value="true">
+            <not>
+                <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/>
+            </not>
+        </condition>
+    </target>
+    <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported">
+        <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
+            <isfalse value="${annotation.processing.run.all.processors}"/>
+        </condition>
+        <condition else="" property="ap.proc.none.internal" value="-proc:none">
+            <isfalse value="${annotation.processing.enabled}"/>
+        </condition>
+    </target>
+    <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
+        <property name="ap.cmd.line.internal" value=""/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
+    <!--
+                ===================
+                COMPILATION SECTION
+                ===================
+            -->
+    <target name="-deps-jar-init" unless="built-jar.properties">
+        <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
+        <delete file="${built-jar.properties}" quiet="true"/>
+    </target>
+    <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
+        <echo level="warn" message="Cycle detected: JNLPConverter was already built"/>
+    </target>
+    <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
+        <mkdir dir="${build.dir}"/>
+        <touch file="${built-jar.properties}" verbose="false"/>
+        <property file="${built-jar.properties}" prefix="already.built.jar."/>
+        <antcall target="-warn-already-built-jar"/>
+        <propertyfile file="${built-jar.properties}">
+            <entry key="${basedir}" value=""/>
+        </propertyfile>
+    </target>
+    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
+    <target depends="init" name="-check-automatic-build">
+        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
+    </target>
+    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
+        <antcall target="clean"/>
+    </target>
+    <target depends="init,deps-jar" name="-pre-pre-compile">
+        <mkdir dir="${build.classes.dir}"/>
+    </target>
+    <target name="-pre-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="do.depend.true" name="-compile-depend">
+        <pathconvert property="build.generated.subdirs">
+            <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="*"/>
+            </dirset>
+        </pathconvert>
+        <j2seproject3:depend srcdir="${src.src.dir}:${build.generated.subdirs}"/>
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
+        <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
+        <copy todir="${build.classes.dir}">
+            <fileset dir="${src.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+        </copy>
+    </target>
+    <target if="has.persistence.xml" name="-copy-persistence-xml">
+        <mkdir dir="${build.classes.dir}/META-INF"/>
+        <copy todir="${build.classes.dir}/META-INF">
+            <fileset dir="${meta.inf.dir}" includes="persistence.xml orm.xml"/>
+        </copy>
+    </target>
+    <target name="-post-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+    <target name="-pre-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:force-recompile/>
+        <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.src.dir}"/>
+    </target>
+    <target name="-post-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+    <!--
+                ====================
+                JAR BUILDING SECTION
+                ====================
+            -->
+    <target depends="init" name="-pre-pre-jar">
+        <dirname file="${dist.jar}" property="dist.jar.dir"/>
+        <mkdir dir="${dist.jar.dir}"/>
+    </target>
+    <target name="-pre-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init" if="do.archive" name="-do-jar-create-manifest" unless="manifest.available">
+        <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+        <touch file="${tmp.manifest.file}" verbose="false"/>
+    </target>
+    <target depends="init" if="do.archive+manifest.available" name="-do-jar-copy-manifest">
+        <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+        <copy encoding="${manifest.encoding}" file="${manifest.file}" outputencoding="UTF-8" tofile="${tmp.manifest.file}"/>
+    </target>
+    <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+main.class.available" name="-do-jar-set-mainclass">
+        <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+            <attribute name="Main-Class" value="${main.class}"/>
+        </manifest>
+    </target>
+    <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+profile.available" name="-do-jar-set-profile">
+        <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+            <attribute name="Profile" value="${javac.profile}"/>
+        </manifest>
+    </target>
+    <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-set-splashscreen">
+        <basename file="${application.splash}" property="splashscreen.basename"/>
+        <mkdir dir="${build.classes.dir}/META-INF"/>
+        <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
+        <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+            <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
+        </manifest>
+    </target>
+    <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.mkdist" name="-do-jar-copylibs">
+        <j2seproject3:copylibs manifest="${tmp.manifest.file}"/>
+        <echo level="info">To run this application from the command line without Ant, try:</echo>
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
+        <echo level="info">java -jar "${dist.jar.resolved}"</echo>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.archive" name="-do-jar-jar" unless="do.mkdist">
+        <j2seproject1:jar manifest="${tmp.manifest.file}"/>
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
+        <pathconvert property="run.classpath.with.dist.jar">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+        </pathconvert>
+        <condition else="" property="jar.usage.message" value="To run this application from the command line without Ant, try:${line.separator}${platform.java} -cp ${run.classpath.with.dist.jar} ${main.class}">
+            <isset property="main.class.available"/>
+        </condition>
+        <condition else="debug" property="jar.usage.level" value="info">
+            <isset property="main.class.available"/>
+        </condition>
+        <echo level="${jar.usage.level}" message="${jar.usage.message}"/>
+    </target>
+    <target depends="-do-jar-copylibs" if="do.archive" name="-do-jar-delete-manifest">
+        <delete>
+            <fileset file="${tmp.manifest.file}"/>
+        </delete>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-jar,-do-jar-delete-manifest" name="-do-jar-without-libraries"/>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-copylibs,-do-jar-delete-manifest" name="-do-jar-with-libraries"/>
+    <target name="-post-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-jar,-do-jar-without-libraries,-do-jar-with-libraries,-post-jar" name="-do-jar"/>
+    <target depends="init,compile,-pre-jar,-do-jar,-post-jar" description="Build JAR." name="jar"/>
+    <!--
+                =================
+                EXECUTION SECTION
+                =================
+            -->
+    <target depends="init,compile" description="Run a main class." name="run">
+        <j2seproject1:java>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <target name="-do-not-recompile">
+        <property name="javac.includes.binary" value=""/>
+    </target>
+    <target depends="init,compile-single" name="run-single">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2seproject1:java classname="${run.class}"/>
+    </target>
+    <target depends="init,compile-test-single" name="run-test-with-main">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
+    </target>
+    <!--
+                =================
+                DEBUGGING SECTION
+                =================
+            -->
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
+        <j2seproject1:nbjpdastart name="${debug.class}"/>
+    </target>
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
+        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
+    </target>
+    <target depends="init,compile" name="-debug-start-debuggee">
+        <j2seproject3:debug>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
+        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+    </target>
+    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2seproject3:debug classname="${debug.class}"/>
+    </target>
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+    <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
+    </target>
+    <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
+    <target depends="init" name="-pre-debug-fix">
+        <fail unless="fix.includes">Must set fix.includes</fail>
+        <property name="javac.includes" value="${fix.includes}.java"/>
+    </target>
+    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+        <j2seproject1:nbjpdareload/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+    <!--
+                =================
+                PROFILING SECTION
+                =================
+            -->
+    <!--
+                pre NB7.2 profiler integration
+            -->
+    <target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile/>
+    </target>
+    <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72">
+        <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile classname="${profile.class}"/>
+    </target>
+    <target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </profile>
+    </target>
+    <target depends="profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.test.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <junit dir="${profiler.info.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${profiler.info.jvm}" showoutput="true">
+            <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+            <jvmarg value="${profiler.info.jvmargs.agent}"/>
+            <jvmarg line="${profiler.info.jvmargs}"/>
+            <test name="${profile.class}"/>
+            <classpath>
+                <path path="${run.test.classpath}"/>
+            </classpath>
+            <syspropertyset>
+                <propertyref prefix="test-sys-prop."/>
+                <mapper from="test-sys-prop.*" to="*" type="glob"/>
+            </syspropertyset>
+            <formatter type="brief" usefile="false"/>
+            <formatter type="xml"/>
+        </junit>
+    </target>
+    <!--
+                end of pre NB72 profiling section
+            -->
+    <target if="netbeans.home" name="-profile-check">
+        <condition property="profiler.configured">
+            <or>
+                <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/>
+                <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/>
+            </or>
+        </condition>
+    </target>
+    <target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent">
+        <startprofiler/>
+        <antcall target="run"/>
+    </target>
+    <target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <startprofiler/>
+        <antcall target="run-single"/>
+    </target>
+    <target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/>
+    <target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <startprofiler/>
+        <antcall target="test-single"/>
+    </target>
+    <target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <startprofiler/>
+        <antcall target="run-test-with-main"/>
+    </target>
+    <target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <startprofiler/>
+        <antcall target="run-applet"/>
+    </target>
+    <!--
+                ===============
+                JAVADOC SECTION
+                ===============
+            -->
+    <target depends="init" if="have.sources" name="-javadoc-build">
+        <mkdir dir="${dist.javadoc.dir}"/>
+        <condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}">
+            <and>
+                <isset property="endorsed.classpath.cmd.line.arg"/>
+                <not>
+                    <equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/>
+                </not>
+            </and>
+        </condition>
+        <condition else="" property="bug5101868workaround" value="*.java">
+            <matches pattern="1\.[56](\..*)?" string="${java.version}"/>
+        </condition>
+        <javadoc additionalparam="-J-Dfile.encoding=${file.encoding} ${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+            <classpath>
+                <path path="${javac.classpath}"/>
+            </classpath>
+            <fileset dir="${src.src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
+                <filename name="**/*.java"/>
+            </fileset>
+            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="**/*.java"/>
+                <exclude name="*.java"/>
+            </fileset>
+            <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/>
+        </javadoc>
+        <copy todir="${dist.javadoc.dir}">
+            <fileset dir="${src.src.dir}" excludes="${excludes}" includes="${includes}">
+                <filename name="**/doc-files/**"/>
+            </fileset>
+            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="**/doc-files/**"/>
+            </fileset>
+        </copy>
+    </target>
+    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+    </target>
+    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+    <!--
+                =========================
+                TEST COMPILATION SECTION
+                =========================
+            -->
+    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+        <mkdir dir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-pre-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="do.depend.true" name="-compile-test-depend">
+        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir=""/>
+    </target>
+    <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir=""/>
+        <copy todir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-post-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+    <target name="-pre-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
+        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="" srcdir=""/>
+        <copy todir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-post-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+    <!--
+                =======================
+                TEST EXECUTION SECTION
+                =======================
+            -->
+    <target depends="init" if="have.tests" name="-pre-test-run">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
+        <j2seproject3:test includes="${includes}" testincludes="**/*Test.java"/>
+    </target>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init" if="have.tests" name="test-report"/>
+    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+    <target depends="init" if="have.tests" name="-pre-test-run-single">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <j2seproject3:test excludes="" includes="${test.includes}" testincludes="${test.includes}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method">
+        <fail unless="test.class">Must select some files in the IDE or set test.class</fail>
+        <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+        <j2seproject3:test excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/>
+    <!--
+                =======================
+                TEST DEBUGGING SECTION
+                =======================
+            -->
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+        <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/>
+    </target>
+    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
+    </target>
+    <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+    <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/>
+    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+    <!--
+                =========================
+                APPLET EXECUTION SECTION
+                =========================
+            -->
+    <target depends="init,compile-single" name="run-applet">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject1:java classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <!--
+                =========================
+                APPLET DEBUGGING  SECTION
+                =========================
+            -->
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject3:debug classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
+    <!--
+                ===============
+                CLEANUP SECTION
+                ===============
+            -->
+    <target name="-deps-clean-init" unless="built-clean.properties">
+        <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
+        <delete file="${built-clean.properties}" quiet="true"/>
+    </target>
+    <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
+        <echo level="warn" message="Cycle detected: JNLPConverter was already built"/>
+    </target>
+    <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
+        <mkdir dir="${build.dir}"/>
+        <touch file="${built-clean.properties}" verbose="false"/>
+        <property file="${built-clean.properties}" prefix="already.built.clean."/>
+        <antcall target="-warn-already-built-clean"/>
+        <propertyfile file="${built-clean.properties}">
+            <entry key="${basedir}" value=""/>
+        </propertyfile>
+    </target>
+    <target depends="init" name="-do-clean">
+        <delete dir="${build.dir}"/>
+        <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
+    </target>
+    <target name="-post-clean">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
+    <target name="-check-call-dep">
+        <property file="${call.built.properties}" prefix="already.built."/>
+        <condition property="should.call.dep">
+            <and>
+                <not>
+                    <isset property="already.built.${call.subproject}"/>
+                </not>
+                <available file="${call.script}"/>
+            </and>
+        </condition>
+    </target>
+    <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
+        <ant antfile="${call.script}" inheritall="false" target="${call.target}">
+            <propertyset>
+                <propertyref prefix="transfer."/>
+                <mapper from="transfer.*" to="*" type="glob"/>
+            </propertyset>
+        </ant>
+    </target>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/genfiles.properties	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=9017e41e
+build.xml.script.CRC32=5cf818d6
+build.xml.stylesheet.CRC32=8064a381@1.80.1.48
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=9017e41e
+nbproject/build-impl.xml.script.CRC32=d0290d6d
+nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/project.properties	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,96 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=JNLPConverter
+application.vendor=
+auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+    ${run.classpath}
+debug.test.classpath=\
+    ${run.test.classpath}
+# Files in build.classes.dir which should be excluded from distribution jar
+dist.archive.excludes=
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/JNLPConverter.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.jnlpconverter-src=../../../jpackager/jnlpconverter/src
+includes=**
+jar.archive.disabled=${jnlp.enabled}
+jar.compress=false
+jar.index=${jnlp.enabled}
+javac.classpath=
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.external.vm=true
+javac.processorpath=\
+    ${javac.classpath}
+javac.source=1.8
+javac.target=1.8
+javac.test.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}
+javac.test.processorpath=\
+    ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+jnlp.codebase.type=no.codebase
+jnlp.descriptor=application
+jnlp.enabled=false
+jnlp.mixed.code=default
+jnlp.offline-allowed=false
+jnlp.signed=false
+jnlp.signing=
+jnlp.signing.alias=
+jnlp.signing.keystore=
+main.class=jnlp.converter.Main
+# Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found.
+manifest.custom.application.library.allowable.codebase=
+# Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts.
+manifest.custom.caller.allowable.codebase=
+# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed
+manifest.custom.codebase=
+# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions)
+manifest.custom.permissions=
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project.
+# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
+# To set system properties for unit tests define test-sys-prop.name=value:
+run.jvmargs=
+run.test.classpath=\
+    ${javac.test.classpath}:\
+    ${build.test.classes.dir}
+source.encoding=UTF-8
+src.src.dir=${file.reference.jnlpconverter-src}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackager/JNLPConverter/nbproject/project.xml	Fri Oct 12 19:00:51 2018 -0400
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.java.j2seproject</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+            <name>JNLPConverter</name>
+            <source-roots>
+                <root id="src.src.dir" name="Source Packages"/>
+            </source-roots>
+            <test-roots/>
+        </data>
+    </configuration>
+</project>