VersionManagement.java

/*
 * @(#)VersionManagement.java
 *
 * Project:		JHotdraw - a GUI framework for technical drawings
 *				http://www.jhotdraw.org
 *				http://jhotdraw.sourceforge.net
 * Copyright:	© by the original author(s) and all contributors
 * License:		Lesser GNU Public License (LGPL)
 *				http://www.opensource.org/licenses/lgpl-license.html
 */

package CH.ifa.draw.util;

import CH.ifa.draw.framework.JHotDrawRuntimeException;
import java.io.*;
import java.util.*;
import java.util.jar.*;

/**
 * Collection of utility methods that are useful for dealing with version information
 * retrieved from reading jar files or package loaded by the class manager. Some
 * methods also help comparing version information. The method getJHotDrawVersion()
 * can be used to retrieve the current version of JHotDraw as loaded by the class manager.
 *
 * @author Wolfram Kaiser
 * @version <$CURRENT_VERSION$>
 */
public class VersionManagement {
	public static String JHOTDRAW_COMPONENT = "CH.ifa.draw/";
	public static String JHOTDRAW_JAR = "jhotdraw.jar";
	
	public static Package[] packages = {
			Package.getPackage("CH.ifa.draw.applet"),
			Package.getPackage("CH.ifa.draw.application"),
			Package.getPackage("CH.ifa.draw.contrib"),
			Package.getPackage("CH.ifa.draw.figures"),
			Package.getPackage("CH.ifa.draw.framework"),
			Package.getPackage("CH.ifa.draw.standard"),
			Package.getPackage("CH.ifa.draw.util")
		};

	/**
	 * Return the version of the main package of the framework. A version number is
	 * available if there is a corresponding version entry in the JHotDraw jar file
	 * for the framework package.
	 */	
	public static String getJHotDrawVersion() {
		// look for the framework main package
		Package pack = packages[4];
		return pack.getSpecificationVersion();
	}

	/**
	 *
	 */
	public static String getPackageVersion(final Package lookupPackage) {
		if (lookupPackage == null) {
			return null;
		}
		
		String specVersion = lookupPackage.getSpecificationVersion();
		if (specVersion != null) {
			return specVersion;
		}
		else {
			// search in parent package
			String normalizedPackageName = normalizePackageName(lookupPackage.getName());
			String nextPackageName = getNextPackage(normalizedPackageName);
			return getPackageVersion(Package.getPackage(nextPackageName));
		}
	}

	/**
	 * Check whether a given application version is compatible with the version
	 * of JHotDraw currently loaded in the Java VM. A version number is
	 * available if there is a corresponding version entry in the JHotDraw jar file
	 * for the framework package.
	 */	
	public static boolean isCompatibleVersion(String compareVersionString) {
//		Package pack = VersionManagement.class.getPackage();
		Package pack = packages[4];
		if (compareVersionString == null) {
			return pack.getSpecificationVersion() == null;
		}
		else {
			return pack.isCompatibleWith(compareVersionString);
		}
	}

	/**
	 * Read the version information from a file with a given file name. The file
	 * must be a jar manifest file and all its entries are searched for package names
	 * and their specification version information. All information is collected
	 * in a map for later lookup of package names and their versions.
	 *
	 * @param versionFileName name of the jar file containing version information
	 */
	public static String readVersionFromFile(String applicationName, String versionFileName) {
		try {
			FileInputStream fileInput = new FileInputStream(versionFileName);
			Manifest manifest = new Manifest();
			manifest.read(fileInput);

			Map entries = manifest.getEntries();
			// Now write out the pre-entry attributes
			Iterator entryIterator = entries.entrySet().iterator();
			while (entryIterator.hasNext()) {
				Map.Entry currentEntry = (Map.Entry)entryIterator.next();
				String packageName = currentEntry.getKey().toString();
				packageName = normalizePackageName(packageName);
				Attributes attributes = (Attributes)currentEntry.getValue();
				String packageSpecVersion = attributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
				packageSpecVersion = extractVersionInfo(packageSpecVersion);
				return packageSpecVersion;
			}
		}
		catch (IOException exception) {
			exception.printStackTrace();
		}
		
		// no version found
		return null;
	}

	/**
	 * Get the super package of a package specifier. The super package is the package
	 * specifier without the latest package name, e.g. the next package for "org.jhotdraw.tools"
	 * would be "org.jhotdraw".
	 *
	 * @param searchPackage package specifier
	 * @return next package if one is available, null otherwise
	 */
	protected static String getNextPackage(String searchPackage) {
		if (searchPackage == null) {
			return null;
		}
	
		int foundNextPackage = searchPackage.lastIndexOf('.');
		if (foundNextPackage > 0) {
			return searchPackage.substring(0, foundNextPackage);
		}
		else {
			return null;
		}
	}
	
	/**
	 * A package name is normalized by replacing all path delimiters by "." to retrieve
	 * a valid standardized package specifier used in package declarations in Java source
	 * files.
	 *
	 * @param toBeNormalized package name to be normalized
	 * @return normalized package name
	 */
	public static String normalizePackageName(String toBeNormalized) {
		// first, replace the standard package delimiter used in jars
		String replaced = toBeNormalized.replace('/', '.');
		// then, replace the default path separator in case this one was used as well
		replaced = replaced.replace(File.pathSeparatorChar, '.');
		if (replaced.endsWith(".")) {
			int lastSeparator = replaced.lastIndexOf('.');
			return replaced.substring(0, lastSeparator);
		}
		else {
			return replaced;
		}
	}

	/**
	 * Get the version information specified in a jar manifest file without
	 * any leading or trailing "\"".
	 *
	 * @param versionString a version string with possibly leading or trailing "\""
	 * @return stripped version information
	 */
	public static String extractVersionInfo(String versionString) {
		// guarding conditions
		if (versionString == null) {
			return null;
		}
		if (versionString.length() == 0) {
			return "";
		}
		
		int startIndex = versionString.indexOf("\"");
		if (startIndex < 0) {
			startIndex = 0;
		}
		else {
			// start from next character
			startIndex++;
		}
		
		int endIndex = versionString.lastIndexOf("\"");
		if (endIndex < 0) {
			endIndex = versionString.length();
		}
		
		return versionString.substring(startIndex, endIndex);
	}
}