/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation, and/or the GNU Lesser
 * General Public License, version 2.1, also as published by the Free
 * Software Foundation.
 *
 * This program 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 and the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * and the GNU Lesser General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.rhq.core.util.file;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.jar.Attributes.Name;

/**
 * Return the version of the jar file by inspecting the Manifest.
 * The file does not necessarily need to be a jar but rather can be
 * any archive satisfying jar file structure (e.g. an .ear or .war), exploded or not.  
 *
 * @author Jay Shaughnessy
 * @author John Mazzitelli
 */
public class JarContentFileInfo extends ContentFileInfo {

    private final Attributes mainAttributes;

    public JarContentFileInfo(File file) {
        super(file);

        Manifest manifest = getManifest();
        if (manifest != null) {
            this.mainAttributes = manifest.getMainAttributes();
        } else {
            this.mainAttributes = null;
        }
    }

    /**
     * Determines if the content file is a valid jar file (i.e. one that has a manifest).
     * Even if this returns <code>true</code>, you still are not guaranteed the jar
     * file has a non-<code>null</code> {@link #getVersion(String) version} because there may not
     * be a version attribute in the manifest.
     * 
     * @return <code>true</code> if the {@link #getContentFile() file} is a valid jar file with a manifest
     */
    public boolean isValid() {
        return null != this.mainAttributes;
    }

    /**
     * Returns the version of the Jar file (or archive satisfying Jar file structure) by inspecting the Manifest.
     * The returned version will be the following, with this preference:<br>
     * <br>
     * Specification-Version (Implementation-Version)<br>
     * Implementation-Version<br> 
     * Specification-Version<br>
     *  
     * @param defaultValue the default value
     * @return the version or the default if it cannot be determined
     */
    public String getVersion(String defaultValue) {
        String result = defaultValue;

        if (null != this.mainAttributes) {
            try {
                String specVersion = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION, null);
                String implVersion = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION, null);
                if ((null != specVersion) && (null != implVersion)) {
                    result = specVersion + " (" + implVersion + ")";
                } else {
                    if (null != implVersion) {
                        result = implVersion;
                    } else if (null != specVersion) {
                        result = specVersion;
                    }
                }
            } catch (Exception e) {
                result = defaultValue;
            }
        }
        return result;
    }

    /**
     * Return a description of the Jar file (or archive satisfying Jar file structure) by inspecting the Manifest.
     * The returned description will be the value of one of the following attributes, with this preference:<br>
     * <br>
     * Implementation-Title<br>
     * Specification-Title<br> 
     * 
     * @param defaultValue the default value
     * @return description or <code>null</code> if it cannot be determined
     */
    public String getDescription(String defaultValue) {
        String result = null;

        if (null != this.mainAttributes) {
            result = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE, null);
            if (result == null) {
                result = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE, null);
            }
        }

        return (result != null) ? result : defaultValue;
    }

    /**
     * Returns an attribute value as found in the manifest.
     * 
     * @param attributeName the attribute name
     * @param defaultValue the default if the attribute does not exist in the manifest 
     *
     * @return the attribute value or the default if it doesn't exist or cannot be retrieved
     */
    public String getAttributeValue(Name attributeName, String defaultValue) {
        return getAttributeValue(attributeName.toString(), defaultValue);
    }

    /**
     * Returns an attribute value as found in the manifest.
     * 
     * @param attributeName the attribute name
     * @param defaultValue the default if the attribute does not exist in the manifest 
     *
     * @return the attribute value or the default if it doesn't exist or cannot be retrieved
     */
    public String getAttributeValue(String attributeName, String defaultValue) {
        String result = defaultValue;

        if (null != this.mainAttributes) {
            try {
                String val = this.mainAttributes.getValue(attributeName);
                if (null != val) {
                    result = val;
                }
            } catch (Exception e) {
                result = defaultValue;
            }
        }

        return result;
    }

    private Manifest getManifest() {
        Manifest manifest = null;
        try {
            File file = getContentFile();

            if (!file.isDirectory()) {
                JarFile jarFile = new JarFile(file);
                if (null != jarFile) {
                    manifest = jarFile.getManifest();
                    jarFile.close();
                }
            } else {
                File manifestFile = new File(file, "/META-INF/MANIFEST.MF");
                if (manifestFile.exists()) {
                    InputStream is = null;
                    try {
                        is = new FileInputStream(manifestFile);
                        manifest = new Manifest(is);
                    } finally {
                        if (null != is)
                            is.close();
                    }
                }
            }
        } catch (Exception ignore) {
        }

        return manifest;
    }
}
