/*
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License (the License). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
 * or http://www.netbeans.org/cddl.txt.
 * 
 * When distributing Covered Code, include this CDDL Header Notice in each file
 * and include the License file at http://www.netbeans.org/cddl.txt.
 * If applicable, add the following below the CDDL Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

/** Responsible for actually installing the contents of module JARs into the IDE.
 * While the manager tracks which modules are enabled and their dependencies,
 * the installer actually understands the manifest contents and is able to
 * add layers, sections, etc. to the IDE's runtime.
 * @author Jesse Glick
 */
public abstract class ModuleInstaller {
    
    /** No-op constructor for subclasses. */
    protected ModuleInstaller() {}
    
    /** Called to ensure that a module is valid to be installed, before continuing.
     * Should examine the manifest and check its syntax (may keep cached parse
     * information about that syntax). When this method is called, the module's
     * classloader should be ready for use, but the system classloader will not
     * yet contain it. It may (but need not) try to load
     * mentioned resources from that classloader in order to catch errors early
     * on. For some resources it may be preferable to load them lazily.
     * If the installer indicates the module is invalid, it will not be installed.
     * InvalidException thrown from here must include a reference to the module.
     */
    public abstract void prepare(Module m) throws InvalidException;
    
    /** Called when a module is being uninstalled and runtime information
     * about it is no longer needed.
     * The installer should remove any associated cache entries and try
     * to release anything that might have been loaded with the module's
     * classloader. The classloader may still be valid at this point but
     * better not to use it.
     */
    public abstract void dispose(Module m);
    
    /** Actually load some modules into the IDE.
     * They will all have been prepared already, and their classloaders
     * will be contained in the system classloader.
     * It is expected to handle any resultant exceptions appropriately
     * (e.g. skipping over a module, or skipping over that piece of a module).
     * Where load order is significant, items should be installed in the order
     * supplied: starting with basic modules and ending with dependent modules.
     * Installers may choose to install all resources of a certain type (e.g.
     * layers) from every module, then all of the next type, etc., for purposes
     * of efficiency.
     * Note that "loading" could really mean installing or restoring or upgrading,
     * depending on module history.
     */
    public abstract void load(List modules);
    
    /** Unload some modules from the IDE.
     * Where unload order is significant, items should be uninstalled in the order
     * supplied: starting with dependent modules and ending with basic modules.
     * Module classloaders will still be in the system classloader.
     */
    public abstract void unload(List modules);
    
    /** Ask to shut down the IDE from a set of modules.
     * Will begin with dependent and end with basic modules.
     */
    public abstract boolean closing(List modules);
    
    /** Notify modules the IDE will be shut down.
     * Will begin with dependent and end with basic modules.
     */
    public abstract void close(List modules);
    
    /** Optionally refine the dependencies for a module.
     * For example, an installer might decide to automatically add a dependency
     * on a "stock" library module for all client modules meeting some criterion.
     * The default implementation makes no change.
     * @param m a module to possibly refine dependencies for; overriders must not call
     *          getDependencies on this module nor attempt to directly change it
     *          in any way; overriders may ask for module code name, version, etc.
     * @param dependencies a set of Dependency's; mutable, entries may be added or removed
     *                     during the dynamic scope of this call
     * @since org.netbeans.core/1 1.2
     */
    public void refineDependencies(Module m, Set dependencies) {}
    
    /** Optionally mask package use in a module classloader.
     * For example, an installer might decide that a module may not use a
     * package in the application classpath because it is an unguaranteed
     * implementation package and the module has not explicitly requested
     * to use it.
     * <p>The module system automatically excludes improper access
     * to non-public packages (as declared via <code>OpenIDE-Module-Public-Packages</code>)
     * and cross-module access to the <code>META-INF/</code> directory.
     * <p>The default implementation of this method permits all other
     * package delegation.
     * @param m the module requesting use of a given package
     * @param parent the module which might possibly supply that package, or
     *               null if the possible provider is not a module (i.e. application
     *               classpath)
     * @param pkg the name of the package in use, in the form "org/netbeans/modules/foo/"
     *            (i.e. slash-separated and ending in a slash as well)
     * @since org.netbeans.core/1 1.3
     */
    public boolean shouldDelegateResource(Module m, Module parent, String pkg) {
        return true;
    }
    
    /** Scan a disabled module JAR file for its manifest contents.
     * Subclasses may implement this efficiently, e.g. to use a special cache.
     * <p>The default implementation simply opens the JAR and gets its manifest
     * using the standard JRE calls.
     * <p>Never called for reloadable JARs.
     * @param jar a module JAR to open
     * @return its manifest
     * @throws IOException if the JAR cannot be opened or does not have a manifest at all
     * @since org.netbeans.core/1 1.5
     * @see "#26786"
     */
    public Manifest loadManifest(File jar) throws IOException {
        JarFile jarFile = new JarFile(jar, false);
        try {
            Manifest m = jarFile.getManifest();
            if (m == null) throw new IOException("No manifest found in " + jar); // NOI18N
            return m;
        } finally {
            jarFile.close();
        }
    }
    
    /** Permit a module installer to add extra parent classloaders for a module.
     * Called during enablement of a module.
     * The default implementation does nothing.
     * @param m a module which is about to be enabled
     * @param parents current list of <code>ClassLoader</code> parents; may be mutated (appended to)
     * @since org.netbeans.core/1 > 1.6
     * @see "#27853"
     */
    public void refineClassLoader(Module m, List parents) {
        // do nothing
    }

    /** Optionally adds additional token for the module.
     * @param m the module to add token to 
     * @return null or list of tokens this module provides
     * @since org.netbeans.core/1 > 1.25
     * @see "#46833"
     */
    public String[] refineProvides (Module m) {
        return null;
    }
    
    /** Is this package special in that the package domain cache should be disabled for it?
     * The result must never change between subsequent calls for the same argument.
     * The default implementation always says no.
     * @param pkg a package in the form "org/netbeans/modules/foo/"
     * @return true if this package might be split across modules, otherwise false
     * @since org.netbeans.core/1 > 1.7
     */
    public boolean isSpecialResource(String pkg) {
        return false;
    }

}
