src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java
author kcr
Thu, 10 Jan 2019 11:30:09 -0800
branchJDK-8200758-branch
changeset 57106 ea870b9ce89a
parent 57039 98d3963b0b7b
permissions -rw-r--r--
8216492: Update copyright of all new jpackage fils to 2019 Reviewed-by: herrick

/*
 * Copyright (c) 2006, 2019, 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);
        }
    }
}