src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java
author herrick
Wed, 05 Dec 2018 12:23:37 -0500
branchJDK-8200758-branch
changeset 57059 9bb2a4dc3af7
parent 57039 98d3963b0b7b
child 57091 06bc4bd64599
permissions -rw-r--r--
8214143: Reduce Resource files Reviewed-by: almatvee

/*
 * 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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 jdk.jpackage.internal;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.StandardCopyOption;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.Map;
import java.util.ResourceBundle;

/**
 * AbstractBundler
 *
 * This is the base class all Bundlers extend from.
 * It contains methods and parameters common to all Bundlers.
 * The concrete implementations are in the platform specific Bundlers.
 */
public abstract class AbstractBundler implements Bundler {

    private static final ResourceBundle I18N = ResourceBundle.getBundle(
            "jdk.jpackage.internal.resources.MainResources");

    public static final BundlerParamInfo<File> IMAGES_ROOT =
            new StandardBundlerParam<>(
            I18N.getString("param.images-root.name"),
            I18N.getString("param.images-root.description"),
            "imagesRoot",
            File.class,
            params -> new File(
                StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "images"),
            (s, p) -> null);

    // do not use file separator -
    // we use it for classpath lookup and there / are not platform specific
    public final static String BUNDLER_PREFIX = "package/";

    protected Class<?> baseResourceLoader = null;

    protected void fetchResource(String publicName, String category,
            String defaultName, File result, boolean verbose, File publicRoot)
            throws IOException {
        InputStream is = streamResource(publicName, category,
                defaultName, verbose, publicRoot);
        if (is != null) {
            try {
                Files.copy(is, result.toPath(),
                        StandardCopyOption.REPLACE_EXISTING);
            } finally {
                is.close();
            }
        } else {
            if (verbose) {
                Log.verbose(MessageFormat.format(I18N.getString(
                        "message.using-default-resource"),
                        category == null ? "" : "[" + category + "] ",
                        publicName));
            }
        }
    }

    protected void fetchResource(String publicName, String category,
            File defaultFile, File result, boolean verbose, File publicRoot)
            throws IOException {
        InputStream is = streamResource(publicName, category,
                null, verbose, publicRoot);
        if (is != null) {
            try {
                Files.copy(is, result.toPath());
            } finally {
                is.close();
            }
        } else {
            IOUtils.copyFile(defaultFile, result);
            if (verbose) {
                Log.verbose(MessageFormat.format(I18N.getString(
                        "message.using-custom-resource-from-file"),
                        category == null ? "" : "[" + category + "] ",
                        defaultFile.getAbsoluteFile()));
            }
        }
    }

    private InputStream streamResource(String publicName, String category,
            String defaultName, boolean verbose, File publicRoot)
            throws IOException {
        boolean custom = false;
        InputStream is = null;
        if (publicName != null) {
            if (publicRoot != null) {
                File publicResource = new File(publicRoot, publicName);
                if (publicResource.exists() && publicResource.isFile()) {
                    is = new BufferedInputStream(
                            new FileInputStream(publicResource));
                }
            } else {
                is = baseResourceLoader.getClassLoader().getResourceAsStream(
                        publicName);
            }
            custom = (is != null);
        }
        if (is == null && defaultName != null) {
            is = baseResourceLoader.getResourceAsStream(defaultName);
        }
        if (verbose && is != null) {
            String msg = null;
            if (custom) {
                msg = MessageFormat.format(I18N.getString(
                        "message.using-custom-resource-from-classpath"),
                        category == null ?
                        "" : "[" + category + "] ", publicName);
            } else {
                msg = MessageFormat.format(I18N.getString(
                        "message.using-default-resource-from-classpath"),
                        category == null ?
                        "" : "[" + category + "] ", publicName);
            }
            Log.verbose(msg);
        }
        return is;
    }

    protected String preprocessTextResource(String publicName, String category,
            String defaultName, Map<String, String> pairs,
            boolean verbose, File publicRoot) throws IOException {
        InputStream inp = streamResource(
                publicName, category, defaultName, verbose, publicRoot);
        if (inp == null) {
            throw new RuntimeException(
                    "Jar corrupt? No " + defaultName + " resource!");
        }

        // read fully into memory
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inp.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }

        // substitute
        String result = new String(baos.toByteArray());
        for (Map.Entry<String, String> e : pairs.entrySet()) {
            if (e.getValue() != null) {
                result = result.replace(e.getKey(), e.getValue());
            }
        }
        return result;
    }

    @Override
    public String toString() {
        return getName();
    }

    @Override
    public void cleanup(Map<String, ? super Object> params) {
        try {
            if (!Log.isDebug() && !Log.isVerbose()) {
                IOUtils.deleteRecursive(
                        StandardBundlerParam.BUILD_ROOT.fetchFrom(params));
            }
        } catch (IOException e) {
            Log.debug(e.getMessage());
        }
    }
}