diff options
Diffstat (limited to 'FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools')
39 files changed, 5438 insertions, 0 deletions
diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Entries b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Entries new file mode 100644 index 000000000..399714e97 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Entries @@ -0,0 +1,2 @@ +D/internal//// +D/versioning//// diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Repository b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Repository new file mode 100644 index 000000000..e5e983673 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Repository @@ -0,0 +1 @@ +org.eclipse.sdk.tests-feature/plugins/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Root b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Root new file mode 100644 index 000000000..2d37d165b --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@dev.eclipse.org:/cvsroot/eclipse diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Tag b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Tag new file mode 100644 index 000000000..4dc597cc4 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/CVS/Tag @@ -0,0 +1 @@ +Tr20080922 diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Entries b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Entries new file mode 100644 index 000000000..6a61f3c67 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Entries @@ -0,0 +1 @@ +D/versioning//// diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Repository b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Repository new file mode 100644 index 000000000..07366a6a4 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Repository @@ -0,0 +1 @@ +org.eclipse.sdk.tests-feature/plugins/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Root b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Root new file mode 100644 index 000000000..2d37d165b --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@dev.eclipse.org:/cvsroot/eclipse diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Tag b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Tag new file mode 100644 index 000000000..4dc597cc4 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/CVS/Tag @@ -0,0 +1 @@ +Tr20080922 diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/Activator.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/Activator.java new file mode 100755 index 000000000..5983ccade --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/Activator.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import org.eclipse.osgi.service.debug.DebugOptions; +import org.eclipse.osgi.service.pluginconversion.PluginConverter; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +/** + * org.eclipse.pde.tools.versioning bundle activator + * + */ +public class Activator implements BundleActivator { + public static final String PLUGIN_ID = "org.eclipse.pde.tools.versioning"; //$NON-NLS-1$ + private static PluginConverter pluginConverter = null; + private static BundleContext context = null; + private static ServiceTracker bundleTracker = null; + private static ServiceTracker debugTracker = null; + + /** + * Log the given debug message. + */ + public static void debug(String message) { + if (message != null) + System.out.println(message); + } + + /** + * Return a boolean value indicating whether or not debug options are + * turned on for the given key. + */ + public static boolean getBooleanDebugOption(String key) { + if (context == null) + return false; + if (debugTracker == null) { + debugTracker = new ServiceTracker(context, DebugOptions.class.getName(), null); + debugTracker.open(); + } + DebugOptions debug = (DebugOptions) debugTracker.getService(); + return debug == null ? false : debug.getBooleanOption(key, false); + } + + /* + * Constructor for the class. + */ + public Activator() { + super(); + } + + /* (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext runtimeContext) throws Exception { + context = runtimeContext; + } + + /* (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext runtimeContext) { + if (bundleTracker != null) { + bundleTracker.close(); + bundleTracker = null; + } + if (debugTracker != null) { + debugTracker.close(); + debugTracker = null; + } + context = null; + pluginConverter = null; + } + + /** + * Return the plug-in converter class or <code>null</code> if it + * is not available. + * + * @return the plug-in converter or <code>null</code> + */ + public static PluginConverter getPluginConverter() { + if (pluginConverter == null) { + if (bundleTracker == null) { + if (context == null) + return null; + bundleTracker = new ServiceTracker(context, PluginConverter.class.getName(), null); + bundleTracker.open(); + } + pluginConverter = (PluginConverter) bundleTracker.getService(); + } + return pluginConverter; + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Entries b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Entries new file mode 100644 index 000000000..8c69857b6 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Entries @@ -0,0 +1,21 @@ +/Activator.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/CheckedItem.java/1.2/Tue Oct 10 15:43:45 2006//Tr20080922 +/ClassFileHelper.java/1.2/Tue Oct 10 15:43:45 2006//Tr20080922 +/CompareOptionFileHelper.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/CompareResult.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/FeatureModelTable.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/FeatureVersionCompare.java/1.2/Mon Sep 22 17:32:07 2008//Tr20080922 +/JavaClassVersionCompare.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/ManifestHelper.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/Messages.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/PluginVersionCompare.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersionClassDirFilter.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersionCompareConstants.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersionCompareDispatcher.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersionVerifier.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersioningDirFilter.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersioningFeatureFileFilter.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersioningFilenameFilter.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/VersioningProgressMonitorWrapper.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/messages.properties/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +D diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Repository b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Repository new file mode 100644 index 000000000..34c27aef8 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Repository @@ -0,0 +1 @@ +org.eclipse.sdk.tests-feature/plugins/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Root b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Root new file mode 100644 index 000000000..2d37d165b --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@dev.eclipse.org:/cvsroot/eclipse diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Tag b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Tag new file mode 100644 index 000000000..25d2caaa5 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CVS/Tag @@ -0,0 +1 @@ +Nr20080922 diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CheckedItem.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CheckedItem.java new file mode 100755 index 000000000..66f6a5045 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CheckedItem.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import org.eclipse.pde.tools.versioning.IVersionCompare; +import org.osgi.framework.Version; + +/** + * This class represents version change of a checked feature(or plugin) + */ +public class CheckedItem { + /** + * compare source feature or plugin key(id + "#" + version) + */ + private String sourceKey; + /** + * compare destination feature or plugin key(id + "#" + version) + * for plugin, this value must be set when create a new instance of this class, + * since it is possible there are two plugins have the same id but different versions exist + * in an Eclipse installation; + * for feature, this value can be set as <code>null</code> + */ + private String destinationKey; + private Version version; + private int change; + + /** + * Constructor for the class. Set the key, version, and change to be the given values. + * + * @param sourceKey compare source feature id(or plugin id) + "#" + feature version(or plugin version) + * @param destinationKey compare destination feature id(or plugin id) + "#" + feature version(or plugin version) + * @param version if the new version is correct, it is the new version; if the new version + * is incorrect, it is the recommended version; if some error happened, it is <code>null</code> + * @param change change happened on the feature or plugin + */ + public CheckedItem(String sourceKey, String destinationKey, Version version, int change) { + this.sourceKey = sourceKey; + this.destinationKey = destinationKey; + this.version = version; + this.change = change; + } + + /** + * Return the compare source feature(or plugin) key. + * + * @return feature(or plugin) key, feature id(or plugin id) + "#" + feature version(or plugin version) + */ + public String getSourceKey() { + return this.sourceKey; + } + + /** + * Return the compare destination feature(or plugin) key. + * + * @return feature(or plugin) key, feature id(or plugin id) + "#" + feature version(or plugin version) + * it can be <code>null</code> if the instance of this class represents a compare result of features + */ + public String getDestinationKey() { + return this.destinationKey; + } + + /** + * Return the compare result's version. If the new version is correct, it is the + * new version; if the new version is incorrect, it is the recommended + * version; if some error happened, it is <code>null</code>. + * + * @return version the version or <code>null</code> + */ + public Version getVersion() { + return this.version; + } + + /** + * returns change on the feature or plugin + * <p> + * The value of change is an int number of the following: + * <ul> + * <li>{@link IVersionCompare#ERROR_OCCURRED}</li> + * <li>{@link IVersionCompare#MAJOR_CHANGE}</li> + * <li>{@link IVersionCompare#MINOR_CHANGE}</li> + * <li>{@link IVersionCompare#NEW_ADDED}</li> + * <li>{@link IVersionCompare#NO_LONGER_EXIST}</li> + * <li>{@link IVersionCompare#MICRO_CHANGE}</li> + * <li>{@link IVersionCompare#QUALIFIER_CHANGE}</li> + * <li>{@link IVersionCompare#NO_CHANGE}</li> + * </ul> + * </p> + * @return change int number which indicates the overall change happened on a plugin or class + * @see IVersionCompare#ERROR_OCCURRED + * @see IVersionCompare#MAJOR_CHANGE + * @see IVersionCompare#MINOR_CHANGE + * @see IVersionCompare#NEW_ADDED + * @see IVersionCompare#NO_LONGER_EXIST + * @see IVersionCompare#MICRO_CHANGE + * @see IVersionCompare#QUALIFIER_CHANGE + * @see IVersionCompare#NO_CHANGE + */ + public int getChange() { + return this.change; + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/ClassFileHelper.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/ClassFileHelper.java new file mode 100755 index 000000000..69eeddd42 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/ClassFileHelper.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.*; +import java.net.URL; +import org.eclipse.core.runtime.*; +import org.eclipse.jdt.core.ToolFactory; +import org.eclipse.jdt.core.util.IClassFileReader; +import org.eclipse.osgi.util.NLS; + +/** + * Helper class for dealing with class files. + */ +public class ClassFileHelper implements VersionCompareConstants { + + /** + * Return a class file reader based on the given object or <code>null</code>. + */ + public static IClassFileReader getReader(Object object) throws CoreException { + if (object instanceof String) + return getClassReaderFromFile(new File((String) object)); + if (object instanceof File) + return getClassReaderFromFile((File) object); + if (object instanceof URL) + return getClassReaderFromURL((URL) object); + if (object instanceof InputStream) { + IClassFileReader fileReader = ToolFactory.createDefaultClassFileReader((InputStream) object, IClassFileReader.ALL); + return fileReader; + } else if (object instanceof IClassFileReader) + return (IClassFileReader) object; + else + // otherwise throw CoreException + throw new CoreException(new Status(IStatus.WARNING, PLUGIN_ID, IStatus.WARNING, NLS.bind(Messages.JavaClassVersionCompare_unexpectedTypeMsg, object.getClass().getName()), null)); + } + + /** + * gets IClassFileReader of the java class denoted by <code>file</code> + * @param file java class file + * @return IClassFileReader instance or <code>null</code>if file type is wrong or any error occurred + */ + private static IClassFileReader getClassReaderFromFile(File file) { + if (isGivenTypeFile(file, CLASS_FILE_EXTENSION)) + return ToolFactory.createDefaultClassFileReader(file.getAbsolutePath(), IClassFileReader.ALL_BUT_METHOD_BODIES); + if (isGivenTypeFile(file, JAVA_FILE_EXTENSION)) + return null; + return null; + } + + /** + * Checks whether or not the given file potentially represents a given type file on the file-system. + * + * @param file File instance which denotes to a file on the file-system + * @param type file type(e.g. "java","class") + * @return <code>true</code> <code>file</code> exists and is a configuration file, + * <code>false</code> otherwise + */ + private static boolean isGivenTypeFile(File file, String type) { + return file.isFile() && file.getName().endsWith(type); + } + + /** + * Return a class file reader based on the contents of the given URL's input stream. + * + * @param url location of the class file + * @return the class file reader or <code>null</code>if any error occurred + */ + private static IClassFileReader getClassReaderFromURL(URL url) { + try { + InputStream input = url.openStream(); + try { + return ToolFactory.createDefaultClassFileReader(input, IClassFileReader.ALL_BUT_METHOD_BODIES); + } finally { + if (input != null) { + input.close(); + input = null; + } + } + } catch (IOException e) { + //ignore, result will be checked outside + } + return null; + } + +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CompareOptionFileHelper.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CompareOptionFileHelper.java new file mode 100755 index 000000000..18a187a19 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CompareOptionFileHelper.java @@ -0,0 +1,390 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.eclipse.core.runtime.*; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.tools.versioning.IVersionCompare; +import org.eclipse.update.core.IIncludedFeatureReference; +import org.eclipse.update.core.model.FeatureModel; +import org.eclipse.update.core.model.PluginEntryModel; + +/** + * This class provides methods to process compare option property file, store the inclusive and exclusive + * information, check if a feature of plugin is need to be compared. + * <p> + * The properties in the compare option file are as following: + * <ul> + * <li>"exclude.os" - which indicates exclusive Operation Systems + * <li>"include.os" - which indicates inclusive Operation Systems + * <li>"exclude.ws" - which indicates exclusive windows system architecture specifications + * <li>"include.ws" - which indicates inclusive windows system architecture specifications + * <li>"exclude.arch" - which indicates exclusive optional system architecture specifications + * <li>"include.arch" - which indicates inclusive optional system architecture specifications + * <li>"exclude.nl" - which indicates exclusive optional locale language specifications + * <li>"include.nl" - which indicates inclusive optional locale language specifications + * <li>"exclude.features" - which indicates exclusive feature ids + * <li>"include.features" - which indicates inclusive feature ids + * <li>"exclude.plugins" - which indicates exclusive plugin ids + * <li>"include.plugins" - which indicates inclusive plugin ids + * </ul> + * </p> + * + */ +public class CompareOptionFileHelper implements VersionCompareConstants { + + private Map optionTable = null; + + /** + * constructor + * @param file compare option file + * @throws CoreException <p>if nested CoreException has been thrown + */ + public CompareOptionFileHelper(File file) throws CoreException { + if (file == null) + return; + processVersioningOptionFile(file); + } + + /** + * processes the compare option file denoted by <code>file</code>, and put + * property key as key, List of property values of the property key as value + * into a map. + * + * + * @param file compare option file + * @throws CoreException if <code>file</code> does not exist or any IOException has been thrown + */ + private void processVersioningOptionFile(File file) throws CoreException { + Map table = new Hashtable(); + InputStream inputStream = null; + Properties properties = new Properties(); + try { + inputStream = new BufferedInputStream(new FileInputStream(file)); + properties.load(inputStream); + } catch (FileNotFoundException fnfe) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_fileNotFoundMsg, file.getAbsolutePath()), fnfe)); + } catch (IOException ioe) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_readPropertyFailedMsg, file.getAbsolutePath()), ioe)); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ioe) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_closeFileFailedMsg, file.getAbsolutePath()), ioe)); + } + } + } + for (Iterator iterator = properties.keySet().iterator(); iterator.hasNext();) { + Object key = iterator.next(); + String property = properties.getProperty((String) key); + if (property == null || property.trim().equals(VersionCompareConstants.EMPTY_STRING)) + continue; + List propertyValueList; + // for feature or plugin inclusion, exclusion values, we convert them into Patterns + if (key.equals(IVersionCompare.INCLUDE_FEATURES_OPTION) || key.equals(IVersionCompare.EXCLUDE_FEATURES_OPTION) || key.equals(IVersionCompare.INCLUDE_PLUGINS_OPTION) || key.equals(IVersionCompare.EXCLUDE_PLUGINS_OPTION)) + propertyValueList = generatePatternList(Arrays.asList(property.split(VersionCompareConstants.COMMA_MARK))); + else + propertyValueList = Arrays.asList(property.split(VersionCompareConstants.COMMA_MARK)); + table.put(key, propertyValueList); + } + this.optionTable = table; + } + + /* + * check the includes/excludes filters based on operating system + */ + private boolean filterOS(Object object) { + List inclusionValueList = (List) optionTable.get(IVersionCompare.INCLUDE_OS_OPTION); + String[] propertyValue; + if (object instanceof IIncludedFeatureReference) + propertyValue = ((IIncludedFeatureReference) object).getOS() == null ? null : ((IIncludedFeatureReference) object).getOS().split(COMMA_MARK); + else if (object instanceof PluginEntryModel) + propertyValue = ((PluginEntryModel) object).getOS() == null ? null : ((PluginEntryModel) object).getOS().split(COMMA_MARK); + else + propertyValue = ((FeatureModel) object).getOS() == null ? null : ((FeatureModel) object).getOS().split(COMMA_MARK); + if (!inclusionMatch(propertyValue, inclusionValueList)) + return false; + // inclusionMatch is true, do exclusionMatch + return !exclusionMatch(propertyValue, (List) optionTable.get(IVersionCompare.EXCLUDE_OS_OPTION)); + } + + /* + * check the includes/excludes filters based on windowing system + */ + private boolean filterWS(Object object) { + List inclusionValueList = (List) optionTable.get(IVersionCompare.INCLUDE_WS_OPTION); + String[] propertyValue; + if (object instanceof IIncludedFeatureReference) + propertyValue = ((IIncludedFeatureReference) object).getWS() == null ? null : ((IIncludedFeatureReference) object).getWS().split(COMMA_MARK); + else if (object instanceof PluginEntryModel) + propertyValue = ((PluginEntryModel) object).getWS() == null ? null : ((PluginEntryModel) object).getWS().split(COMMA_MARK); + else + propertyValue = ((FeatureModel) object).getWS() == null ? null : ((FeatureModel) object).getWS().split(COMMA_MARK); + if (!inclusionMatch(propertyValue, inclusionValueList)) + return false; + // inclusionMatch is true, do exclusionMatch + return !exclusionMatch(propertyValue, (List) optionTable.get(IVersionCompare.EXCLUDE_WS_OPTION)); + } + + /* + * check the includes/excludes filters based on system architecture + */ + private boolean filterArch(Object object) { + List inclusionValueList = (List) optionTable.get(IVersionCompare.INCLUDE_ARCH_OPTION); + String[] propertyValue; + if (object instanceof IIncludedFeatureReference) + propertyValue = ((IIncludedFeatureReference) object).getOSArch() == null ? null : ((IIncludedFeatureReference) object).getOSArch().split(COMMA_MARK); + else if (object instanceof PluginEntryModel) + propertyValue = ((PluginEntryModel) object).getOSArch() == null ? null : ((PluginEntryModel) object).getOSArch().split(COMMA_MARK); + else + propertyValue = ((FeatureModel) object).getOSArch() == null ? null : ((FeatureModel) object).getOSArch().split(COMMA_MARK); + if (!inclusionMatch(propertyValue, inclusionValueList)) + return false; + // inclusionMatch is true, do exclusionMatch + return !exclusionMatch(propertyValue, (List) optionTable.get(IVersionCompare.EXCLUDE_ARCH_OPTION)); + } + + /* + * check the includes/excludes filters based on the language + */ + private boolean filterNL(Object object) { + List inclusionValueList = (List) optionTable.get(IVersionCompare.INCLUDE_NL_OPTION); + String[] propertyValue; + if (object instanceof IIncludedFeatureReference) + propertyValue = ((IIncludedFeatureReference) object).getNL() == null ? null : ((IIncludedFeatureReference) object).getNL().split(COMMA_MARK); + else if (object instanceof PluginEntryModel) + propertyValue = ((PluginEntryModel) object).getNL() == null ? null : ((PluginEntryModel) object).getNL().split(COMMA_MARK); + else + propertyValue = ((FeatureModel) object).getNL() == null ? null : ((FeatureModel) object).getNL().split(COMMA_MARK); + if (!inclusionMatch(propertyValue, inclusionValueList)) + return false; + // inclusionMatch is true, do exclusionMatch + return !exclusionMatch(propertyValue, (List) optionTable.get(IVersionCompare.EXCLUDE_NL_OPTION)); + } + + /* + * check the includes/excludes filters based on feature identifiers + */ + private boolean filterFeatures(Object object) throws CoreException { + List inclusionPatternList = (List) optionTable.get(IVersionCompare.INCLUDE_FEATURES_OPTION); + String id; + if (object instanceof IIncludedFeatureReference) + id = ((IIncludedFeatureReference) object).getVersionedIdentifier().getIdentifier(); + else + id = ((FeatureModel) object).getFeatureIdentifier(); + if (inclusionPatternList != null) { + if (isMatch(id, inclusionPatternList)) { + // inclusion match, do exclusion match + List exclusionPatternList = (List) optionTable.get(IVersionCompare.EXCLUDE_FEATURES_OPTION); + if (exclusionPatternList != null && isMatch(id, exclusionPatternList)) + return false; + } else + return false; + } else { + // do exclusion match + List exclusionPatternList = (List) optionTable.get(IVersionCompare.EXCLUDE_FEATURES_OPTION); + if (exclusionPatternList != null && isMatch(id, exclusionPatternList)) + return false; + } + return true; + } + + /* + * check the includes/excludes filters based on plugin identifiers + */ + private boolean filterPlugins(Object object) { + List inclusionPatternList = (List) optionTable.get(IVersionCompare.INCLUDE_PLUGINS_OPTION); + String id = ((PluginEntryModel) object).getPluginIdentifier(); + if (inclusionPatternList != null) { + if (isMatch(id, inclusionPatternList)) { + // inclusion match, do exclusion match + List exclusionPatternList = (List) optionTable.get(IVersionCompare.EXCLUDE_PLUGINS_OPTION); + if (exclusionPatternList != null && isMatch(id, exclusionPatternList)) + return false; + } else + return false; + } else { + // do exclusion match + List exclusionPatternList = (List) optionTable.get(IVersionCompare.EXCLUDE_PLUGINS_OPTION); + if (exclusionPatternList != null && isMatch(id, exclusionPatternList)) + return false; + } + return true; + } + + /** + * Returns a boolean value indicating whether or not the given object should be compared + * based on the includes and excludes filters. + * <p> + * The parameter can be of any of the following types: + * <ul> + * <li><code>IIncludedFeatureReference</code></li> + * <li><code>FeatureModel</code></li> + * <li><code>PluginEntryModel</code></li> + * </ul> + * </p> + * + * @param object the object to check + * @return <code>true</code> if we should compare and <code>false</code> otherwise + * @throws CoreException <p> if any nested CoreException has been thrown</p> + */ + public boolean shouldCompare(Object object) throws CoreException { + if (!((object instanceof IIncludedFeatureReference) || (object instanceof PluginEntryModel) || (object instanceof FeatureModel))) + return false; + if (optionTable == null || optionTable.size() == 0) + // if inclusionTable is null, means everything need to be compared + return true; + + // check the includes/excludes filters based on operating system + if (!filterOS(object)) + return false; + + // check the includes/excludes filters based on windowing system + if (!filterWS(object)) + return false; + + // check the includes/excludes filters based on system architecture + if (!filterArch(object)) + return false; + + // check the includes/excludes filters based on the language + if (!filterNL(object)) + return false; + + // check the includes/excludes filters based on feature or plugin identifiers + if ((object instanceof IIncludedFeatureReference) || (object instanceof FeatureModel)) + return filterFeatures(object); + else + return filterPlugins(object); + } + + /** + * checks whether <code>list1</code> contains at least one element of <code>list2</code> + * @param list1 List + * @param list2 List + * @return <code>true</code>if <code>list1</code> contains at least one element of <code>list2</code>, + * <code>false</code> otherwise + */ + private boolean isIntersecting(List list1, List list2) { + for (Iterator iterator = list2.iterator(); iterator.hasNext();) { + Object key = iterator.next(); + if (list1.contains(key)) + return true; + } + return false; + } + + /** + * check whether given <code>values</code> and <code>inclusionValueList</code> has an intersection + * @param values String array + * @param inclusionValueList inclusion value list + * @return <code>true</code> if given <code>values</code> and <code>inclusionValueList</code> has an intersection + * <code>false</code> otherwise + */ + private boolean inclusionMatch(String[] values, List inclusionValueList) { + if (values == null || inclusionValueList == null) + return true; + List propertyValue = Arrays.asList(values); + if (!isIntersecting(inclusionValueList, propertyValue)) + // if propertyValue and inclusionValueList does not an intersection, return false + return false; + return true; + } + + /** + * check whether given <code>values</code> are included the <code>exclusionValueList</code> + * @param values String array + * @param exclusionValueList exclusion value list + * @return <code>true</code> if given <code>values</code> are included the <code>exclusionValueList</code> + * <code>false</code> otherwise + */ + private boolean exclusionMatch(String[] values, List exclusionValueList) { + if (values == null || exclusionValueList == null) + return false; + List propertyValue = Arrays.asList(values); + if (!isIncluded(exclusionValueList, propertyValue)) + // if propertyValue are not included in exclusionValuList, return false + return false; + return true; + } + + /** + * checks whether <code>list1</code> contains all the elements of <code>list2</code> + * @param list1 List + * @param list2 List + * @return <code>true</code>if <code>list1</code> contains all the elements of <code>list2</code>, + * <code>false</code> otherwise + */ + private boolean isIncluded(List list1, List list2) { + for (Iterator iterator = list2.iterator(); iterator.hasNext();) { + Object key = iterator.next(); + if (!list1.contains(key)) + return false; + } + return true; + } + + /** + * checks whether the <code>string</code> matches the <code>regex</code> + * @param string String need to be matched + * @param patternList List contains Patterns + * @return <code>true</code>if the <code>string</code> matches the <code>regex</code> + * <code>false</code>otherwise + */ + private boolean isMatch(String string, List patternList) { + for (Iterator iterator = patternList.iterator(); iterator.hasNext();) { + Pattern pattern = (Pattern) iterator.next(); + Matcher matcher = pattern.matcher(string); + if (matcher.matches()) + return true; + } + return false; + } + + /** + * generates Pattern instances from each String in <code>list</code> + * @param list List contains Strings + * @return List contains Pattern instances + */ + private List generatePatternList(List list) { + List newList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) { + newList.add(Pattern.compile(generateRegex((String) iterator.next()))); + } + return newList; + } + + /** + * generates regular expression from <code>string</code> + * @param string + * @return regular expression + */ + private String generateRegex(String string) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < string.length(); i++) { + char singleChar = string.charAt(i); + if (singleChar == START_CHAR) + // convert '*' to ".*" + buffer.append(WILD_CAST_STRING); + else if (singleChar == DOT_CHAR) + // convert '.' to "\." + buffer.append(DOT_QUOTE_STRING); + else + buffer.append(singleChar); + } + return buffer.toString(); + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CompareResult.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CompareResult.java new file mode 100755 index 000000000..8cd7edd5b --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/CompareResult.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.pde.tools.versioning.ICompareResult; + +/** + * This class represents compare result of a plugin or class + */ +public class CompareResult implements ICompareResult { + int change; + MultiStatus status; + + /** + * constructor + * + * @param change change on a feature or plugin + * @param status contains messages created when verify a plugin or class + */ + public CompareResult(int change, MultiStatus status) { + this.change = change; + this.status = status; + } + + /** + * gets status which contains messages created when verify a plugin or class + * @return MultiStatus instance + */ + public MultiStatus getResultStatus() { + return this.status; + } + + /** + * get change on a plugin or class + * @return change + */ + public int getChange() { + return this.change; + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/FeatureModelTable.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/FeatureModelTable.java new file mode 100755 index 000000000..2c3232ca1 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/FeatureModelTable.java @@ -0,0 +1,386 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; +import org.eclipse.core.runtime.*; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.tools.versioning.IVersionCompare; +import org.eclipse.update.configurator.ConfiguratorUtils; +import org.eclipse.update.configurator.IPlatformConfiguration; +import org.eclipse.update.configurator.IPlatformConfiguration.ISiteEntry; +import org.eclipse.update.core.model.FeatureModel; +import org.eclipse.update.core.model.FeatureModelFactory; +import org.xml.sax.SAXException; + +/** + * FeatureModels use two hashtables to store FeatureModels and + * feature.xml file locations. It provides methods to access + * information of FeatureModels + * + * + */ +public class FeatureModelTable implements VersionCompareConstants { + + class TableEntry { + FeatureModel model; + IPath location; + + TableEntry(FeatureModel model, IPath location) { + super(); + this.model = model; + this.location = location; + } + } + + // indicate the FeatureModels is from a configuration file or a directory + private boolean isConfiguration; + // table for storing features. key is a string which is the feature identifier. values are instances + // of the TableEntry inner class as defined by this type. + private Map featureTable; + // MultiStatus which stores error or warning message + private MultiStatus status; + // + private CompareOptionFileHelper compareOptionFileHelper; + + /** + * constructor + * + * @param object it could be a String, File, or URL which denotes to a eclipse configuration file or a feature directory + * @param helper CompareOptionFileHelper instance + * @throws CoreException <p>if any nested CoreException has been thrown + */ + public FeatureModelTable(Object object, CompareOptionFileHelper helper) throws CoreException { + if (object instanceof URL) + initialize((URL) object, helper); + else if (object instanceof File) + initialize((File) object, helper); + else if (object instanceof String) + initialize(new File((String) object), helper); + } + + /** + * initialization + * + * @param configURL URL instance which denotes an eclipse configuration XML file + * @throws CoreException + */ + private void initialize(URL configURL, CompareOptionFileHelper helper) throws CoreException { + compareOptionFileHelper = helper; + status = new MultiStatus(PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + featureTable = new HashMap(0); + IPlatformConfiguration config; + // get configuration + try { + config = ConfiguratorUtils.getPlatformConfiguration(configURL); + } catch (IOException ioe) { + throw new CoreException(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureModelTable_couldNotReadConfigFileMsg, configURL.getFile()), ioe)); + } + // generate Lists which store URLs of feature entries included in configuration1 and configuration2 + List featureList = generateFeatureEntryList(configURL, config); + if (featureList == null) { + status.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureVersionCompare_noFeaturesFoundInConfigMsg, configURL), null)); + } + // generate feature model maps and feature address maps + generateFeatureModelTable(config, featureList); + // this is a configuration file + isConfiguration = true; + } + + /** + * initialization + * + * @param file File instance which denotes an directory of FileSystem + * @param map inclusive map + * @throws CoreException + */ + private void initialize(File file, CompareOptionFileHelper helper) throws CoreException { + // check to see if we are pointing to configuration files + if (isConfiguration(file)) { + try { + initialize(file.toURL(), helper); + return; + } catch (MalformedURLException e) { + throw new CoreException(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureModelTable_urlConvertErrorMsg, file.getAbsolutePath()), e)); + } + } + compareOptionFileHelper = helper; + status = new MultiStatus(PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + featureTable = new HashMap(0); + // check to see if we have feature directories + if (isFeaturesDirectory(file)) { + // if file are directory, get sub feature directories under it + File[] subFeatures = file.listFiles(new VersioningDirFilter()); + // check if there is any feature directory under directory1 and directory2 + if (subFeatures == null || subFeatures.length == 0) { + status.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureVersionCompare_noFeatureFoundMsg, file), null)); + return; + } + // generate feature model maps + generateFeatureModelTable(subFeatures); + if (featureTable.size() == 0) + status.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureVersionCompare_noFeatureFoundMsg, file), null)); + // this is a directory + isConfiguration = false; + return; + } + status.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureModelTable_featureSourceErrorMsg, file), null)); + } + + /** + * check whether or not the given file potentially represents a platform configuration file on the file-system. + * + * @return <code>true</code> if <code>file</code> is a configuration file + * <code>false</code> otherwise + */ + private boolean isConfiguration(File file) { + IPath path = new Path(file.getAbsolutePath()); + return file.isFile() && CONFIGURATION_FILE_NAME.equalsIgnoreCase(path.lastSegment()); + } + + /** + * check whether or not the given file represents a features directory. + * + * @return <code>true</code> if <code>file</code> is a directory + * <code>false</code> otherwise + */ + private boolean isFeaturesDirectory(File file) { + return file.isDirectory(); + } + + /** + * extracts FeatureModels of features in <code>featureFiles</code>, and + * put them into hashtable + * Key is feature id + * Value is FeatureModel instance of the feature + * + * @param featureFiles array of File instances + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private void generateFeatureModelTable(File[] featureFiles) throws CoreException { + File[] featureXMLFile; + for (int i = 0; i < featureFiles.length; i++) { + // get "feature.xml" file under the feature directory + featureXMLFile = featureFiles[i].listFiles(new VersioningFeatureFileFilter(FEATURES_FILE_NAME)); + if (featureXMLFile == null || featureXMLFile.length == 0) { + // there is no "feature.xml" file found + continue; + } + // parse the "feature.xml" file and get its FeatureModel object + FeatureModel fm = parseFeature(featureXMLFile[0].getAbsolutePath()); + if (compareOptionFileHelper == null ? true : compareOptionFileHelper.shouldCompare(fm)) + // add the feature and its information to the table + featureTable.put(fm.getFeatureIdentifier(), new TableEntry(fm, new Path(featureXMLFile[0].getAbsolutePath()))); + } + } + + /** + * Generates a list which contains locations of feature entries included in <code>config</code> + * represented by <code>IPath</code> objects. + * + * @param config configuration + * @return List instance contains IPaths of feature entries included in <code>config</code>; return is <code>null</code> + * if there is no feature entry has been found in <code>config</code> + */ + private List generateFeatureEntryList(URL configURL, IPlatformConfiguration config) { + // get site entries from the configuration + ISiteEntry[] sites = config.getConfiguredSites(); + // create a new List + ArrayList list = new ArrayList(0); + for (int i = 0; i < sites.length; i++) { + IPath sitePath; + // get URL of site + URL siteURL = sites[i].getURL(); + if (siteURL.getProtocol().equals(PLATFORM_PROTOCOL)) { + sitePath = new Path(configURL.getPath()); + // path points to the platform.xml file in the update bundle's + // location in the configuration area so work our way back + // to the root of the Eclipse install. + sitePath = sitePath.removeLastSegments(3); + } else { + sitePath = new Path(siteURL.getPath()); + } + String[] features = sites[i].getFeatures(); + // create a path for each feature entry included in the site + for (int j = 0; j < features.length; j++) + list.add(sitePath.append(features[j]).append(FEATURES_FILE_NAME)); + } + return list.size() == 0 ? null : list; + } + + /** + * extracts FeatureModels of features in configuration file denoted by <code>config</code>, + * Key is feature id + * Value is FeatureModel object + * + * @param config configuration + * @param list list of feature locations (IPath) + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private void generateFeatureModelTable(IPlatformConfiguration config, List list) throws CoreException { + // create a map which contains the feature id and version of the feature entries in the configuration + for (Iterator features = list.iterator(); features.hasNext();) { + IPath location = null; + try { + // get location of feature + location = (IPath) features.next(); + // get FeatureModel + FeatureModel fm = parseFeature(location.toFile().toURL().openStream()); + if (compareOptionFileHelper == null ? true : compareOptionFileHelper.shouldCompare(fm)) + featureTable.put(fm.getFeatureIdentifier(), new TableEntry(fm, location)); + } catch (IOException ioe) { + status.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureVersionCompare_featureFileErrorMsg, location.toOSString()), ioe)); + continue; + } + } + } + + /** + * Return a new status object populated with the given information. + * + * @param severity severity of status + * @param code indicates type of this IStatus instance, it could be one of: FEATURE_OVERALL_STATUS, + * FEATURE_DETAIL_STATUS, PLUGIN_OVERALL_STATUS, PLUGIN_DETAIL_STATUS, PROCESS_ERROR_STATUS, + * CLASS_OVERALL_STATUS, CLASS_DETAIL_STATUS + * @param message the status message + * @param exception exception which has been caught, or <code>null</code> + * @return the new status object + */ + private IStatus resultStatusHandler(int severity, int code, String message, Exception exception) { + if (message == null) { + if (exception != null) + message = exception.getMessage(); + // extra check because the exception message can be null + if (message == null) + message = EMPTY_STRING; + } + return new Status(severity, PLUGIN_ID, code, message, exception); + } + + /** + * Parses the feature manifest file denoted by the given input stream and returns + * the resulting feature model object. The stream is closed when this method is returned. + * + * @param input the stream pointing to the feature manifest + * @return resulting feature model object + * @throws CoreException if there was an error parsing the input stream + */ + private FeatureModel parseFeature(InputStream input) throws CoreException { + try { + FeatureModelFactory factory = new FeatureModelFactory(); + return factory.parseFeature(input); + } catch (SAXException saxe) { + throw new CoreException(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS, null, saxe)); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + // ignore + } + } + } + } + + /** + * Parses the feature manifest file located in the file-system at the given path. Return + * the resulting feature model object. + * + * @param path the location of the feature manifest + * @return the resulting feature model + * @throws CoreException if there was an error parsing the feature manifest + */ + private FeatureModel parseFeature(String path) throws CoreException { + try { + InputStream fis = new BufferedInputStream(new FileInputStream(path)); + // return FeatureModel of feature.xml the FielInputStream passed to parseFeature(InputStream) + // will be closed in parseFeature(InputStream featureInputStream) method. + return parseFeature(fis); + } catch (FileNotFoundException fnfe) { + throw new CoreException(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureModelTable_featureFileParseErrorMsg, path), fnfe)); + } + } + + /** + * checks if the FeatureModels included in the current object are from a configuration file + * @return <code>true</code> if the current object contains FeatureModels from a configuration file + * <code>false</code> otherwise + */ + public boolean isConfiguration() { + return isConfiguration; + } + + /** + * checks if there is any error occurred when generate the current object + * @return <code>true</code> if there is no error occurred + * <code>false</code> otherwise + */ + public boolean isOK() { + return status.isOK(); + } + + /** + * gets location of the feature which is denoted by <code>id</code> + * @param id feature id + * @return location of feature <code>id</code> + */ + public IPath getLocation(String id) { + TableEntry entry = (TableEntry) featureTable.get(id); + return entry == null ? null : entry.location; + } + + /** + * gets version of the feature which is denoted by <code>id</code> + * @param id feature id + * @return feature version(String) + */ + public String getVersion(String id) { + TableEntry entry = (TableEntry) featureTable.get(id); + return entry == null ? null : entry.model.getFeatureVersion(); + } + + /** + * gets status of the current FeatureModelTable object + * @return status + */ + public IStatus getStatus() { + return status; + } + + /** + * gets FeatureModel instance of the feature denoted by <code>id</code> + * @param id feature id + * @return Object instance which presents a FeatureModel instance + */ + public Object getFeatureModel(Object id) { + TableEntry entry = (TableEntry) featureTable.get(id); + return entry == null ? null : entry.model; + } + + /** + * gets number of FeatureModel instance included in the current FeatureModelTable instance + * @return number of FeatureModel instance + */ + public int size() { + return featureTable.size(); + } + + /** + * gets a Set view of the keys contained in the current FeatureModelTable instance + * @return a set view of keys contained in the current FeatureModelTable instance + */ + public Set getKeySet() { + return featureTable.keySet(); + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/FeatureVersionCompare.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/FeatureVersionCompare.java new file mode 100755 index 000000000..d057e0702 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/FeatureVersionCompare.java @@ -0,0 +1,811 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.File; +import java.net.URL; +import java.util.*; +import org.eclipse.core.runtime.*; +import org.eclipse.osgi.util.ManifestElement; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.tools.versioning.IVersionCompare; +import org.eclipse.update.core.IIncludedFeatureReference; +import org.eclipse.update.core.model.FeatureModel; +import org.eclipse.update.core.model.PluginEntryModel; +import org.osgi.framework.Version; + +/** + * VersionCompare + * + * @see org.eclipse.pde.tools.versioning.IVersionCompare + */ +public class FeatureVersionCompare implements VersionCompareConstants { + // MultiStatus instance used to store error or warning messages of features and plugin entries + private MultiStatus finalResult; + // Map used to store ICompreResult instances of verified features + private Map verifiedFeatureTable; + // Map used to store ICompreResult instances of verified plugins + private Map verifiedPluginTable; + // FeatureModelTables used to store FeatureModels + private FeatureModelTable featureModelTable1; + private FeatureModelTable featureModelTable2; + // + private PluginVersionCompare pluginVersionCompare; + // + private CompareOptionFileHelper compareOptionFileHelper; + // monitor related variables + private VersioningProgressMonitorWrapper monitorWrapper; + // + private boolean needPluginCompare; + // + private long startTime; + private boolean DEBUG = false; + private static final String DEBUG_OPTION = VersionCompareConstants.PLUGIN_ID + "/debug/features"; //$NON-NLS-1$ + + /** + * Constructor + */ + public FeatureVersionCompare() { + pluginVersionCompare = new PluginVersionCompare(); + DEBUG = Activator.getBooleanDebugOption(DEBUG_OPTION); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkFeatureVersions(URL, URL, boolean, Map) + */ + public IStatus checkFeatureVersions(URL configURL1, URL configURL2, boolean comparePlugins, File optionFile, IProgressMonitor monitor) throws CoreException { + return compareMain(configURL1, configURL2, comparePlugins, optionFile, monitor); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkFeatureVersions(java.lang.String, java.lang.String, boolean, Map) + */ + public IStatus checkFeatureVersions(String path1, String path2, boolean comparePlugins, File optionFile, IProgressMonitor monitor) throws CoreException { + return checkFeatureVersions(new File(path1), new File(path2), comparePlugins, optionFile, monitor); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkFeatureVersions(File, File, boolean, Map) + */ + public IStatus checkFeatureVersions(File file1, File file2, boolean comparePlugins, File optionFile, IProgressMonitor monitor) throws CoreException { + // check to see if we are pointing to two configuration files or two directories + if ((isConfiguration(file1) && isConfiguration(file2)) || (isFeaturesDirectory(file1) && isFeaturesDirectory(file2))) { + return compareMain(file1, file2, comparePlugins, optionFile, monitor); + } + return resultStatusHandler(IStatus.WARNING, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.FeatureVersionCompare_inputErrorMsg, file1, file2), null); + } + + /** + * compare main method + * @param object1 URL, String, File instance which denotes a directory or a configuration file + * @param object2 URL, String, File instance which denotes a directory or a configuration file + * @param comparePlugins <code>true</code>plugins need to be compared as object, <code>false</code> just to flat comparison + * @param optionFile File instance which denotes a compare option file + * @param monitor IProgressMonitor instance + * @return IStatus instance which contains message generated during comparing + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private IStatus compareMain(Object object1, Object object2, boolean comparePlugins, File optionFile, IProgressMonitor monitor) throws CoreException { + try { + monitor = VersioningProgressMonitorWrapper.monitorFor(monitor); + monitorWrapper = new VersioningProgressMonitorWrapper(monitor); + if (optionFile != null) + // generate CompareOptionFileHelper instance + compareOptionFileHelper = new CompareOptionFileHelper(optionFile); + // generate a MultiStatus instance to store compare result + finalResult = new MultiStatus(PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + // generate verified features and plugins maps + verifiedFeatureTable = new Hashtable(0); + verifiedPluginTable = new Hashtable(0); + this.needPluginCompare = comparePlugins; + // begin task + monitorWrapper.beginTask(Messages.FeatureVersionCompare_verifyingFeatureMsg, 100); + // generate feature model maps + featureModelTable1 = new FeatureModelTable(object1, compareOptionFileHelper); + monitorWrapper.worked(comparePlugins ? 5 : 30); + featureModelTable2 = new FeatureModelTable(object2, compareOptionFileHelper); + monitorWrapper.worked(comparePlugins ? 5 : 30); + if (!featureModelTable1.isOK()) { + finalResult.merge(featureModelTable1.getStatus()); + } + if (!featureModelTable2.isOK()) { + finalResult.merge(featureModelTable2.getStatus()); + } + // if featureModelTable1 or featureModelTable2 is empty, we return here + if (featureModelTable1.size() == 0 || featureModelTable2.size() == 0) { + return finalResult; + } + compareFeatures(); + monitorWrapper.worked(comparePlugins ? 90 : 40); + return finalResult; + } finally { + monitorWrapper.done(); + } + } + + /** + * check whether or not the given file potentially represents a platform configuration file on the file-system. + * + * @return <code>true</code> if <code>file</code> is a configuration file + * <code>false</code> otherwise + */ + private boolean isConfiguration(File file) { + return file.isFile() && CONFIGURATION_FILE_NAME.equalsIgnoreCase(new Path(file.getAbsolutePath()).lastSegment()); + } + + /** + * check whether or not the given file represents a features directory. + * + * @return <code>true</code> if <code>file</code> is a directory + * <code>false</code> otherwise + */ + private boolean isFeaturesDirectory(File file) { + return file.isDirectory(); + } + + /** + * Add the given compare result to the list of already processed features. + * + * @param result the compare result + */ + private void featureProcessed(CheckedItem result) { + verifiedFeatureTable.put(result.getSourceKey(), result); + } + + /** + * Add the given compare result to the list of already processed plugins. + * + * @param result the compare result + */ + private void pluginProcessed(CheckedItem result) { + verifiedPluginTable.put(result.getSourceKey()+KEY_SEPARATOR+ result.getDestinationKey(), result); + } + + /** + * Verifies every version of feature included in featureModelTable1 based on the feature which has the same feature + * id in featureModelTable2, and generates an overall result as an VersioningMultiStatus instance + * + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private void compareFeatures() throws CoreException { + startTime = System.currentTimeMillis(); + // compare features + for (Iterator keys = featureModelTable1.getKeySet().iterator(); keys.hasNext();) { + if (monitorWrapper.isCanceled()) + throw new OperationCanceledException(); + // get a key (feature ID) from the new features map + Object key = keys.next(); + FeatureModel newFeatureModel = (FeatureModel) (featureModelTable1.getFeatureModel(key)); + // check if the feature has been checked or not + if (searchFeatureResult(newFeatureModel.getFeatureIdentifier() + KEY_SEPARATOR + newFeatureModel.getFeatureVersion()) == null) { + // if the feature has not been checked, we check it and add the result to status list + featureProcessed(verifyNewFeatureVersion(newFeatureModel, (FeatureModel) featureModelTable2.getFeatureModel(key), monitorWrapper.getSubMonitor((needPluginCompare ? 90 : 40) / featureModelTable1.size()))); + } + } + debug("Feature Compare Time: " + (System.currentTimeMillis() - startTime) + " milliseconds"); //$NON-NLS-1$//$NON-NLS-2$ + debug("Total message number: " + finalResult.getChildren().length); //$NON-NLS-1$ + } + + /* + * Log the given debug message. + */ + private void debug(String message) { + if (DEBUG) + Activator.debug(message); + } + + /** + * Compares <code>featureModel1</code> to <code>featureModel2</code> to check if the version of <code>featureModel1</code> is correct or not; + * <code>featureModel1</code> and <code>featureModel2</code> have the same feature ID + * + * @param featureModel1 the FeatureModel instance + * @param featureModel2 the FeatureModel instance + * @return result of verification encapsulated in an IStatus instance + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private CheckedItem verifyNewFeatureVersion(FeatureModel featureModel1, FeatureModel featureModel2, IProgressMonitor monitor) throws CoreException { + // get feature id + String id = featureModel1.getFeatureIdentifier(); + // get versions of featureModel1 + Version version1 = new Version(featureModel1.getFeatureVersion()); + // check if featureModel2 is null, if it's null, we consider it as new added + if (featureModel2 == null) { + Object[] msg = {id, version1, featureModelTable1.getLocation(id)}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.FEATURE_OVERALL_STATUS, NLS.bind(Messages.FeatureVersionCompare_newIntroducedFeatureMsg, msg), null)); + return new CheckedItem(id + KEY_SEPARATOR + featureModel1.getFeatureVersion(), null, new Version(featureModel1.getFeatureVersion()), IVersionCompare.NEW_ADDED); + } + // get versions of featureModel2 + Version version2 = new Version(featureModel2.getFeatureVersion()); + // compare featureModel1 to featureModel2 + int result = checkFeatureChange(featureModel1, featureModel2, monitor); + // some versions of included features or plugins are wrong + if (result == IVersionCompare.ERROR_OCCURRED) { + Object[] msg = {id, version1, featureModelTable1.getLocation(id)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_OVERALL_STATUS, NLS.bind(Messages.FeatureVersionCompare_nestedErrorOccurredMsg, msg), null)); + return new CheckedItem(id + KEY_SEPARATOR + featureModel1.getFeatureVersion(), null, null, result); + } + // get the recommended version + Version recommendedVersion = recommendVersion(version2, result); + // check if version1 is correct or not + if (!isNewVersionValid(version1, recommendedVersion, result)) { + Object[] msg = new Object[] {id, version1, featureModelTable1.getLocation(id), recommendedVersion}; + if (result == IVersionCompare.QUALIFIER_CHANGE) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_OVERALL_STATUS, NLS.bind(Messages.FeatureVersionCompare_incorrectNewVersionMsg2, msg), null)); + } else { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_OVERALL_STATUS, NLS.bind(Messages.FeatureVersionCompare_incorrectNewVersionMsg, msg), null)); + } + return new CheckedItem(id + KEY_SEPARATOR + featureModel1.getFeatureVersion(), null, recommendedVersion, result); + }else{ + // check if version has been increased properly + int change = checkVersionChange(version1, version2); + if (change < result) { + Object[] msg = new Object[] {id, version1, featureModelTable1.getLocation(id), getVersionName(result), getVersionName(change)}; + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.FEATURE_OVERALL_STATUS, NLS.bind(Messages.FeatureVersionCompare_versionChangeIncorrectMsg, msg), null)); + return new CheckedItem(id + KEY_SEPARATOR + version1.toString(), null, version1, change); + } + return new CheckedItem(id + KEY_SEPARATOR + version1.toString(), null, version1, result); + } + } + + /** + * gets String which represent type of version denoted by <code>versionType</code> + * @param versionType int number + * @return String + */ + private String getVersionName(int versionType){ + switch (versionType){ + case IVersionCompare.MAJOR_CHANGE: return "Major"; //$NON-NLS-1$ + case IVersionCompare.MINOR_CHANGE: return "Minor"; //$NON-NLS-1$ + case IVersionCompare.MICRO_CHANGE: return "Micro"; //$NON-NLS-1$ + case IVersionCompare.QUALIFIER_CHANGE: return "Qualify"; //$NON-NLS-1$ + case IVersionCompare.NO_CHANGE: return "NoChange"; //$NON-NLS-1$ + default: return EMPTY_STRING; + } + } + + /** + * Compares included features and plugins in <code>featureModel1</code> to those in <code>featureModel2</code>. + * Check if there is any change from <code>featureModel2</code> to <code>feaureModel1</code> + * + * @param featureModel1 FeatureModel instance + * @param featureModel2 FeatureModel instance + * @return change with the highest priority from <code>featureModel2</code> to <code>featureModel1</code> + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private int checkFeatureChange(FeatureModel featureModel1, FeatureModel featureModel2, IProgressMonitor monitor) throws CoreException { + int overallChange = IVersionCompare.NO_CHANGE; + String id = featureModel1.getFeatureIdentifier(); + Version version1 = new Version(featureModel1.getFeatureVersion()); + Version version2 = new Version(featureModel2.getFeatureVersion()); + // compare feature version first + if (version1.compareTo(version2) < 0) { + Object[] msg = {id, version1, featureModelTable1.getLocation(id), version2, featureModelTable2.getLocation(id)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_OVERALL_STATUS, NLS.bind(Messages.FeatureVersionCompare_newVersionLowerThanOldMsg, msg), null)); + } + // get IncludedFeatureReference, PluginEntries of featureModel1 + IIncludedFeatureReference[] includedFeatures1 = featureModel1.getFeatureIncluded(); + PluginEntryModel[] pluginEntries1 = featureModel1.getPluginEntryModels(); + // get IncludedFeatureReference, PluginEntries of featureModel2 + IIncludedFeatureReference[] includedFeatures2 = featureModel2.getFeatureIncluded(); + PluginEntryModel[] pluginEntries2 = featureModel2.getPluginEntryModels(); + try { + monitor.beginTask(NLS.bind(Messages.FeatureVersionCompare_comparingFeatureMsg, id + UNDERSCORE_MARK + version1.toString()), 10); + // compare IncludedFeatureReferences in featureModel1 with those in featureModel2 + overallChange = Math.min(overallChange, compareReferences(id, includedFeatures1, includedFeatures2, monitorWrapper.getSubMonitor(5))); + // compare PluginEntries in featureModel1 with those in featureModel2 + overallChange = Math.min(overallChange, compareReferences(id, pluginEntries1, pluginEntries2, monitorWrapper.getSubMonitor(5))); + // return the version change with the highest priority + return overallChange; + } finally { + monitor.done(); + } + } + + /** + * Generates and returns a recommended version based on the given version and change. + * + * @param version the current version + * @param change the degree of change + * @return the recommended new version or <code>null</code> if the change is IVersionCompare.ERROR_OCCURRED + * or the change is not an expected value + */ + private Version recommendVersion(Version version, int change) { + // get major, minor, service of version + int major = version.getMajor(); + int minor = version.getMinor(); + int micro = version.getMicro(); + String qualifier = version.getQualifier(); + // update major, minor, service based on the compare result + switch (change) { + case IVersionCompare.ERROR_OCCURRED : + return null; + case IVersionCompare.MAJOR_CHANGE : { + major++; + minor = 0; + micro = 0; + qualifier = EMPTY_STRING; + break; + } + // NOT_EXIST, NEW_ADDED are currently treated as minor change + case IVersionCompare.NO_LONGER_EXIST : + case IVersionCompare.NEW_ADDED : + case IVersionCompare.MINOR_CHANGE : { + minor++; + micro = 0; + qualifier = EMPTY_STRING; + break; + } + case IVersionCompare.MICRO_CHANGE : { + micro++; + qualifier = EMPTY_STRING; + break; + } + case IVersionCompare.QUALIFIER_CHANGE : { + // TODO increase qualifier + break; + } + case IVersionCompare.NO_CHANGE : { + // if there is no change, just return the version + return version; + } + default : { + return null; + } + } + // generate recommended version + return new Version(major, minor, micro, qualifier); + } + + /** + * Checks if the <code>version1</code> is valid, based on <code>version2</code> and <code>change</code> + * from <code>version2</code> to <code>version1</code> + * + * @param version1 feature version + * @param version2 feature version + * @param change change from <code>version2</code> to <code>version1</code> + * @return <code>true</code> if version1 is correct and <code>false</code> otherwise + */ + private boolean isNewVersionValid(Version version1, Version version2, int change) { + if (change == IVersionCompare.QUALIFIER_CHANGE) { + // compare qualifier version. We currently don't increase qualifier version if there is + // QUALIFIER_CHANGE,we need the version1 be big than version2 + return version1.compareTo(version2) > 0; + } + // compare qualifier version + return version1.compareTo(version2) >= 0; + } + + /** + * Compares feature version(or plugin entry versions) in <code>references1</code> to those in <code>references2</code>. + * Check if there is any version change from <code>references2</code> to <code>references1</code>. The change with + * the highest priority will be returned + * + * @param parentID id of the feature which these features(or plugin entries) belong to + * @param references1 array of included plugins or features + * @param references2 array of included plugins or features + * @return change with the highest priority from <code>references2</code> to <code>references1</code> + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private int compareReferences(String parentID, Object[] references1, Object[] references2, IProgressMonitor monitor) throws CoreException { + try { + // generate Maps for references1 and references2 + Map referenceMap1 = generateTable(references1); + Map referenceMap2 = generateTable(references2); + // + if (referenceMap1 == null && referenceMap2 == null) { + return IVersionCompare.NO_CHANGE; + } + monitor.beginTask(Messages.FeatureVersionCompare_comparingReferenceMsg, referenceMap1 == null? 1 : referenceMap1.size() + 1); + // initialization before compare + int overallChange = IVersionCompare.NO_CHANGE; + if (referenceMap1 != null) { + // compares features(or plugin entries) in referenceMap1 to those in referenceMap2 + for (Iterator keys = referenceMap1.keySet().iterator(); keys.hasNext();) { + if (monitorWrapper.isCanceled()) + throw new OperationCanceledException(); + // get the next key (feature id) + Object id = keys.next(); + // get corresponding version from referenceMap1 + Version version1 = referenceMap1 == null ? null : (Version) (referenceMap1.get(id)); + // get the version of corresponding reference from referenceMap2 + Version version2 = referenceMap2 == null ? null : (Version) referenceMap2.get(id); + int currentChange = IVersionCompare.NO_CHANGE; + if (references1[0] instanceof IIncludedFeatureReference) { + currentChange = compareIncludedFeatureReference(parentID, (String) id, version1, version2, new SubProgressMonitor(monitor, 1)); + } else { + currentChange = compareIncludedPluginReference(parentID, (String) id, version1, version2, new SubProgressMonitor(monitor, 1)); + } + // we save the version change with higher priority + overallChange = Math.min(overallChange, currentChange); + // delete the reference from referenceMap2 + if (referenceMap2 != null) + referenceMap2.remove(id); + } + } + // new features or plugins added into the feature + if (referenceMap2 != null) { + for (Iterator keys = referenceMap2.keySet().iterator(); keys.hasNext();) { + Object id = keys.next(); + Version version = (Version) referenceMap2.get(id); + if (references2[0] instanceof IIncludedFeatureReference) { + Object[] msg = {FEATURE_TITLE, (String) id + UNDERSCORE_MARK + version.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_deletedFeaturePluginMsg, msg), null)); + } else { + Object[] msg = {PLUGIN_TITLE, (String) id + UNDERSCORE_MARK + version.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_deletedFeaturePluginMsg, msg), null)); + } + } + if (!referenceMap2.isEmpty()) { + overallChange = Math.min(overallChange, IVersionCompare.NO_LONGER_EXIST); + } + } + monitor.worked(1); + return overallChange; + } finally { + monitor.done(); + } + } + + /** + * compares two versions of an included plugins + * @param parentID ID of the feature to which the plugin denoted by <code>pluginID</code> belongs + * @param pluginID plugin id + * @param version1 new version of plugin denoted by <code>pluginID</code> + * @param version2 old version of plugin denoted by <code>pluginID</code> + * @return change happened on plugin denoted by <code>pluginID</code> + */ + private int compareIncludedPluginReference(String parentID, String pluginID, Version version1, Version version2, IProgressMonitor monitor) { + try { + monitor.beginTask("", 10); //$NON-NLS-1$ + int pluginChange = IVersionCompare.NO_CHANGE; + if (version2 == null) { + // if version2 is null, the plugin is a new added one + Object[] msg = {PLUGIN_TITLE, pluginID + UNDERSCORE_MARK + version1.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_newAddedFeaturePlingMsg, msg), null)); + //pluginChange = IVersionCompare.NEW_ADDED; + // the return was changed to a MINOR_CHANGE because adding a new plugin is considering a minor change + // and the value associated with NEW_ADDED indicates that it is a greater than minor change + pluginChange = IVersionCompare.MINOR_CHANGE; + } else { + if (version1.compareTo(version2) < 0) { + // if version1 is lower than version2, it is wrong + Object[] msg = new Object[] {version1, PLUGIN_TITLE, pluginID, parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID), version2, featureModelTable2.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_lowerNewVersion, msg), null)); + if (!needPluginCompare) + return IVersionCompare.ERROR_OCCURRED; + } + if (needPluginCompare) { + // Object level compare + String pluginKey1 = pluginID + KEY_SEPARATOR + version1.toString(); + String pluginKey2 = pluginID + KEY_SEPARATOR + version2.toString(); + // check if the plugin has been compared + CheckedItem pluginResult = searchPluginResult(pluginKey1 + pluginKey2); + monitor.worked(1); + if (pluginResult != null) { + pluginChange = pluginResult.getChange(); + } else { + // compare plugins as objects + IPath pluginDirPath1 = getPluginDirPath(featureModelTable1.getLocation(parentID)); + IPath pluginDirPath2 = getPluginDirPath(featureModelTable2.getLocation(parentID)); + Map pluginInfo1 = searchPlugin(pluginDirPath1, pluginID, version1.toString()); + Map pluginInfo2 = searchPlugin(pluginDirPath2, pluginID, version2.toString()); + monitor.worked(2); + // do plugin compare if both pluginPath1 and pluginPath2 are not null + if (pluginInfo1 != null && pluginInfo2 != null) { + try { + // compare two plugins + pluginChange = Math.min(pluginChange, pluginVersionCompare.checkPluginVersions(finalResult, ((IPath) pluginInfo1.get(BUNDLE_LOCATION)).toFile(), (ManifestElement[]) pluginInfo1.get(BUNDLE_CLASSPATH), ((IPath) pluginInfo2.get(BUNDLE_LOCATION)).toFile(), (ManifestElement[]) pluginInfo2.get(BUNDLE_CLASSPATH), new SubProgressMonitor(monitor, 7))); + } catch (CoreException ce) { + pluginChange = IVersionCompare.ERROR_OCCURRED; + } + if (pluginChange == IVersionCompare.ERROR_OCCURRED) { + // put the compare result into table, we don't need to compare it again if it is also included in another feature + pluginProcessed(new CheckedItem(pluginKey1, pluginKey2, null, pluginChange)); + // set message + Object[] msg = {pluginID + UNDERSCORE_MARK + version1.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_errorInVerifyPluginMsg, msg), null)); + // if result is error, set overallChange as error and continue to check next plugin + pluginChange = IVersionCompare.ERROR_OCCURRED; + } else { + // get recommended version based on compare result + Version recommendedVersion = recommendVersion(version2, pluginChange); + if (!isNewVersionValid(version1, recommendedVersion, pluginChange)) { + // if version1 is not correct, set message + Object[] msg = {pluginID, version1.toString(), ((IPath) pluginInfo1.get(BUNDLE_LOCATION)).toOSString(), recommendedVersion}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_incorrectPluginVersionMsg, msg), null)); + }else{ + // check if version has been increased properly + int actualChange = checkVersionChange(version1, version2); + if (actualChange < pluginChange){ + Object[] msg = new Object[] {pluginID, version1, getVersionName(pluginChange), getVersionName(actualChange)}; + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.FEATURE_OVERALL_STATUS, NLS.bind(Messages.FeatureVersionCompare_incorrectPluginVersionChange, msg), null)); + pluginChange = actualChange; + } + } + // put the compare result into table, we don't need to compare it again if it is also included in another feature + pluginProcessed(new CheckedItem(pluginKey1, pluginKey2,recommendedVersion, pluginChange)); + } + } else { + // if pluginPath1 or pluginPath2 is null, set messages + if (pluginInfo1 == null) { + Object[] msg = {pluginID + UNDERSCORE_MARK + version1.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID), pluginDirPath1}; + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_pluginNotFoundMsg, msg), null)); + } + if (pluginInfo2 == null) { + Object[] msg = {pluginID + UNDERSCORE_MARK + version2.toString(), parentID, featureModelTable2.getVersion(parentID), featureModelTable2.getLocation(parentID), pluginDirPath2}; + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_pluginNotFoundMsg, msg), null)); + } + pluginChange = IVersionCompare.ERROR_OCCURRED; + // put the compare into table, we don't need to compare it again if it is also included in another feature + pluginProcessed(new CheckedItem(pluginKey1, pluginKey2, null, pluginChange)); + } + } + } else { + // String level compare + pluginChange = checkVersionChange(version1, version2); + } + } + return pluginChange; + } finally { + monitor.done(); + } + } + + /** + * compares two versions of an included features + * @param parentID ID of the feature to which the feature denoted by <code>featureID</code> belongs + * @param featureID feature id + * @param version1 new version of feature denoted by <code>featureID</code> + * @param version2 old version of feature denoted by <code>featureID</code> + * @return change happened on feature denoted by <code>featureID</code> + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private int compareIncludedFeatureReference(String parentID, String featureID, Version version1, Version version2, IProgressMonitor monitor) throws CoreException { + String key1 = featureID + KEY_SEPARATOR + version1.toString(); + int change = IVersionCompare.NO_CHANGE; + // if version2 is null, the feature is a new added one + if (version2 == null) { + Object[] msg = {FEATURE_TITLE, featureID + UNDERSCORE_MARK + version1.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_newAddedFeaturePlingMsg, msg), null)); + change = IVersionCompare.NEW_ADDED; + } + // get the corresponding FeatureModel from featureModelTable1 + FeatureModel featureModel1 = (FeatureModel) featureModelTable1.getFeatureModel(featureID); + FeatureModel featureModel2 = (FeatureModel) featureModelTable2.getFeatureModel(featureID); + if (featureModel1 == null) { + // if the new FeatureModel has not been found, it's an error + Object[] msg = {featureID + UNDERSCORE_MARK + version1.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_sourceIncludedFeatureNotFoundMsg, msg), null)); + change = IVersionCompare.ERROR_OCCURRED; + } else if (!featureModel1.getFeatureVersion().equals(version1.toString())) { + //if the version of the FeatureModel we got does not match the version of the reference, it is an error + // e.g. feature f1 includes feature f2-1.0.0.v2006, but the corresponding FeatureModel we found is feature f2-1.0.1, it is wrong + Object[] msg = {featureID + UNDERSCORE_MARK + version1.toString(), parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_sourceIncludedFeatureNotFoundMsg, msg), null)); + change = IVersionCompare.ERROR_OCCURRED; + } + // if the feature is a new added one, we don't do further check + if (version2 != null) { + if (featureModel2 == null) { + // if featureModel2 is null, it is an error + Object[] msg = {featureID + UNDERSCORE_MARK + version2.toString(), parentID, featureModelTable2.getVersion(parentID), featureModelTable2.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_destIncludedFeatureNotFoundMsg, msg), null)); + change = IVersionCompare.ERROR_OCCURRED; + } else if (!featureModel2.getFeatureVersion().equals(version2.toString())) { + //if the version of the FeatureModel we got does not match the version of the reference, it is an error + // e.g. feature f1 includes feature f2-1.0.0.v2006, but the corresponding FeatureModel we found is feature f2-1.0.1, it is wrong + Object[] msg = {featureID + UNDERSCORE_MARK + version2.toString(), parentID, featureModelTable2.getVersion(parentID), featureModelTable2.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_destIncludedFeatureNotFoundMsg, msg), null)); + change = IVersionCompare.ERROR_OCCURRED; + } + // String level compare + if (version1.compareTo(version2) < 0) { + Object[] msg = new Object[] {version1, FEATURE_TITLE, featureID, parentID, featureModelTable1.getVersion(parentID), featureModelTable1.getLocation(parentID), version2, featureModelTable2.getVersion(parentID), featureModelTable1.getLocation(parentID)}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.FEATURE_DETAIL_STATUS, NLS.bind(Messages.FeatureVersionCompare_lowerNewVersion, msg), null)); + } + if (change == IVersionCompare.ERROR_OCCURRED) { + featureProcessed(new CheckedItem(key1, null, null, IVersionCompare.ERROR_OCCURRED)); + return IVersionCompare.ERROR_OCCURRED; + } + // check if the current feature version has been checked + CheckedItem compareResult = searchFeatureResult(key1); + // Object level compare + if (compareResult == null) { + // check the included feature models and get result status + compareResult = verifyNewFeatureVersion(featureModel1, featureModel2, monitorWrapper.getSubMonitor(needPluginCompare ? 90 : 40 / featureModelTable1.size())); + // add the result status into result list + featureProcessed(compareResult); + } + change = Math.min(change, compareResult.getChange()); + } + return change; + } + + /* + * Return a Map contains bundle name, bundle version, bundle classpath, and bundle location + * if the location of the specific plug-in version that we are looking for. Otherwise return + * <code>null</code> + */ + private Map matchesPlugin(File file, String pluginName, String pluginVersion) throws CoreException { + if (!file.exists()) + return null; + Map manifest = ManifestHelper.getElementsFromManifest(file, new String[] {BUNDLE_SYMBOLICNAME, BUNDLE_VERSION, BUNDLE_CLASSPATH}); + ManifestElement[] elements = (ManifestElement[]) manifest.get(BUNDLE_SYMBOLICNAME); + if (elements == null) + return null; + String name = elements[0].getValue(); + if (name == null || !name.equals(pluginName)) + return null; + elements = (ManifestElement[]) manifest.get(BUNDLE_VERSION); + if (elements == null) + return null; + String version = elements[0].getValue(); + if (version == null || !version.equals(pluginVersion)) + return null; + manifest.put(BUNDLE_LOCATION, new Path(file.getAbsolutePath())); + return manifest; + } + + /** + * searches for plugin denoted by <code>pluginName</code> and <code>pluginVersion</code> under + * plugin directory indicated by <code>plugDir</code>, and return a Map contains bundle name, + * bundle version, bundle classpath, and bundle location if we find, return <code>null</code> + * if we don't find + * + * @param plugDir plugin directory + * @param pluginName plugin name + * @param pluginVersion plugin version + * @return full path of plugin + */ + private Map searchPlugin(IPath plugDir, String pluginName, String pluginVersion) { + // as an optimization first check to see if the plug-in directory name is the Eclipse-format + try { + // check to see if we can guess the JAR name + File jar = plugDir.append(pluginName + UNDERSCORE_MARK + pluginVersion + DOT_MARK + JAR_FILE_EXTENSION).toFile(); + Map manifest = matchesPlugin(jar, pluginName, pluginVersion); + if (manifest != null) + return manifest; + // didn't match the JAR so try and match the directory name + File dir = plugDir.append(pluginName + UNDERSCORE_MARK + pluginVersion).toFile(); + manifest = matchesPlugin(dir, pluginName, pluginVersion); + if (manifest != null) + return manifest; + } catch (CoreException e) { + // ignore and catch below if re-thrown + } + File[] plugins = plugDir.toFile().listFiles(); + if (plugins == null) + return null; + for (int i = 0; i < plugins.length; i++) { + try { + Map manifest = matchesPlugin(plugins[i], pluginName, pluginVersion); + if (manifest != null) + return manifest; + } catch (CoreException ce) { + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.PluginVersionCompare_couldNotReadManifestMsg, plugins[i].getAbsolutePath()), null)); + } + } + return null; + } + + /** + * generates a map which stores the key-value pairs; Feature id(or plugin id) is key and version is value; + * or <code>null</code> when <code>objects</code> is empty + * + * @param objects array of feature or plugin entry objects + * @return Map instance contains id-version pairs or <code>null</code> + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private Map generateTable(Object[] objects) throws CoreException { + if (objects == null || objects.length == 0) { + return null; + } + Hashtable hashTable = new Hashtable(); + // set the ids and versions of plugins into hashtable, id is key, version is value + if (objects[0] instanceof IIncludedFeatureReference) { + for (int i = 0; i < objects.length; i++) + if (objects[i] != null && (compareOptionFileHelper == null ? true : compareOptionFileHelper.shouldCompare(objects[i]))) + hashTable.put(((IIncludedFeatureReference) objects[i]).getVersionedIdentifier().getIdentifier(), new Version(((IIncludedFeatureReference) objects[i]).getVersionedIdentifier().getVersion().toString())); + } else if (objects[0] instanceof PluginEntryModel) { + for (int i = 0; i < objects.length; i++) + if (objects[i] != null && (compareOptionFileHelper == null ? true : compareOptionFileHelper.shouldCompare(objects[i]))) + hashTable.put(((PluginEntryModel) objects[i]).getPluginIdentifier(), new Version(((PluginEntryModel) objects[i]).getPluginVersion())); + } + return hashTable; + } + + /** + * Checks if there is any change from <version2> to <version1> + * + * @param version1 feature version + * @param version2 feature version + * @return compare result indicates change in major, minor, micro, qualifier ,no change or error if <code>version1</code> is lower than <code>version2</code> + */ + private int checkVersionChange(Version version1, Version version2) { + // overall compare + if (version1.compareTo(version2) < 0) { + return IVersionCompare.ERROR_OCCURRED; + } + // compare major version + if (version1.getMajor() > version2.getMajor()) { + return IVersionCompare.MAJOR_CHANGE; + } + // compare minor version + if (version1.getMinor() > version2.getMinor()) { + return IVersionCompare.MINOR_CHANGE; + } + // compare micro version + if (version1.getMicro() > version2.getMicro()) { + return IVersionCompare.MICRO_CHANGE; + } + // compare qualifier version + if (version1.getQualifier().compareTo(version2.getQualifier()) > 0) { + return IVersionCompare.QUALIFIER_CHANGE; + } + // if we got to this point, it means no change + return IVersionCompare.NO_CHANGE; + } + + /** + * Returns the compare result for the feature with the specified key. Returns <code>null</code> + * if it is not found. + * + * @param key FeatureKey instance (feature id + "#" + feature version) + * @return the compare result or <code>null</code> + */ + private CheckedItem searchFeatureResult(Object key) { + return (CheckedItem) verifiedFeatureTable.get(key); + } + + /** + * Returns the compare result for the plugin with the specified key. Returns <code>null</code> + * if it is not found. + * + * @param key compare source plugin key instance (plugin id + "#" + plugin version) + "#" + + * compare destination plugin key instance (plugin id + "#" + plugin version) + * @return the compare result or <code>null</code> if not found + */ + private CheckedItem searchPluginResult(Object key) { + return (CheckedItem) verifiedPluginTable.get(key); + } + + /** + * Return a new status object populated with the given information. + * + * @param severity severity of status + * @param code indicates type of this IStatus instance, it could be one of: FEATURE_OVERALL_STATUS, + * FEATURE_DETAIL_STATUS, PLUGIN_OVERALL_STATUS, PLUGIN_DETAIL_STATUS, PROCESS_ERROR_STATUS, + * CLASS_OVERALL_STATUS, CLASS_DETAIL_STATUS + * @param message the status message + * @param exception exception which has been caught, or <code>null</code> + * @return the new status object + */ + private IStatus resultStatusHandler(int severity, int code, String message, Exception exception) { + if (message == null) { + if (exception != null) + message = exception.getMessage(); + // extra check because the exception message can be null + if (message == null) + message = EMPTY_STRING; + } + return new Status(severity, PLUGIN_ID, code, message, exception); + } + + /** + * get path of "plugins/" based on the given feature file path + * @param featurePath path of a "feature.xml" file + * @return path of "plugins/" + */ + private IPath getPluginDirPath(IPath featurePath) { + IPath path = featurePath.removeLastSegments(3); + return path.append(PLUGIN_DIR_NAME); + } +}
\ No newline at end of file diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/JavaClassVersionCompare.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/JavaClassVersionCompare.java new file mode 100755 index 000000000..26a7fd9ff --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/JavaClassVersionCompare.java @@ -0,0 +1,654 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.InputStream; +import java.util.*; +import org.eclipse.core.runtime.*; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.util.*; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.tools.versioning.IVersionCompare; + +/** + * ClassSourceVersionCompare + */ +public class JavaClassVersionCompare implements VersionCompareConstants { + // MultiStatus instance used to store IStatus objects which indicate changes between two java classes + private MultiStatus finalResult; + // + private boolean hasMajorChange; + private boolean hasMinorChange; + private boolean hasMicroChange; + private boolean hasError; + + /** + * Constructor + */ + public JavaClassVersionCompare() { + super(); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.internal.versioning.VersionCompareDispatcher#checkJavaClassVersions(String, String, IProgressMonitor) + */ + public int checkJavaClassVersions(MultiStatus status, Object javaClassObj1, Object javaClassObj2, IProgressMonitor monitor) throws CoreException { + try { + monitor = VersioningProgressMonitorWrapper.monitorFor(monitor); + monitor.beginTask(Messages.JavaClassVersionCompare_comparingClassMsg, 100); + finalResult = status; + // get IClassFileReader instances of javaClassObj1 and javaClassObj2 + IClassFileReader classFileReader1 = ClassFileHelper.getReader(javaClassObj1); + if (classFileReader1 == null) { + if (javaClassObj1 instanceof InputStream) + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, Messages.JavaClassVersionCompare_inputStreamErrMsg, null)); + else + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.JavaClassVersionCompare_classFileNotLoadedMsg, javaClassObj1), null)); + return IVersionCompare.ERROR_OCCURRED; + } + IClassFileReader classFileReader2 = ClassFileHelper.getReader(javaClassObj2); + // worked 5% + monitor.worked(5); + if (monitor.isCanceled()) + throw new OperationCanceledException(); + if (classFileReader2 == null) { + if (javaClassObj2 instanceof InputStream) + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, Messages.JavaClassVersionCompare_inputStreamErrMsg, null)); + else + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.JavaClassVersionCompare_classFileNotLoadedMsg, javaClassObj2), null)); + return IVersionCompare.ERROR_OCCURRED; + } + // worked 5% + monitor.worked(5); + if (monitor.isCanceled()) + throw new OperationCanceledException(); + // initialize flags + hasError = false; + hasMajorChange = false; + hasMinorChange = false; + hasMicroChange = false; + // compare two classes ( 90% workload) + compareJavaClasses(classFileReader1, classFileReader2, new SubProgressMonitor(monitor, 90)); + // analysis result + if (hasError) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.CLASS_OVERALL_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.JavaClassVersionCompare_classErrorOccurredMsg, charsToString(classFileReader1.getClassName())), null)); + return IVersionCompare.ERROR_OCCURRED; + } + if (hasMajorChange) { + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_OVERALL_STATUS, NLS.bind(Messages.JavaClassVersionCompare_classMajorChangeMsg, charsToString(classFileReader1.getClassName())), null)); + return IVersionCompare.MAJOR_CHANGE; + } + if (hasMinorChange) { + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_OVERALL_STATUS, NLS.bind(Messages.JavaClassVersionCompare_classMinorChangeMsg, charsToString(classFileReader1.getClassName())), null)); + return IVersionCompare.MINOR_CHANGE; + } + if (hasMicroChange) { + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_OVERALL_STATUS, NLS.bind(Messages.JavaClassVersionCompare_classMicroChange, charsToString(classFileReader1.getClassName())), null)); + return IVersionCompare.MICRO_CHANGE; + } + return IVersionCompare.NO_CHANGE; + } finally { + monitor.done(); + } + } + + /** + * compares two java classes denoted by <code>classFileReader1</code> and <code>classFileReader2</code>, + * and check if there is any change from <code>classFileReader2</code> to <code>classFileReader1</code>. + * + * @param classFileReader1 + * @param classFileReader2 + * @param monitor IProgressMonitor instance + * @return compare result IStatus instance + */ + private IStatus compareJavaClasses(IClassFileReader classFileReader1, IClassFileReader classFileReader2, IProgressMonitor monitor) { + try { + monitor.beginTask("", 100); //$NON-NLS-1$ + // compare class names + String name1 = charsToString(classFileReader1.getClassName()); + String name2 = charsToString(classFileReader2.getClassName()); + if (!name1.equals(name2)) { + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.JavaClassVersionCompare_differentClassNameMsg, name1, name2), null)); + return finalResult; + } + // worked 5% + monitor.worked(5); + // compare super classes + checkSuperClasses(name1, classFileReader1.getSuperclassName(), classFileReader2.getSuperclassName()); + // worked 5% + monitor.worked(5); + // compare interfaces + checkInterfaces(name1, generateList(classFileReader1.getInterfaceNames()), generateList(classFileReader2.getInterfaceNames())); + // worked 5% + monitor.worked(5); + // compare modifier of classes + checkClassModifiers(name1, classFileReader1.getAccessFlags(), classFileReader2.getAccessFlags()); + // worked 5% + monitor.worked(5); + // compare fields + checkElements(name1, generateTable(classFileReader1.getFieldInfos()), generateTable(classFileReader2.getFieldInfos())); + // worked 40% + monitor.worked(40); + // compare methods (40%) + checkElements(name1, generateTable(classFileReader1.getMethodInfos()), generateTable(classFileReader2.getMethodInfos())); + return finalResult; + } finally { + monitor.done(); + } + } + + /** + * checks if super class has been changed + * + * @param className name of the class we are comparing + * @param superClassName1 + * @param superClassName2 + */ + private void checkSuperClasses(String className, char[] superClassName1, char[] superClassName2) { + if (!charsToString(superClassName1).equals(charsToString(superClassName2))) { + Object[] msg = {className, charsToString(superClassName2), charsToString(superClassName1)}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_differentSuperClassMsg, msg), null)); + } + } + + /** + * checks if there is any change between <code>interfaceList1</code> and <code>interfaceList2</code> + * + * @param className name of the class we are comparing + * @param interfaceList1 List of interface name + * @param interfaceList2 List of interface name + */ + private void checkInterfaces(String className, List interfaceList1, List interfaceList2) { + for (Iterator nameIterator1 = interfaceList1.iterator(); nameIterator1.hasNext();) { + // get a name of interface from interfaceNames1 + String name1 = (String) nameIterator1.next(); + // check if the interface is in interfaceNames2 + if (!interfaceList2.remove(name1)) + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_newAddedInterfaceMsg, name1, className), null)); + } + // check what does no longer exist + for (Iterator nameIterator2 = interfaceList2.iterator(); nameIterator2.hasNext();) { + String name2 = (String) nameIterator2.next(); + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_deletedInterfaceMsg, name2, className), null)); + } + } + + /** + * checks two class modifiers to see if there is any change from <code>accessFlag2</code> to <code>accessFlag1</code> + * @param accessFlag1 + * @param accessFlag2 + */ + private void checkClassModifiers(String className, int accessFlag1, int accessFlag2) { + int change = accessFlag1 ^ accessFlag2; + if (change != 0) { + // if the visibility of a method has been narrowed, it is a change should be caught + Object[] msg = {className, createChangedModifierString(accessFlag2, change), createChangedModifierString(accessFlag1, change)}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_classModifierChangedMsg, msg), null)); + } + } + + /** + * checks corresponding elements(IMethodInfo instances or IFieldInfo instances), + * to see if there is any change + * @param className className name of class to which values in <code>elementMap1</code> + * (and <code>elementMap2</code>) belong + * @param elementMap1 map contains IMethodInfo instances or IFieldInfo instances + * @param elementMap2 map contains IMethodInfo instances or IFieldInfo instances + */ + private void checkElements(String className, Map elementMap1, Map elementMap2) { + for (Iterator iterator1 = elementMap1.keySet().iterator(); iterator1.hasNext();) { + // get a key and value from methodMap1 + String key = (String) iterator1.next(); + Object value1 = elementMap1.get(key); + // try to get the corresponding method from methodMap2 + Object value2 = elementMap2.get(key); + if (value2 != null) { + if (value1 instanceof IMethodInfo) + // compare the corresponding methods + compareMethodInfos(className, (IMethodInfo) value1, (IMethodInfo) value2); + else + // compare the corresponding fields in fieldInfoMap1 and fieldInfoMap2 + compareFieldInfos(className, (IFieldInfo) value1, (IFieldInfo) value2); + elementMap2.remove(key); + } else { + if (value1 instanceof IMethodInfo) { + if (Flags.isPublic(((IMethodInfo) value1).getAccessFlags()) || Flags.isProtected(((IMethodInfo) value1).getAccessFlags())) { + Object[] msg = new Object[] {METHOD_TITLE, getSignatureString(value1), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MINOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_newAddedMsg, msg), null)); + } + } else { + if (Flags.isPublic(((IFieldInfo) value1).getAccessFlags()) || Flags.isProtected(((IFieldInfo) value1).getAccessFlags())) { + Object[] msg = new Object[] {FIELD_TITLE, getSignatureString(value1), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MINOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_newAddedMsg, msg), null)); + } + } + } + } + // if there are anythings left in fieldInfoMap1, they no longer exist in the class + for (Iterator iterator2 = elementMap2.values().iterator(); iterator2.hasNext();) { + Object object = iterator2.next(); + if (object instanceof IMethodInfo) { + // we only care which is protected or public + if (Flags.isPublic(((IMethodInfo) object).getAccessFlags()) || Flags.isProtected(((IMethodInfo) object).getAccessFlags())) { + Object[] msg = new Object[] {METHOD_TITLE, getSignatureString(object), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_noLongerExistMsg, msg), null)); + } + } else { + if (Flags.isPublic(((IFieldInfo) object).getAccessFlags()) || Flags.isProtected(((IFieldInfo) object).getAccessFlags())) { + Object[] msg = new Object[] {FIELD_TITLE, getSignatureString(object), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_noLongerExistMsg, msg), null)); + } + } + + } + } + + /** + * compares two IMethodInfo instances to find any change from <code>method2</code> to <code>method1</code> + * + * @param className name of class to which <code>method1</code>(and <code>method2</code>) belongs + * @param method1 IMethodInfo instance + * @param method2 IMethodInfo instance + */ + private void compareMethodInfos(String className, IMethodInfo method1, IMethodInfo method2) { + // compare AccessFlags (AccessFlags defines the modifiers of a method (e.g. public static final compareTo()) + int accessFlag1 = method1.getAccessFlags(); + int accessFlag2 = method2.getAccessFlags(); + // we just care about field who was public or protected + if (!(Flags.isPublic(accessFlag2) || Flags.isProtected(accessFlag2))) + return; + if (isModifierNarrowed(accessFlag1, accessFlag2)) { + // get what modifiers have been changed + int change = accessFlag1 ^ accessFlag2; + // if the visibility of a method has been narrowed, it is a change should be caught + Object[] msg = {METHOD_TITLE, getSignatureString(method1), createChangedModifierString(accessFlag2, change), createChangedModifierString(accessFlag1, change), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_ModifierChangedMsg, msg), null)); + } + // compare isDeprecated(if a method has been deprecated) + if (method1.isDeprecated() && !method2.isDeprecated()) { + Object[] msg = {METHOD_TITLE, getSignatureString(method1), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MICRO_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_deprecatedChangedMsg, msg), null)); + } + // compare exceptions + IExceptionAttribute exceptionAtrributes1 = method1.getExceptionAttribute(); + IExceptionAttribute exceptionAtrributes2 = method2.getExceptionAttribute(); + checkExceptions(className, getSignatureString(method1), exceptionAtrributes1, exceptionAtrributes2); + } + + /** + * check two IExceptionAttribute instances to see if there is any change + * @param className name of the class to which the method denoted by <code>methodName</code> belongs + * @param methodName name of the method to which exceptionsAttribute1(exceptionsAttribute2) belongs + * @param exceptionsAttribute1 + * @param exceptionsAttribute2 + */ + private void checkExceptions(String className, String methodName, IExceptionAttribute exceptionsAttribute1, IExceptionAttribute exceptionsAttribute2) { + List exceptionList1 = null; + List exceptionList2 = null; + if (exceptionsAttribute1 != null && exceptionsAttribute2 != null) { + exceptionList1 = generateList(exceptionsAttribute1.getExceptionNames()); + exceptionList2 = generateList(exceptionsAttribute2.getExceptionNames()); + } else if (exceptionsAttribute1 != null) { + exceptionList1 = generateList(exceptionsAttribute1.getExceptionNames()); + } else if (exceptionsAttribute2 != null) { + exceptionList2 = generateList(exceptionsAttribute2.getExceptionNames()); + } else { + return; + } + StringBuffer newAddedExceptions = new StringBuffer(); + if (exceptionList1 != null) { + // check what is new added + for (Iterator iterator1 = exceptionList1.iterator(); iterator1.hasNext();) { + // get a exception from exceptionList1 + String exception1 = (String) iterator1.next(); + if (exceptionList2 != null) { + // check if the exception is in exceptionList2 + if (!exceptionList2.remove(exception1)) { + newAddedExceptions.append(exception1); + newAddedExceptions.append(COMMA_MARK); + } + } else { + // if exceptionList2 is null, it is new added + newAddedExceptions.append(exception1); + newAddedExceptions.append(COMMA_MARK); + } + } + if (!newAddedExceptions.toString().trim().equals(EMPTY_STRING)) { + Object[] msg = {newAddedExceptions.toString(), methodName, className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MINOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_newAddedExceptionMsg, msg), null)); + } + } + if (exceptionList2 != null) { + StringBuffer notExistExceptions = new StringBuffer(); + // check what does no longer exist + for (Iterator iterator2 = exceptionList2.iterator(); iterator2.hasNext();) { + String exception2 = (String) iterator2.next(); + notExistExceptions.append(exception2); + notExistExceptions.append(COMMA_MARK); + } + if (!notExistExceptions.toString().trim().equals(EMPTY_STRING)) { + Object[] msg2 = {notExistExceptions.toString(), methodName, className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MINOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_noLongerExistExceptionMsg, msg2), null)); + } + } + } + + /** + * checks if there is any change from <code>accessFlag2</code> to <code>accessFlag1</code> + * we check changes which narrowed down the visibility of a field or method with protected or public modifier + * + * @param accessFlag1 denotes combined modifiers of a field or method + * @param accessFlag2 denotes combined modifiers of a field or method + * @return <code>true</code> if visibility has been narrowed down from <code>accessFlag2</code> to <code>accessFlag1</code>, + * <code>false</code> otherwise + */ + private boolean isModifierNarrowed(int accessFlag1, int accessFlag2) { + if (Flags.isProtected(accessFlag2)) { + // from protected to modifier other than public and protected + if (!(Flags.isPublic(accessFlag1) || Flags.isProtected(accessFlag1))) { + return true; + } + } else if (Flags.isPublic(accessFlag2)) { + // from public to modifier other than public + if (!Flags.isPublic(accessFlag1)) { + return true; + } + } else { + // only check which was protected or public + return false; + } + // from non-final to final + if (!Flags.isFinal(accessFlag2) && Flags.isFinal(accessFlag1)) { + return true; + } + // from static to non-static + if (Flags.isStatic(accessFlag2) && !Flags.isStatic(accessFlag1)) { + return true; + } + // from non-abstract to abstract + if (!Flags.isAbstract(accessFlag2) && Flags.isAbstract(accessFlag1)) { + return true; + } + // volatile modifier changed + if (Flags.isVolatile(accessFlag2) != Flags.isVolatile(accessFlag1)) { + return true; + } + return false; + } + + /** + * check if <code>accessFlag</code> is <code>default</code> + * @param accessFlag + * @return <code>true</code> if <code>addessFlag</code> is <code>default</code>, + * <code>false</code> otherwise + */ + private boolean isDefault(int accessFlag) { + return (accessFlag & DEFAULT_MODIFIER_TESTER) == 0 ? true : false; + } + + /** + * compares two IFieldInfo instances to find any change from <code>field1</code> to <code>field2</code> + * + * @param className name of class to which <code>field1</code>(and <code>field2</code>) belongs + * @param field1 IFieldInfo instance + * @param field2 IFieldInfo instance + */ + private void compareFieldInfos(String className, IFieldInfo field1, IFieldInfo field2) { + // compare AccessFlags (AccessFlags defines the modifiers of a field (e.g. public static final IVersionCompare.PLUGIN_ID = "org.eclipse.pde.tools.versioning") + int accessFlag1 = field1.getAccessFlags(); + int accessFlag2 = field2.getAccessFlags(); + // we just care about field who was public or protected + if (!(Flags.isPublic(accessFlag2) || Flags.isProtected(accessFlag2))) { + return; + } + if (isModifierNarrowed(accessFlag1, accessFlag2)) { + // get what modifiers have been changed + int change = accessFlag1 ^ accessFlag2; + // if the visibility of a field which was public or protected has been narrowed, it is a change should be caught + Object[] msg = {FIELD_TITLE, charsToString(field1.getName()), createChangedModifierString(accessFlag2, change), createChangedModifierString(accessFlag1, change), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_ModifierChangedMsg, msg), null)); + } + // compare Descriptor (Descriptor describes the type of a field) + String descriptor1 = charsToString(field1.getDescriptor()); + String descriptor2 = charsToString(field2.getDescriptor()); + if (!descriptor1.equals(descriptor2)) { + String[] msg = {charsToString(field1.getName()), Signature.toString(descriptor2), Signature.toString(descriptor1), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_descriptorChangedMsg, msg), null)); + } + // compare isDeprecated (if a field has been deprecated) + if (field1.isDeprecated() && !field2.isDeprecated()) { + Object[] msg = {FIELD_TITLE, charsToString(field1.getName()), className}; + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_DETAIL_STATUS | IVersionCompare.MICRO_CHANGE, NLS.bind(Messages.JavaClassVersionCompare_deprecatedChangedMsg, msg), null)); + } + } + + /** + * creates modifier string which includes multiple modifiers(e.g. "public static final") + * + * @param accessFlags accessFlags of field or method + * @param changedFlags indicates change on accessFlags + * @return modifier string + */ + private String createChangedModifierString(int accessFlags, int changedFlags) { + StringBuffer buffer = new StringBuffer(); + if (Flags.isPublic(changedFlags)) { + if (Flags.isPublic(accessFlags)) { + buffer.append(PUBLIC_STRING); + buffer.append(SPACE); + } else if (isDefault(accessFlags)) { + buffer.append(DEFAULT_STRING); + buffer.append(SPACE); + } + } + if (Flags.isProtected(changedFlags)) { + if (Flags.isProtected(accessFlags)) { + buffer.append(PROTECTED_STRING); + buffer.append(SPACE); + } else if (isDefault(accessFlags)) { + buffer.append(DEFAULT_STRING); + buffer.append(SPACE); + } + } + if (Flags.isPrivate(changedFlags)) { + if (Flags.isPrivate(accessFlags)) { + buffer.append(PRIVATE_STRING); + buffer.append(SPACE); + } else if (isDefault(accessFlags)) { + buffer.append(DEFAULT_STRING); + buffer.append(SPACE); + } + } + if (Flags.isStatic(changedFlags)) { + if (Flags.isStatic(accessFlags)) { + buffer.append(STATIC_STRING); + buffer.append(SPACE); + } else { + buffer.append(NON_STATIC_STRING); + buffer.append(SPACE); + } + } + if (Flags.isFinal(changedFlags)) { + if (Flags.isFinal(accessFlags)) { + buffer.append(FINAL_STRING); + buffer.append(SPACE); + } else { + buffer.append(NON_FINAL_STRING); + buffer.append(SPACE); + } + } + if (Flags.isAbstract(changedFlags)) { + if (Flags.isAbstract(accessFlags)) { + buffer.append(ABSTRACT_STRING); + buffer.append(SPACE); + } else { + buffer.append(NON_ABSTRACT_STRING); + buffer.append(SPACE); + } + } + if (Flags.isVolatile(changedFlags)) { + if (Flags.isVolatile(accessFlags)) { + buffer.append(VOLATILE_STRING); + buffer.append(SPACE); + } else { + buffer.append(NON_VOLATILE_STRING); + buffer.append(SPACE); + } + } + return buffer.toString().trim(); + } + + /** + * converts char array to String + * @param chars array of char + * @return String which represents the content of <code>chars</code> + */ + private String charsToString(char[] chars) { + return new String(chars); + } + + /** + * generates a List which stores instances in array <code>objects</code> + * + * @param objects instance objects + * @return List + */ + private List generateList(Object[] objects) { + ArrayList list = new ArrayList(0); + if (objects == null || objects.length == 0) + return list; + if (objects[0] instanceof char[]) + for (int i = 0; i < objects.length; i++) + list.add(charsToString((char[]) objects[i])); + else + for (int i = 0; i < objects.length; i++) + list.add(objects[i]); + return list; + } + + /** + * converts the given object signature to a readable string + * @param object IFieldInfo instance of IMethodInfo instance + * @return String type signature + */ + private String getSignatureString(Object object) { + StringBuffer buffer = new StringBuffer(); + if (object instanceof IMethodInfo) { + IMethodInfo method = (IMethodInfo) object; + buffer.append(Flags.toString(method.getAccessFlags())); + buffer.append(SPACE); + buffer.append(Signature.toString(charsToString(method.getDescriptor()), charsToString(method.getName()), null, false, true)); + } else { + IFieldInfo field = (IFieldInfo) object; + buffer.append(Flags.toString(field.getAccessFlags())); + buffer.append(SPACE); + buffer.append(Signature.toString(charsToString(field.getDescriptor()))); + buffer.append(SPACE); + buffer.append(charsToString(field.getName())); + } + return buffer.toString(); + } + + /** + * generates a map which stores IField instances or IMethod instances + * + * @param objects array of IMethodInfo or IFieldInfo instances + * @return Map instance contains IField instances or IMethod instances + */ + private Map generateTable(Object[] objects) { + Hashtable hashTable = new Hashtable(0); + if (objects == null || objects.length == 0) { + return hashTable; + } + // set hashtable + if (objects[0] instanceof IFieldInfo) { + for (int i = 0; i < objects.length; i++) { + if (objects[i] != null) + hashTable.put(charsToString(((IFieldInfo) objects[i]).getName()), objects[i]); + } + } else if (objects[0] instanceof IMethodInfo) { + for (int i = 0; i < objects.length; i++) { + if (objects[i] != null) + hashTable.put(getMethodKey((IMethodInfo) objects[i]), objects[i]); + } + } + return hashTable; + } + + /** + * get key of a method. Key of a method is a String which + * includes the retype of the method,name of the method and the parameter types of the method + * (e.g. key of public int foo(int , String) is "int#foo#int#String" + * @param method + * @return key of a method + */ + private String getMethodKey(IMethodInfo method) { + StringBuffer buffer = new StringBuffer(); + // append return type + buffer.append(method.getDescriptor()); + // append name of method + buffer.append(charsToString(method.getName())); + char[][] parameters = Signature.getParameterTypes(method.getDescriptor()); + // append parameter types + if (parameters != null) { + for (int i = 0; i < parameters.length; i++) { + buffer.append(KEY_SEPARATOR); + buffer.append(charsToString(parameters[i])); + } + } + return buffer.toString(); + } + + /** + * Return a new status object populated with the given information. + * + * @param severity severity of status + * @param code indicates type of this IStatus instance, it could be one of: FEATURE_OVERALL_STATUS, + * FEATURE_DETAIL_STATUS, PLUGIN_OVERALL_STATUS, PLUGIN_DETAIL_STATUS, PROCESS_ERROR_STATUS, + * CLASS_OVERALL_STATUS, CLASS_DETAIL_STATUS + * @param message the status message + * @param exception exception which has been caught, or <code>null</code> + * @return the new status object + */ + private IStatus resultStatusHandler(int severity, int code, String message, Exception exception) { + processed(code); + + if (message == null) { + if (exception != null) + message = exception.getMessage(); + // extra check because the exception message can be null + if (message == null) + message = EMPTY_STRING; + } + return new Status(severity, PLUGIN_ID, code, message, exception); + } + + /** + * checks what kind of change does <code>code</code> represent + * @param code + */ + private void processed(int code) { + if ((code & IVersionCompare.ERROR_OCCURRED) != 0){ + hasError = true; + return; + } + if ((code & IVersionCompare.MAJOR_CHANGE) != 0){ + hasMajorChange = true; + return; + } + if ((code & IVersionCompare.MINOR_CHANGE) != 0){ + hasMinorChange = true; + return; + } + if ((code & IVersionCompare.MICRO_CHANGE) != 0) + hasMicroChange = true; + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/ManifestHelper.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/ManifestHelper.java new file mode 100755 index 000000000..7876a5713 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/ManifestHelper.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.jar.Attributes.Name; +import org.eclipse.core.runtime.*; +import org.eclipse.osgi.service.pluginconversion.PluginConversionException; +import org.eclipse.osgi.service.pluginconversion.PluginConverter; +import org.eclipse.osgi.util.ManifestElement; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.BundleException; + +/** + * Helper class to manipulate manifest files. + */ +public class ManifestHelper implements VersionCompareConstants { + + /** + * gets attributes from plugin.xml file in a plugin jar or directory denoted by <code>file</code> + * @param file File instance which denotes a plugin jar or directory + * @return Map which contains attributes of plugin.xml + */ + private static Map getAttributesFromPluginManifest(File file) { + PluginConverter pluginConverter = Activator.getPluginConverter(); + if (pluginConverter == null) { + // TODO log and return + // throw new CoreException(new Status(IStatus.WARNING, PLUGIN_ID, IStatus.WARNING, Messages.PluginVersionCompare_noPluginConverterInstanceMsg, null)); + return null; + } + Dictionary pluginXML = null; + try { + pluginXML = pluginConverter.convertManifest(file, true, null, true, null); + } catch (PluginConversionException pce) { + // TODO log return + // throw new CoreException(new Status(IStatus.WARNING, PLUGIN_ID, IStatus.WARNING, NLS.bind(Messages.PluginVersionCompare_couldNotConvertManifestMsg, file.getAbsoluteFile()), pce)); + return null; + } + // we need to convert the dictionary into a map. Too bad. + Map result = new HashMap(); + for (Enumeration e = pluginXML.keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + result.put(key, pluginXML.get(key)); + } + return result; + } + + /** + * get ManifestElement array of element denoted by <code>elementName</code> + * from manifest file of a jar or a plugin directory + * + * @param file File instance which represents a bundle directory(or jar file) + * @param keys the names of the elements we want to retrieve + * @return ManifestElement array of element denoted by <code>elementName</code> or <code>null</code> if + * element has not been found + * @throws CoreException <p>if {@link BundleException} has been thrown</p> + */ + public static Map getElementsFromManifest(File file, String[] keys) throws CoreException { + Map result = new HashMap(); + // try to get attributes from /META-INF/MANIFEST.MF file first + Attributes attributes = getAttributes(file); + // if we didn't get information, try to get information from plugin.xml file + Map map = attributes == null ? getAttributesFromPluginManifest(file) : convertAttributes(attributes); + // TODO log and return + if (map == null) + return result; + for (int i = 0; i < keys.length; i++) { + String value = (String) map.get(keys[i]); + if (value == null) { + // TODO log? we couldn't find the key we were looking for + result.put(keys[i], null); + continue; + } + try { + // get elements of attribute denoted by elementName + ManifestElement[] elements = ManifestElement.parseHeader(keys[i], value); + // we got a result so add it to the list and move to the next key + result.put(keys[i], elements); + } catch (BundleException be) { + throw new CoreException(new Status(IStatus.WARNING, PLUGIN_ID, IStatus.WARNING, NLS.bind(Messages.PluginVersionCompare_couldNotParseHeaderMsg, value), be)); + } + } + return result; + } + + /* + * Convert the given Attributes object into a Map so we can use simple Map APIs on + * it later. + */ + private static Map convertAttributes(Attributes attributes) { + Map result = new HashMap(); + for (Iterator iter = attributes.keySet().iterator(); iter.hasNext();) { + Attributes.Name key = (Name) iter.next(); + result.put(key.toString(), attributes.getValue(key)); + } + return result; + } + + /** + * Return the attributes that are in the given manifest file. + * + * @param file manifest file + * @return the attributes of the manifest file, or <code>null</code> + * @throws IOException <p>if any IOException has been thrown</p> + */ + private static Attributes getAttributes(File file) { + // if file is a directory, we try to get /META-INF/MANIFEST.MF file under it + if (file.isDirectory()) { + File manifestFile = new File(file, BUNDLE_MANIFEST); + if (!manifestFile.exists()) + return null; + InputStream input = null; + try { + input = new BufferedInputStream(new FileInputStream(manifestFile)); + return new Manifest(input).getMainAttributes(); + } catch (IOException e) { + return null; + } finally { + if (input != null) + try { + input.close(); + } catch (IOException e) { + // ignore + } + } + } + // if file is a jar, we try to get /META-INF/MANIFEST.MF file in it + if (file.getName().endsWith(JAR_FILE_EXTENSION)) { + try { + return new JarFile(file).getManifest().getMainAttributes(); + } catch (IOException e) { + return null; + } + } + // we don't have a directory or JAR file + return null; + } + +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/Messages.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/Messages.java new file mode 100755 index 000000000..f3cc9c820 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/Messages.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import org.eclipse.osgi.util.NLS; + +public final class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.pde.tools.internal.versioning.messages";//$NON-NLS-1$ + + public static String FeatureModelTable_couldNotReadConfigFileMsg; + public static String FeatureModelTable_featureFileParseErrorMsg; + + public static String FeatureModelTable_featureSourceErrorMsg; + public static String FeatureModelTable_urlConvertErrorMsg; + public static String FeatureVersionCompare_comparingFeatureMsg; + public static String FeatureVersionCompare_comparingReferenceMsg; + public static String FeatureVersionCompare_deletedFeaturePluginMsg; + public static String FeatureVersionCompare_destIncludedFeatureNotFoundMsg; + public static String FeatureVersionCompare_errorInVerifyPluginMsg; + public static String FeatureVersionCompare_errorReasonMsg; + public static String FeatureVersionCompare_featureFileErrorMsg; + public static String FeatureVersionCompare_incorrectNewVersionMsg; + public static String FeatureVersionCompare_incorrectNewVersionMsg2; + + public static String FeatureVersionCompare_incorrectPluginVersionChange; + public static String FeatureVersionCompare_incorrectPluginVersionMsg; + public static String FeatureVersionCompare_inputErrorMsg; + public static String FeatureVersionCompare_lowerNewVersion; + public static String FeatureVersionCompare_nestedErrorOccurredMsg; + public static String FeatureVersionCompare_newAddedFeaturePlingMsg; + public static String FeatureVersionCompare_newIntroducedFeatureMsg; + public static String FeatureVersionCompare_newVersionLowerThanOldMsg; + public static String FeatureVersionCompare_noFeatureFoundMsg; + public static String FeatureVersionCompare_noFeaturesFoundInConfigMsg; + public static String FeatureVersionCompare_versionChangeIncorrectMsg; + public static String FeatureVersionCompare_pluginNotFoundMsg; + public static String FeatureVersionCompare_sourceIncludedFeatureNotFoundMsg; + public static String FeatureVersionCompare_verifyingFeatureMsg; + public static String JavaClassVersionCompare_classErrorOccurredMsg; + public static String JavaClassVersionCompare_classFileNotLoadedMsg; + public static String JavaClassVersionCompare_classMajorChangeMsg; + public static String JavaClassVersionCompare_classMicroChange; + public static String JavaClassVersionCompare_classMinorChangeMsg; + public static String JavaClassVersionCompare_classModifierChangedMsg; + public static String JavaClassVersionCompare_comparingClassMsg; + public static String JavaClassVersionCompare_deletedInterfaceMsg; + public static String JavaClassVersionCompare_deprecatedChangedMsg; + public static String JavaClassVersionCompare_descriptorChangedMsg; + public static String JavaClassVersionCompare_differentClassNameMsg; + public static String JavaClassVersionCompare_differentSuperClassMsg; + public static String JavaClassVersionCompare_inputStreamErrMsg; + public static String JavaClassVersionCompare_ModifierChangedMsg; + public static String JavaClassVersionCompare_newAddedExceptionMsg; + public static String JavaClassVersionCompare_newAddedInterfaceMsg; + public static String JavaClassVersionCompare_newAddedMsg; + public static String JavaClassVersionCompare_noLongerExistExceptionMsg; + public static String JavaClassVersionCompare_noLongerExistMsg; + public static String JavaClassVersionCompare_unexpectedTypeMsg; + + public static String PluginVersionCompare_classPathJarNotFoundMsg; + public static String PluginVersionCompare_comparingPluginMsg; + public static String PluginVersionCompare_couldNotConvertManifestMsg; + public static String PluginVersionCompare_couldNotOpenJarMsg; + public static String PluginVersionCompare_couldNotParseHeaderMsg; + public static String PluginVersionCompare_couldNotReadAttributesMsg; + public static String PluginVersionCompare_couldNotReadClassMsg; + public static String PluginVersionCompare_couldNotReadClassPathMsg; + public static String PluginVersionCompare_couldNotReadManifestMsg; + public static String PluginVersionCompare_destinationClassNotFoundMsg; + public static String PluginVersionCompare_errorWhenCompareClassesMsg; + public static String PluginVersionCompare_failCloseJarEntryAfterExtractMsg; + public static String PluginVersionCompare_failCloseJarMsg; + public static String PluginVersionCompare_failCloseTmpAfterCreateMsg; + public static String PluginVersionCompare_failCreatTmpJarMsg; + public static String PluginVersionCompare_failExtractJarEntryMsg; + public static String PluginVersionCompare_failOpenJarEntryMsg; + public static String PluginVersionCompare_failOpenTmpJarMsg; + public static String PluginVersionCompare_finishedProcessPluginMsg; + public static String PluginVersionCompare_inputNotExistMsg; + public static String PluginVersionCompare_inValidClassPathMsg; + public static String PluginVersionCompare_noPluginConverterInstanceMsg; + public static String PluginVersionCompare_noValidClassFoundMsg; + + public static String PluginVersionCompare_pluginErrorOccurredMsg; + public static String PluginVersionCompare_pluginMajorChangeMsg; + public static String PluginVersionCompare_pluginMicroChangeMsg; + public static String PluginVersionCompare_pluginMinorChangeMsg; + public static String PluginVersionCompare_sourceClassNotFoundMsg; + + public static String VersionCompareDispatcher_closeFileFailedMsg; + + public static String VersionCompareDispatcher_failedCreateDocMsg; + + public static String VersionCompareDispatcher_failedWriteXMLFileMsg; + public static String VersionCompareDispatcher_fileNotFoundMsg; + + public static String VersionCompareDispatcher_invalidXMLFileNameMsg; + public static String VersionCompareDispatcher_readPropertyFailedMsg; + public static String VersionVerifier_compareOKMsg; + public static String VersionVerifier_coreExceptionMsg; + public static String VersionVerifier_createFileErrorMsg; + public static String VersionVerifier_messageNumberMsg; + public static String VersionVerifier_mixedInputMsg; + public static String VersionVerifier_pathIsNotFileMsg; + public static String VersionVerifier_summaryMsg; + static { + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + // Do not instantiate + } +}
\ No newline at end of file diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/PluginVersionCompare.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/PluginVersionCompare.java new file mode 100755 index 000000000..25d42cf78 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/PluginVersionCompare.java @@ -0,0 +1,889 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import org.eclipse.core.runtime.*; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.util.IClassFileReader; +import org.eclipse.osgi.util.ManifestElement; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.tools.versioning.IVersionCompare; + +/** + * PluginVersionCompare + */ +public class PluginVersionCompare implements VersionCompareConstants { + private final static String CLASS_NAME = "PluginVersionCompare"; //$NON-NLS-1$ + private final static int NEW_CLASS = 0; + private final static int DELETED_CLASS = 1; + private final static int CREATE_FILE_TIMES = 5; + // MultiStatus instance used to store error or warning messages + private MultiStatus finalResult; + // JavaClassVersionCompare instance + JavaClassVersionCompare classCompare; + // + private boolean hasMajorChange; + private boolean hasMinorChange; + private boolean hasMicroChange; + private boolean hasError; + // for debug + private long startTime; + private boolean DEBUG = false; + private static final String DEBUG_OPTION = VersionCompareConstants.PLUGIN_ID + "/debug/plugins"; //$NON-NLS-1$ + + /** + * constructor + */ + public PluginVersionCompare() { + super(); + classCompare = new JavaClassVersionCompare(); + DEBUG = Activator.getBooleanDebugOption(DEBUG_OPTION); + } + + /* + * Log the given debug message. + */ + private void debug(String message) { + if (DEBUG) { + Activator.debug(message); + } + } + + /** + * Compares the two given plug-ins which each other and reports a status object indicating + * whether or not the version number of the plug-ins have been incremented appropriately + * based on the relative changes. + * + * <p> + * The plug-in parameters can be instances of any of the following types: + * <ul> + * <li>{@link java.lang.String} - which denotes the plug-in's jar file name or directory + * <li>{@link java.net.URL} - which denotes the plug-in's jar file name or directory + * <li>{@link ManifestElement}[] - which denotes the manifest elements of Bundle_ClassPath of <code>plugin1</code> + * <li>{@link java.io.File} - which denotes the plug-in's jar file name or directory + * <li>{@link ManifestElement}[] - which denotes the manifest elements of Bundle_ClassPath of <code>plugin2</code> + * </ul> + * </p> + * + * @param plugin1 a plug-in reference + * @param elements1 Bundle_ClassPath of <code>plugin1</code> + * @param plugin2 a plug-in reference + * @param elements2 Bundle_ClassPath of <code>plugin2</code> + * @param status a MultiStatus instance to carry out compare information + * @param monitor IProgressMonitor instance which will monitor the progress during plugin comparing + * @return int number which indicates compare result, it could be MAJOR_CHANGE,MINOR_CHANGE,MICRO_CHANGE,or NO_CHANGE + * @throws CoreException if the parameters are of an incorrect type or if an error occurred during the comparison + */ + protected int checkPluginVersions(MultiStatus status, Object plugin1, ManifestElement[] elements1, Object plugin2, ManifestElement[] elements2, IProgressMonitor monitor) throws CoreException { + try { + monitor = VersioningProgressMonitorWrapper.monitorFor(monitor); + monitor.beginTask(Messages.PluginVersionCompare_comparingPluginMsg, 100); + finalResult = status; + // convert input objects into Files + File file1 = convertInputObject(plugin1); + File file2 = convertInputObject(plugin2); + startTime = System.currentTimeMillis(); + // initialize flags + hasError=false; + hasMajorChange = false; + hasMinorChange = false; + hasMicroChange = false; + // get class URL Tables + Map classFileURLTable1 = null; + Map classFileURLTable2 = null; + classFileURLTable1 = generateClassFileURLTableFromFile(file1, elements1); + // worked 1% + monitor.worked(1); + classFileURLTable2 = generateClassFileURLTableFromFile(file2, elements2); + // worked 1% + monitor.worked(1); + // + return checkPluginVersions(file1.getName(), classFileURLTable1, classFileURLTable2, new SubProgressMonitor(monitor, 98)); + } finally { + monitor.done(); + } + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkPluginVersions(String, String, IProgressMonitor) + */ + public int checkPluginVersions(MultiStatus status, Object plugin1, Object plugin2, IProgressMonitor monitor) throws CoreException { + try { + monitor = VersioningProgressMonitorWrapper.monitorFor(monitor); + monitor.beginTask(Messages.PluginVersionCompare_comparingPluginMsg, 100); + finalResult = status; + // convert input objects into Files + File file1 = convertInputObject(plugin1); + File file2 = convertInputObject(plugin2); + startTime = System.currentTimeMillis(); + // initialize flags + hasError=false; + hasMajorChange = false; + hasMinorChange = false; + hasMicroChange = false; + // get class URL Tables + Map classFileURLTable1 = null; + Map classFileURLTable2 = null; + classFileURLTable1 = generateClassFileURLTable(file1); + // worked 2% + monitor.worked(2); + classFileURLTable2 = generateClassFileURLTable(file2); + // worked 2% + monitor.worked(2); + // check plugin version ( 96% workload) + return checkPluginVersions(file1.getName(), classFileURLTable1, classFileURLTable2, new SubProgressMonitor(monitor, 96)); + } finally { + monitor.done(); + } + } + + /** + * Compares the corresponding classes denoted by URLS in <code>classFileURLTable1</code> to those in <code>classFileURLTable2</code> + * @param pluginName name of plugin + * @param classFileURLTable1 contains URL lists + * @param classFileURLTable2 contains URL lists + * @param monitor IProgressMonitor instance + * @return change with the highest priority + * @throws CoreException <p>if any nested CoreException has been thrown</p> + */ + private int checkPluginVersions(String pluginName, Map classFileURLTable1, Map classFileURLTable2, IProgressMonitor monitor) { + // if both tables are empty, we treat it as no change + if (classFileURLTable1.size() == 0 && classFileURLTable2.size() == 0) { + debug(NLS.bind(Messages.PluginVersionCompare_finishedProcessPluginMsg, pluginName, String.valueOf(System.currentTimeMillis() - startTime))); + return IVersionCompare.NO_CHANGE; + } + // if size of table1 is 0, size of table2 is not 0, we treat it as major change (same as some class has been deleted) + if (classFileURLTable1.size() == 0) { + // check if there is any class no longer exists + processChangedClasseLists(classFileURLTable1, DELETED_CLASS); + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_OVERALL_STATUS, NLS.bind(Messages.PluginVersionCompare_pluginMajorChangeMsg, pluginName), null)); + debug(NLS.bind(Messages.PluginVersionCompare_finishedProcessPluginMsg, pluginName, String.valueOf(System.currentTimeMillis() - startTime))); + return IVersionCompare.MAJOR_CHANGE; + } + // if size of table2 is 0, size of table1 is not 0, we treat it as minor change (same as some class has been new added) + if (classFileURLTable2.size() == 0) { + // check if there is any class new added + processChangedClasseLists(classFileURLTable1, NEW_CLASS); + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_OVERALL_STATUS, NLS.bind(Messages.PluginVersionCompare_pluginMinorChangeMsg, pluginName), null)); + debug(NLS.bind(Messages.PluginVersionCompare_finishedProcessPluginMsg, pluginName, String.valueOf(System.currentTimeMillis() - startTime))); + return IVersionCompare.MINOR_CHANGE; + } + // compare classes + compareClasses(classFileURLTable1, classFileURLTable2, monitor); + // delete temporary directory + deleteTmpDirectory(); + // + debug(NLS.bind(Messages.PluginVersionCompare_finishedProcessPluginMsg, pluginName, String.valueOf(System.currentTimeMillis() - startTime))); + // analysis result + if (hasError) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PLUGIN_OVERALL_STATUS, NLS.bind(Messages.PluginVersionCompare_pluginErrorOccurredMsg, pluginName), null)); + return IVersionCompare.ERROR_OCCURRED; + } + if (hasMajorChange) { + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_OVERALL_STATUS, NLS.bind(Messages.PluginVersionCompare_pluginMajorChangeMsg, pluginName), null)); + return IVersionCompare.MAJOR_CHANGE; + } + if (hasMinorChange) { + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_OVERALL_STATUS, NLS.bind(Messages.PluginVersionCompare_pluginMinorChangeMsg, pluginName), null)); + return IVersionCompare.MINOR_CHANGE; + } + if (hasMicroChange) { + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_OVERALL_STATUS, NLS.bind(Messages.PluginVersionCompare_pluginMicroChangeMsg, pluginName), null)); + return IVersionCompare.MICRO_CHANGE; + } + return IVersionCompare.NO_CHANGE; + } + + /** + * compares corresponding classes indicated by the URLs in two maps + * @param classFileURLTable1 + * @param classFileURLTable2 + * @param monitor IProgressMonitor instance + */ + private void compareClasses(Map classFileURLTable1, Map classFileURLTable2, IProgressMonitor monitor) { + try { + monitor.beginTask("", classFileURLTable1.size() + 1); //$NON-NLS-1$ + for (Iterator iterator1 = classFileURLTable1.keySet().iterator(); iterator1.hasNext();) { + if (monitor.isCanceled()) + throw new OperationCanceledException(); + Object key = iterator1.next(); + // get URL lists of the same class file name + List value1 = (List) classFileURLTable1.get(key); + List value2 = (List) classFileURLTable2.get(key); + if (value2 == null) { + processChangedClasses(value1.toArray(), NEW_CLASS); + continue; + } + List classFileReaderList1 = generateClassFileReaderList(value1); + List classFileReaderList2 = generateClassFileReaderList(value2); + compareClasses(classFileReaderList1, classFileReaderList2, new SubProgressMonitor(monitor, 1)); + // delete the value from classFileURLTable2 + classFileURLTable2.remove(key); + } + // check if there is any class no longer exists + processChangedClasseLists(classFileURLTable2, DELETED_CLASS); + monitor.worked(1); + } finally { + monitor.done(); + } + } + + /** + * compares IClassFileReader instances in <code>list1</code> to the corresponding one + * in <code>list2</code> + * @param list1 contains IClassFileReader instances + * @param list2 contains IClassFileReader instances + * @param monitor IProgressMonitor instance + */ + private void compareClasses(List list1, List list2, IProgressMonitor monitor) { + try { + monitor.beginTask("", list1.size() + 1); //$NON-NLS-1$ + for (Iterator classIterator1 = list1.iterator(); classIterator1.hasNext();) { + if (monitor.isCanceled()) + throw new OperationCanceledException(); + IClassFileReader classFileReader1 = (IClassFileReader) classIterator1.next(); + String className1 = charsToString(classFileReader1.getClassName()); + Iterator classIterator2; + boolean beCompared = false; + for (classIterator2 = list2.iterator(); classIterator2.hasNext();) { + IClassFileReader classFileReader2 = (IClassFileReader) classIterator2.next(); + if (className1.equals(charsToString(classFileReader2.getClassName()))) { + // compare two IClassFileReader instances and merge the result into finalResult + try { + processed(classCompare.checkJavaClassVersions(finalResult, classFileReader1, classFileReader2, new SubProgressMonitor(monitor, 1))); + } catch (CoreException ce) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_errorWhenCompareClassesMsg, charsToString(classFileReader1.getClassName())), null)); + } + list2.remove(classFileReader2); + beCompared = true; + break; + } + } + if (!beCompared && shouldProcess(classFileReader1)) { + // new added class + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_DETAIL_STATUS | IVersionCompare.MINOR_CHANGE, NLS.bind(Messages.PluginVersionCompare_destinationClassNotFoundMsg, charsToString(classFileReader1.getClassName())), null)); + } + } + // no longer exist classes + for (Iterator iterator2 = list2.iterator(); iterator2.hasNext();) { + IClassFileReader classFileReader = (IClassFileReader) iterator2.next(); + if (shouldProcess(classFileReader)) + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_DETAIL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.PluginVersionCompare_sourceClassNotFoundMsg, charsToString(classFileReader.getClassName())), null)); + } + monitor.worked(1); + } finally { + monitor.done(); + } + } + + /** + * processes no long existed classe lists contained in <code>map</code> + * @param map Map which contains class name and URL list pairs which no longer exist in the new plugin + */ + private void processChangedClasseLists(Map map, int flag){ + for (Iterator iterator = map.values().iterator(); iterator.hasNext();) { + List list = (List) iterator.next(); + processChangedClasses(list.toArray(), flag); + } + } + + /** + * process classes indicated by URL instances in <code>classArray</code> depending on <code>flag</code> + * if <code>flag</code> is NEW_CLASS, all the IClassFileReader instances in <code>classArray</code> are new added classes + * if <code>flag</code> is DELETED_CLASS, all the IClassFileReader instances in <code>classArray</code> are classes no longer exist + * @param classArray contains URL instances + * @param flag could be NEW_CLASS or DELETED_CLASS + */ + private void processChangedClasses(Object[] classArray, int flag) { + if (classArray.length == 0) + return; + for (int i = 0; i < classArray.length; i++) { + if (!(classArray[i] instanceof URL)) + continue; + try { + IClassFileReader classFileReader = ClassFileHelper.getReader(classArray[i]); + if (classFileReader == null) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_couldNotReadClassMsg, ((URL) classArray[i]).getFile()), null)); + continue; + } + if (shouldProcess(classFileReader) && shouldProcess(charsToString(classFileReader.getClassName()))) + if (flag == NEW_CLASS) + // new added class + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_OVERALL_STATUS | IVersionCompare.MINOR_CHANGE, NLS.bind(Messages.PluginVersionCompare_destinationClassNotFoundMsg, charsToString(classFileReader.getClassName())), null)); + else if (flag == DELETED_CLASS) + // deleted class + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.CLASS_OVERALL_STATUS | IVersionCompare.MAJOR_CHANGE, NLS.bind(Messages.PluginVersionCompare_sourceClassNotFoundMsg, charsToString(classFileReader.getClassName())), null)); + } catch (CoreException e) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_couldNotReadClassMsg, ((URL) classArray[i]).getFile()), e)); + } + } + } + + /** + * generates a List which stores IClassFileReader instances indicated by URLs in <code>list</code> + * + * @param list contains URL instances + * @return List contains IClassFileReader instances + */ + private List generateClassFileReaderList(List list) { + ArrayList newList = new ArrayList(0); + if (list == null || list.size() == 0) + return newList; + for (Iterator iterator = list.iterator(); iterator.hasNext();) { + try { + IClassFileReader classFileReader = ClassFileHelper.getReader(iterator.next()); + if (classFileReader == null) + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_couldNotReadClassMsg, ((URL) iterator.next()).getFile()), null)); + else + newList.add(classFileReader); + } catch (CoreException e) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_couldNotReadClassMsg, ((URL) iterator.next()).getFile()), null)); + } + } + return newList; + } + + /** + * generates Map containing lists of URL instances indicates java classes + * which are under a plugin directory or included in a plugin jar file denoted by <code>file</code>. + * key is simple name of class file e.g. Foo.class + * value is a List which contains URLs pointing to classes which have the same simple file name + * + * @param file File instance which denotes a plugin directory or a plugin jar file + * @return Map containing URL instances + * @throws CoreException <p>if any nested CoreException has been caught</p> + */ + private Map generateClassFileURLTable(File file) throws CoreException { + // get bundle class path + ManifestElement[] elements = null; + try { + elements = (ManifestElement[]) ManifestHelper.getElementsFromManifest(file, new String[] {BUNDLE_CLASSPATH}).get(BUNDLE_CLASSPATH); + } catch (CoreException ce) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_couldNotReadClassPathMsg, file.getAbsolutePath()), null)); + } + return generateClassFileURLTableFromFile(file, elements); + } + + /** + * converts input Object into File instance + * @param object input object + * @return File which represents <code>object</code> + * @throws CoreException <p>if type of <code>object</code> is unexpected</p> + * <p>or <code>object</code> does not represent an exist file or directory + */ + private File convertInputObject(Object object) throws CoreException { + File file; + if (object instanceof String) { + // if object is a String + file = new File((String) object); + } else if (object instanceof URL) + // if object is an URL + file = new File(((URL) object).getFile()); + else if (object instanceof File) + // if object is a File + file = (File) object; + else + // otherwise throw CoreException + throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.JavaClassVersionCompare_unexpectedTypeMsg, object.getClass().getName()), null)); + if (file.exists()) + return file; + throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.PluginVersionCompare_inputNotExistMsg, file.getAbsolutePath()), null)); + } + + /** + * generates Map containing lists of URL instances indicates java classes + * which are under a plugin directory or included in a plugin jar file denoted by <code>file</code>. + * key is simple name of class file e.g. Foo.class + * value is a List which contains URLs pointing to classes which have the same simple file name + * + * @param file File instance which denotes a plugin directory or a plugin jar file + * @param elements ManifestElement array of class path attribute + * @return Map containing URL instances + * @throws CoreException <p>if any nested CoreException has been caught</p> + */ + private Map generateClassFileURLTableFromFile(File file, ManifestElement[] elements) throws CoreException { + Map table = new Hashtable(0); + if (file.isFile()) + // if file is a file + getClassURLsFromJar(table, file, elements); + else if (file.isDirectory()) + // if file is a directory + getClassURLsFromDir(table, file, elements); + if (table.size() == 0) + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.PluginVersionCompare_noValidClassFoundMsg, file.getAbsolutePath()), null)); + return table; + } + + /** + * gets URL instances indicates java classes which are included in a plugin jar file denoted by <code>file</code>. + * and set them into <code>map</code> + * key is simple name of class file e.g. Foo.class + * value is a List which contains URLs pointing to classes which have the same simple file name + * + * @param map Map used to store URL instances + * @param file File instance which denotes a jar file + * @param elements ManifestElement array of class path attribute + * @throws CoreException <p>if jar file could not be loaded successfully</p> + * <p>or any nested CoreException has been caught</p> + * + */ + private void getClassURLsFromJar(Map map, File file, ManifestElement[] elements) throws CoreException { + // check if file is a jar file + if (!isGivenTypeFile(file, JAR_FILE_EXTENSION)) + return; + JarFile jarFile; + try { + jarFile = new JarFile(file); + } catch (IOException ioe) { + throw new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.PluginVersionCompare_couldNotOpenJarMsg, file.getAbsolutePath()), ioe)); + } + if (elements == null) + // if elements is null, we consider the class path as "." + getAllClassURLsInJar(map, jarFile); + else { + // check elements + for (int i = 0; i < elements.length; i++) { + if (elements[i].getValue().equals(DOT_MARK)) + getAllClassURLsInJar(map, jarFile); + else { + // class path element is supposed to be "." or "*.jar" + if (!new Path(elements[i].getValue()).getFileExtension().equals(JAR_FILE_EXTENSION)) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PLUGIN_DETAIL_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_inValidClassPathMsg, elements[i].getValue(), file.getAbsolutePath()), null)); + continue; + } + // if the jar entry indicates a nested jar file, we get IClassFileReaders of all the classes in it + JarEntry entry = jarFile.getJarEntry(elements[i].getValue()); + if (entry != null) { + JarFile tempJarFile = null; + // get tmp file indicated by entry + File tmpFile = getTmpFile(jarFile, entry); + if (tmpFile == null) + // if failed to get tmp file, continue to check next element + continue; + try { + // generate a JarFile of the tmp file + tempJarFile = new JarFile(tmpFile); + } catch (IOException ioe) { + Object[] msg = {tmpFile.getAbsoluteFile(), entry.getName(), file.getAbsolutePath()}; + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_failOpenTmpJarMsg, msg), ioe)); + // continue to check next element + continue; + } + // get URLs of all classes in the jar + getAllClassURLsInJar(map, tempJarFile); + } else + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.PLUGIN_DETAIL_STATUS, NLS.bind(Messages.PluginVersionCompare_classPathJarNotFoundMsg, elements[i].getValue(), file.getAbsolutePath()), null)); + } + } + } + // close jar file + try { + jarFile.close(); + } catch (IOException ioe) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_failCloseJarMsg, jarFile.getName()), ioe)); + } + } + + /** + * delete temporary directory used by this class + * + */ + private void deleteTmpDirectory() { + // get java tmp directory + IPath tmpPath = getJavaTmpPath(); + // find the directory for this plugin + tmpPath = tmpPath.append(PLUGIN_ID); + // find the directory for this class + tmpPath = tmpPath.append(CLASS_NAME); + // delete all the files and directories + File tmpDir = tmpPath.toFile(); + deleteTmpFile(tmpDir); + } + + /** + * delete all the files and directories under <code>dir</code> + * @param file + */ + private void deleteTmpFile(File dir) { + if (!dir.exists()) + return; + if (dir.isFile()) + dir.delete(); + File[] files = dir.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isFile()) { + files[i].delete(); + } else + deleteTmpFile(files[i]); + } + dir.delete(); + } + + /** + * get URLs of all the classes which is in <code>jar</code>, and + * put them into <code>map</code> + * @param map map used to store URLs of all the classes + * @param jar jar file + */ + private void getAllClassURLsInJar(Map map, JarFile jar) { + // get JarEntrys + Enumeration enumeration = jar.entries(); + String jarURLString = jar.getName() + JAR_URL_SEPARATOR; + String classURLString; + for (; enumeration.hasMoreElements();) { + JarEntry jarEntry = (JarEntry) enumeration.nextElement(); + // get URL for JarEntry which represents a java class file + if (!isValidClassJarEntry(jarEntry)) + continue; + if (!shouldProcess(jarEntry.getName())) + continue; + classURLString = jarURLString + jarEntry.getName(); + URL classFileURL = null; + try { + classFileURL = new URL(JAR_URL_HEAD + classURLString); + } catch (MalformedURLException e) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.FeatureModelTable_urlConvertErrorMsg, classURLString), e)); + // do next + continue; + } + // try to get URL list + IPath entryPath = new Path(jarEntry.getName()); + List urls = (List) map.get(entryPath.lastSegment()); + if (urls == null) { + urls = new ArrayList(0); + urls.add(classFileURL); + } else + urls.add(classFileURL); + // put file name and list into map + map.put(entryPath.lastSegment(), urls); + } + } + + /** + * get File instance which indicates the full path of the temporary jar file + * which is denoted by <code>jarEntry</code>. + * jar file denoted by <code>jarEntry</code> will be extracted to the java + * tmp directory and then be returned as a File instance + * + * @param jarFile + * @param jarEntry + * @return File indicates the full path of the temporary jar file or <code>null</code> + * if could not get the tmp file successfully + */ + private File getTmpFile(JarFile jarFile, JarEntry jarEntry) { + // get java tmp directory + IPath tmpPath = getJavaTmpPath(); + // create a directory for this plugin + tmpPath = tmpPath.append(PLUGIN_ID); + // create a directory for this plugin + tmpPath = tmpPath.append(CLASS_NAME); + // get path represented by jarEntry + IPath entryPath = new Path(jarEntry.getName()); + // generate tmp directory + tmpPath = tmpPath.append(entryPath.removeLastSegments(1)); + File tmpDir = tmpPath.toFile(); + // if the tmp directory does not exist, make it + if (!tmpDir.exists()) + tmpDir.mkdirs(); + // get OutputStream of the tmp file + IPath tmpFile = null; + OutputStream out = null; + int i = 0; + while (out == null) { + // generate the full path of the tmp file(directory/file name) + tmpFile = tmpPath.append(generateTmpFileName(entryPath.lastSegment())); + try { + out = new BufferedOutputStream(new FileOutputStream(tmpFile.toFile())); + } catch (IOException ioe) { + i++; + //we try to create the tmp file for 5 times if any IOException has been caught + if (i == CREATE_FILE_TIMES) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_failCreatTmpJarMsg, tmpFile.toOSString()), ioe)); + return null; + } + } + } + InputStream in = null; + try { + try { + // get InputStream of jarEntry + in = new BufferedInputStream(jarFile.getInputStream(jarEntry)); + } catch (IOException ioe) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_failOpenJarEntryMsg, jarEntry.getName()), ioe)); + return null; + } + // extract the file from jar to the tmp file + int readResult; + try { + while ((readResult = in.read()) != -1) + out.write(readResult); + } catch (IOException ioe) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_failExtractJarEntryMsg, jarEntry.getName()), ioe)); + return null; + } finally { + try { + // ensure the input stream is closed + if (in != null) { + in.close(); + in = null; + } + } catch (IOException ioe) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_failCloseJarEntryAfterExtractMsg, jarEntry.getName()), ioe)); + } + } + } finally { + try { + // ensure the output stream is closed + if (out != null) { + out.close(); + out = null; + } + } catch (IOException ioe) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.PluginVersionCompare_failCloseTmpAfterCreateMsg, tmpFile.toOSString()), ioe)); + } + } + return tmpFile == null ? null : tmpFile.toFile(); + } + + /** + * generates file name with current time stamp in the front + * e.g.: file.jar --> 1152797697531.jar + * @param fileName + * @return generated file name + */ + private String generateTmpFileName(String fileName) { + StringBuffer buffer = new StringBuffer(); + buffer.append(System.currentTimeMillis()); + int index = fileName.lastIndexOf(DOT_MARK); + if (index == -1) + return buffer.toString(); + buffer.append(fileName.substring(index)); + return buffer.toString(); + } + + /** + * gets URL instances indicates java classes which are under in a plugin directory denoted by <code>dir</code>. + * and set them into <code>map</code> + * key is simple name of class file e.g. Foo.class + * value is a List which contains URLs pointing to classes which have the same simple file name + * + * @param map Map used to store URL instances + * @param dir File instance which denotes a directory on file-system + * @param elements ManifestElement array of class path attribute + * @throws CoreException <p>if nested CoreException has been caught</p> + */ + private void getClassURLsFromDir(Map map, File dir, ManifestElement[] elements) throws CoreException { + if (elements == null) + // if elements is null, we consider the class path as "." + getAllClassURLsUnderDir(map, dir); + else { + for (int i = 0; i < elements.length; i++) { + if (elements[i].getValue().equals(DOT_MARK)) + getAllClassURLsUnderDir(map, dir); + else { + IPath path = new Path(dir.getAbsolutePath()); + path = path.append(elements[i].getValue()); + // class path element is supposed to be "." or "*.jar" + if (!path.getFileExtension().equals(JAR_FILE_EXTENSION)) + finalResult.merge(resultStatusHandler(IStatus.WARNING, IVersionCompare.PROCESS_ERROR_STATUS, NLS.bind(Messages.PluginVersionCompare_inValidClassPathMsg, elements[i].getValue(), dir.getAbsolutePath()), null)); + File jarFile = path.toFile(); + if (jarFile.exists()) + getClassURLsFromJar(map, jarFile, null); + else + finalResult.merge(resultStatusHandler(IStatus.INFO, IVersionCompare.PLUGIN_DETAIL_STATUS, NLS.bind(Messages.PluginVersionCompare_classPathJarNotFoundMsg, elements[i].getValue(), dir.getAbsolutePath()), null)); + } + } + } + } + + /** + * checks if the class denoted by <code>className</code> need to be processed. + * @param className class name or class file name + * @return <code>true</code> if className does not include "/internal/", "\internal\", and ".internal." + * <code>false</code> otherwise + */ + private boolean shouldProcess(String className) { + return className.indexOf("/internal/") == -1 && className.indexOf("\\internal\\") == -1 && className.indexOf("\\.internal\\.") == -1; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + /** + * checks if the class denoted by <code>classFileReader</code> need to be processed. + * @param classFileReader IClassFileReader instance + * @return <code>true</code> if access flag of the class is public or protected + * <code>false</code> otherwise + */ + private boolean shouldProcess(IClassFileReader classFileReader) { + return Flags.isPublic(classFileReader.getAccessFlags()) || Flags.isProtected(classFileReader.getAccessFlags()); + } + + /** + * gets URLs of all "*.class" file under directory <code>dir</code> and its sub-directories + * and put them into <code>map</code> + * @param map Map used to store IClassFileReader instance + * @param dir directory + */ + private void getAllClassURLsUnderDir(Map map, File dir) { + // if file is a directory, get URLs of "*.class" files under the directory and its sub-directories + File[] classFiles = dir.listFiles(new VersionClassDirFilter(CLASS_FILE_EXTENSION)); + if (classFiles == null) { + return; + } + for (int i = 0; i < classFiles.length; i++) { + File file = classFiles[i]; + if (classFiles[i].isDirectory()) + // get URLs of class files under sub directory + getAllClassURLsUnderDir(map, classFiles[i]); + else if (isGivenTypeFile(file, CLASS_FILE_EXTENSION)) { + if (!shouldProcess(file.getAbsolutePath())) + continue; + // get URL of class file + URL classFileURL = null; + try { + classFileURL = classFiles[i].toURL(); + } catch (MalformedURLException e) { + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.FeatureModelTable_urlConvertErrorMsg, classFiles[i].getAbsolutePath()), e)); + // do next + continue; + } + if (classFileURL == null) + finalResult.merge(resultStatusHandler(IStatus.ERROR, IVersionCompare.PROCESS_ERROR_STATUS | IVersionCompare.ERROR_OCCURRED, NLS.bind(Messages.FeatureModelTable_urlConvertErrorMsg, classFiles[i].getAbsolutePath()), null)); + else { + // try to get URL list + List urls = (List) map.get(file.getName()); + if (urls == null) { + urls = new ArrayList(0); + urls.add(classFileURL); + } else + urls.add(classFileURL); + // put file name and list into map + map.put(file.getName(), urls); + } + } + } + } + + /** + * converts char array to String + * @param chars array of char + * @return String which represents the content of <code>chars</code> + */ + private String charsToString(char[] chars) { + return new String(chars); + } + + /** + * Checks whether or not the given file potentially represents a given type file on the file-system. + * + * @param file File instance which denotes to file on the file-system + * @param type file type(e.g. "java","class", "jar") + * @return <code>true</code> <code>file</code> exists and is a given type file, + * <code>false</code> otherwise + */ + private boolean isGivenTypeFile(File file, String type) { + IPath path = new Path(file.getAbsolutePath()); + return file.isFile() && path.getFileExtension().equals(type); + } + + /** + * Checks whether or not the given JarEntry potentially represents a valid java class file, + * we filter out class file like "Foo$0.class". + * + * @param entry JarEntry instance + * @param type JarEntry type(e.g. "class") + * @return <code>true</code> <code>entry</code> exists and is a valid java class JarEntry, + * <code>false</code> otherwise + */ + private boolean isValidClassJarEntry(JarEntry entry) { + IPath path = new Path(entry.toString()); + // get file extension + String extension = path.getFileExtension(); + if (extension == null) + return false; + if (!extension.equals(CLASS_FILE_EXTENSION)) + return false; + int extensionIndex = path.lastSegment().lastIndexOf(extension); + if (extensionIndex == -1) + return false; + // get name of the file (without ".class") + String fileName = path.lastSegment().substring(0, extensionIndex - 1); + try { + int dollarIndex = fileName.lastIndexOf(DOLLAR_MARK); + // if no "$" in the file name, it is what we want + if (dollarIndex == -1) + return true; + // if the sub-String after "$" is all number, it is not what we want + Long.parseLong(fileName.substring(dollarIndex + 1)); + return false; + } catch (NumberFormatException nfe) { + // if the sub-String after "$" is not all number, it is what we want + return true; + } + } + + /** + * get java tmp directory IPath from system properties + * @return IPath instance which indicates java tmp directory + */ + private IPath getJavaTmpPath() { + Properties properties = System.getProperties(); + return new Path(properties.getProperty(JAVA_TMP_DIR_PROPERTY)); + } + + /** + * Return a new status object populated with the given information. + * + * @param severity severity of status + * @param code indicates type of this IStatus instance, it could be one of: FEATURE_OVERALL_STATUS, + * FEATURE_DETAIL_STATUS, PLUGIN_OVERALL_STATUS, PLUGIN_DETAIL_STATUS, PROCESS_ERROR_STATUS, + * CLASS_OVERALL_STATUS, CLASS_DETAIL_STATUS + * @param message the status message + * @param exception exception which has been caught, or <code>null</code> + * @return the new status object + */ + private IStatus resultStatusHandler(int severity, int code, String message, Exception exception) { + processed(code); + if (message == null) { + if (exception != null) + message = exception.getMessage(); + // extra check because the exception message can be null + if (message == null) + message = EMPTY_STRING; + } + return new Status(severity, PLUGIN_ID, code, message, exception); + } + + /** + * checks what kind of change does <code>result</code> represent + * @param result compare result + */ + private void processed(int result) { + if ((result & IVersionCompare.ERROR_OCCURRED) != 0){ + hasError = true; + return; + } + if ((result & IVersionCompare.MAJOR_CHANGE) != 0){ + hasMajorChange = true; + return; + } + if ((result & IVersionCompare.MINOR_CHANGE) != 0){ + hasMinorChange = true; + return; + } + if ((result & IVersionCompare.MICRO_CHANGE) != 0) + hasMicroChange = true; + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionClassDirFilter.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionClassDirFilter.java new file mode 100755 index 000000000..0f9663c07 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionClassDirFilter.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.File; +import java.io.FileFilter; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +/** + * This class implements the FileFilter interface. Instance of this class may be + * passed to the listFiles(FileFilter) method of the File class + * + */ +public class VersionClassDirFilter implements FileFilter { + private String fileExtension; + + /** + * Constructor + * + * @param fileExtension the file extension to filter on + */ + public VersionClassDirFilter(String fileExtension) { + super(); + this.fileExtension = fileExtension; + } + + /** + * Returns <code>true</code> if the given file is a directory or its extension matches the file extension for this + * filter and <code>false</code> otherwise. + * + * @param file the abstract pathname to be tested + * @return <code>true</code> if the file is a directory , or its extension matches this filter's file extension + * <code>false</code> otherwise + */ + public boolean accept(File file) { + if (file.isDirectory()) { + return true; + } + + IPath path = new Path(file.getAbsolutePath()); + String extension = path.getFileExtension(); + if (extension == null) { + if (fileExtension == null || fileExtension.trim().equals("")) //$NON-NLS-1$ + return true; + else + return false; + } else { + if (path.getFileExtension().equals(fileExtension)) + return true; + else + return false; + } + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionCompareConstants.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionCompareConstants.java new file mode 100755 index 000000000..de1f88c71 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionCompareConstants.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +public interface VersionCompareConstants { + public static final String PLUGIN_ID = "org.eclipse.pde.tools.versioning"; //$NON-NLS-1$ + public final static String BUNDLE_MANIFEST = "META-INF/MANIFEST.MF"; //$NON-NLS-1$ + public final static String BUNDLE_CLASSPATH = "Bundle-ClassPath"; //$NON-NLS-1$ + public final static String BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName"; //$NON-NLS-1$ + public final static String BUNDLE_VERSION = "Bundle-Version"; //$NON-NLS-1$ + public final static String BUNDLE_LOCATION = "Bundle-Location"; //$NON-NLS-1$ + public final static String JAVA_TMP_DIR_PROPERTY = "java.io.tmpdir";//$NON-NLS-1$ + public final static String PLUGIN_DIR_NAME = "plugins"; //$NON-NLS-1$ + public static final String FEATURES_FILE_NAME = "feature.xml"; //$NON-NLS-1$ + public static final String CONFIGURATION_FILE_NAME = "platform.xml"; //$NON-NLS-1$ + public static final String JAVA_FILE_EXTENSION = "java"; //$NON-NLS-1$ + public static final String CLASS_FILE_EXTENSION = "class"; //$NON-NLS-1$ + public static final String JAR_FILE_EXTENSION = "jar"; //$NON-NLS-1$ + public static final String PLATFORM_PROTOCOL = "platform"; //$NON-NLS-1$ + public static final String FEATURE_TITLE = "feature"; //$NON-NLS-1$ + public static final String PLUGIN_TITLE = "plugin"; //$NON-NLS-1$ + public static final String FIELD_TITLE = "field"; //$NON-NLS-1$ + public static final String METHOD_TITLE = "method"; //$NON-NLS-1$ + public static final String JAR_URL_HEAD = "jar:file:"; //$NON-NLS-1$ + public static final String JAR_URL_SEPARATOR = "!/"; //$NON-NLS-1$ + public static final String WILD_CAST_STRING = ".*"; //$NON-NLS-1$ + public static final String DOT_QUOTE_STRING = "\\."; //$NON-NLS-1$ + public static final int DEFAULT_MODIFIER_TESTER = 0x0007; + public static final char START_CHAR = '*'; + public static final char DOT_CHAR = '.'; + public static final String ENCODING_TYPE = "UTF-8"; //$NON-NLS-1$ + public static final String ROOT_ELEMENT_NAME = "CompareResult"; //$NON-NLS-1$ + public static final String SEVERITY_ELEMENT_NAME = "Category"; //$NON-NLS-1$ + public static final String CHILDREN_ELEMENT_NAME = "Info"; //$NON-NLS-1$ + public static final String CODE_ATTRIBUTE_NAME = "Code"; //$NON-NLS-1$ + public static final String MESSAGE_ATTRIBUTE_NAME = "Message"; //$NON-NLS-1$ + public static final String XML_FILE_EXTENSION = "xml"; //$NON-NLS-1$ + // marks + public static final String EMPTY_STRING = ""; //$NON-NLS-1$ + public final static String DOLLAR_MARK = "$";//$NON-NLS-1$ + public final static String DOT_MARK = ".";//$NON-NLS-1$ + public static final String COMMA_MARK = ","; //$NON-NLS-1$ + public static final String UNDERSCORE_MARK = "_"; //$NON-NLS-1$ + public static final String KEY_SEPARATOR = "#"; //$NON-NLS-1$ + public static final String SPACE = " "; //$NON-NLS-1$ + public static final String START_MARK = "*"; //$NON-NLS-1$ + // modifier String + public static final String ABSTRACT_STRING = "abstract"; //$NON-NLS-1$ + public static final String PUBLIC_STRING = "public"; //$NON-NLS-1$ + public static final String PRIVATE_STRING = "private"; //$NON-NLS-1$ + public static final String FINAL_STRING = "final"; //$NON-NLS-1$ + public static final String DEFAULT_STRING = "default"; //$NON-NLS-1$ + public static final String STATIC_STRING = "static"; //$NON-NLS-1$ + public static final String PROTECTED_STRING = "protected"; //$NON-NLS-1$ + public static final String VOLATILE_STRING = "volatile"; //$NON-NLS-1$ + public static final String NON_ABSTRACT_STRING = "non-abstract"; //$NON-NLS-1$ + public static final String NON_STATIC_STRING = "non-static"; //$NON-NLS-1$ + public static final String NON_FINAL_STRING = "non-final"; //$NON-NLS-1$ + public static final String NON_VOLATILE_STRING = "non-volatile"; //$NON-NLS-1$ +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionCompareDispatcher.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionCompareDispatcher.java new file mode 100755 index 000000000..0e3fff344 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionCompareDispatcher.java @@ -0,0 +1,322 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.*; +import java.net.URL; +import java.util.*; +import javax.xml.parsers.*; +import javax.xml.transform.*; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.eclipse.core.runtime.*; +import org.eclipse.jdt.core.util.IClassFileReader; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.tools.versioning.ICompareResult; +import org.eclipse.pde.tools.versioning.IVersionCompare; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class VersionCompareDispatcher implements IVersionCompare { + private static final String NAME_STRING = "Name"; //$NON-NLS-1$ + private static final String ERROR_STRING = "Error"; //$NON-NLS-1$ + private static final String WARNING_STRING = "Warning"; //$NON-NLS-1$ + private static final String INFO_STRING = "Information"; //$NON-NLS-1$ + private static final String SEVERITY_CODE_STRING = "SeverityCode"; //$NON-NLS-1$ + private static final String VERSION_STRING = "Version"; //$NON-NLS-1$ + private static final String XML_VERSION = "1.0"; //$NON-NLS-1$ + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkFeatureVersions(java.lang.String, java.lang.String, boolean, File, IProgressMonitor) + */ + public IStatus checkFeatureVersions(String path1, String path2, boolean needPluginCompare, File versionOptionFile, IProgressMonitor monitor) throws CoreException { + return new FeatureVersionCompare().checkFeatureVersions(path1, path2, needPluginCompare, versionOptionFile, monitor); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkFeatureVersions(java.net.URL, java.net.URL, boolean, File, IProgressMonitor) + */ + public IStatus checkFeatureVersions(URL configURL1, URL configURL2, boolean needPluginCompare, File compareOptionFile, IProgressMonitor monitor) throws CoreException { + return new FeatureVersionCompare().checkFeatureVersions(configURL1, configURL2, needPluginCompare, compareOptionFile, monitor); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkFeatureVersions(java.io.File, java.io.File, boolean, File, IProgressMonitor) + */ + public IStatus checkFeatureVersions(File file1, File file2, boolean needPluginCompare, File compareOptionFile, IProgressMonitor monitor) throws CoreException { + return new FeatureVersionCompare().checkFeatureVersions(file1, file2, needPluginCompare, compareOptionFile, monitor); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkPluginVersions(String, String, IProgressMonitor) + */ + public ICompareResult checkPluginVersions(String plugin1, String plugin2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new PluginVersionCompare().checkPluginVersions(finalResult, plugin1, plugin2, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkPluginVersions(URL, URL, IProgressMonitor) + */ + public ICompareResult checkPluginVersions(URL pluginURL1, URL pluginURL2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new PluginVersionCompare().checkPluginVersions(finalResult, pluginURL1, pluginURL2, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkPluginVersions(File, File, IProgressMonitor) + */ + public ICompareResult checkPluginVersions(File pluginFile1, File pluginFile2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new PluginVersionCompare().checkPluginVersions(finalResult, pluginFile1, pluginFile2, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkJavaClassVersions(String, String, IProgressMonitor) + */ + public ICompareResult checkJavaClassVersions(String javaClass1, String javaClass2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new JavaClassVersionCompare().checkJavaClassVersions(finalResult, javaClass1, javaClass2, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkJavaClassVersions(URL, URL, IProgressMonitor) + */ + public ICompareResult checkJavaClassVersions(URL javaClassURL1, URL javaClassURL2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new JavaClassVersionCompare().checkJavaClassVersions(finalResult, javaClassURL1, javaClassURL2, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkJavaClassVersions(File, File, IProgressMonitor) + */ + public ICompareResult checkJavaClassVersions(File javaClassFile1, File javaClassFile2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new JavaClassVersionCompare().checkJavaClassVersions(finalResult, javaClassFile1, javaClassFile1, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkJavaClassVersions(InputStream, InputStream, IProgressMonitor) + */ + public ICompareResult checkJavaClassVersions(InputStream javaClassInputStream1, InputStream javaClassInputStream2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new JavaClassVersionCompare().checkJavaClassVersions(finalResult, javaClassInputStream1, javaClassInputStream2, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#checkJavaClassVersions(IClassFileReader, IClassFileReader, IProgressMonitor) + */ + public ICompareResult checkJavaClassVersions(IClassFileReader classFileReader1, IClassFileReader classFileReader2, IProgressMonitor monitor) throws CoreException { + MultiStatus finalResult = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, Messages.FeatureVersionCompare_errorReasonMsg, null); + return new CompareResult(new JavaClassVersionCompare().checkJavaClassVersions(finalResult, classFileReader1, classFileReader2, monitor), finalResult); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#processCompareResult(IStatus status, int infoChoice) + */ + public IStatus processCompareResult(IStatus status, int infoChoice) { + if (!status.isMultiStatus()) + return status; + // create a new multi-status + MultiStatus multiStatus = new MultiStatus(VersionCompareConstants.PLUGIN_ID, IStatus.OK, status.getMessage(), null); + // get children status from result status + IStatus[] childStatus = status.getChildren(); + for (int i = 0; i < childStatus.length; i++) { + if ((childStatus[i].getCode() & infoChoice) != 0) { + multiStatus.merge(childStatus[i]); + } + } + return multiStatus; + } + + /* (non-Javadoc) + * @see org.eclipse.pde.tools.versioning.IVersionCompare#processExclusionListFile(File file) + */ + public Map processInclusionListFile(File file) throws CoreException { + Map table = new Hashtable(); + FileInputStream fileInputStream = null; + try { + //create a properties instance + Properties ppt = new Properties(); + // get InputStream of exclusion-list-file + fileInputStream = new FileInputStream(file); + // load property file + ppt.load(fileInputStream); + for (Iterator iterator = ppt.keySet().iterator(); iterator.hasNext();) { + Object key = iterator.next(); + String property = ppt.getProperty((String) key); + if (property == null || property.trim().equals(VersionCompareConstants.EMPTY_STRING)) + continue; + List propertyValueList = generateList(property.split(VersionCompareConstants.COMMA_MARK)); + table.put(key, propertyValueList); + } + } catch (FileNotFoundException fnfe) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_fileNotFoundMsg, file.getAbsolutePath()), fnfe)); + } catch (IOException ioe) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_readPropertyFailedMsg, file.getAbsolutePath()), ioe)); + } finally { + if (fileInputStream != null) { + // close FileInputStream + try { + fileInputStream.close(); + } catch (IOException ioe) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_closeFileFailedMsg, file.getAbsolutePath()), ioe)); + } + fileInputStream = null; + } + } + return table; + } + + /** + * generates a List which stores instances in array <code>objects</code> + * + * @param objects instance objects + * @return List + */ + private List generateList(Object[] objects) { + ArrayList list = new ArrayList(0); + if (objects == null || objects.length == 0) + return list; + for (int i = 0; i < objects.length; i++) + list.add(objects[i]); + return list; + } + + /** + * writes out children statuses of <code>status</code> to XML file denoted by <code>fileName</code> + * @param status IStatus instance + * @param fileName String name of a XML file + * @throws CoreException <p>if any nested CoreException has been caught</p> + */ + public void writeToXML(IStatus status, String fileName) throws CoreException { + Document doc = createXMLDoc(status); + writeToXML(doc, fileName); + } + + /** + * creates a Document instance containing elements each of which represent a child status + * of <code>status</code> + * @param status IStatus instance + * @throws CoreException <p>if any ParserConfigurationException, or FactoryConfigurationError has been caught</p> + */ + private Document createXMLDoc(IStatus status) throws CoreException { + DocumentBuilder docBuilder = null; + // create a DocumentBuilder instance + try { + docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (ParserConfigurationException pce) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, Messages.VersionCompareDispatcher_failedCreateDocMsg, pce)); + } catch (FactoryConfigurationError fce) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, Messages.VersionCompareDispatcher_failedCreateDocMsg, fce)); + } + // create a Document instance + Document doc = docBuilder.newDocument(); + // create the root element + Element rootElement = doc.createElement(VersionCompareConstants.ROOT_ELEMENT_NAME); + rootElement.setAttribute(VERSION_STRING, XML_VERSION); + doc.appendChild(rootElement); + // create sub elements to contain different type of status + Element errorElement = doc.createElement(VersionCompareConstants.SEVERITY_ELEMENT_NAME); + errorElement.setAttribute(NAME_STRING, ERROR_STRING); + errorElement.setAttribute(SEVERITY_CODE_STRING, String.valueOf(IStatus.ERROR)); + rootElement.appendChild(errorElement); + Element warningElement = doc.createElement(VersionCompareConstants.SEVERITY_ELEMENT_NAME); + warningElement.setAttribute(NAME_STRING, WARNING_STRING); + warningElement.setAttribute(SEVERITY_CODE_STRING, String.valueOf(IStatus.WARNING)); + rootElement.appendChild(warningElement); + Element infoElement = doc.createElement(VersionCompareConstants.SEVERITY_ELEMENT_NAME); + infoElement.setAttribute(NAME_STRING, INFO_STRING); + infoElement.setAttribute(SEVERITY_CODE_STRING, String.valueOf(IStatus.INFO)); + rootElement.appendChild(infoElement); + // get children statuses + IStatus[] children = status.getChildren(); + if (children.length == 0) + return doc; + // create element for each children status + for (int i = 0; i < children.length; i++) { + switch (children[i].getSeverity()) { + case IStatus.ERROR : { + Element childElement = doc.createElement(VersionCompareConstants.CHILDREN_ELEMENT_NAME); + childElement.setAttribute(VersionCompareConstants.CODE_ATTRIBUTE_NAME, String.valueOf(children[i].getCode())); + childElement.setAttribute(VersionCompareConstants.MESSAGE_ATTRIBUTE_NAME, String.valueOf(children[i].getMessage())); + errorElement.appendChild(childElement); + break; + } + case IStatus.WARNING : { + Element childElement = doc.createElement(VersionCompareConstants.CHILDREN_ELEMENT_NAME); + childElement.setAttribute(VersionCompareConstants.CODE_ATTRIBUTE_NAME, String.valueOf(children[i].getCode())); + childElement.setAttribute(VersionCompareConstants.MESSAGE_ATTRIBUTE_NAME, String.valueOf(children[i].getMessage())); + warningElement.appendChild(childElement); + break; + } + case IStatus.INFO : { + Element childElement = doc.createElement(VersionCompareConstants.CHILDREN_ELEMENT_NAME); + childElement.setAttribute(VersionCompareConstants.CODE_ATTRIBUTE_NAME, String.valueOf(children[i].getCode())); + childElement.setAttribute(VersionCompareConstants.MESSAGE_ATTRIBUTE_NAME, String.valueOf(children[i].getMessage())); + infoElement.appendChild(childElement); + break; + } + } + } + return doc; + } + + /** + * writes out <code>doc</code> to the xml file denoted by <code>fileName</code> + * @param doc Document instance + * @param fileName String which denotes a xml file + * @throws CoreException <p>if any TransformerConfigurationException, or TransformerException has been caught</p> + */ + private void writeToXML(Document doc, String fileName) throws CoreException { + if (!isXMLfile(fileName)) + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_invalidXMLFileNameMsg, fileName), null)); + // create a DOMSource instance + DOMSource doms = new DOMSource(doc); + // create a File instance + File file = new File(fileName); + // create a StreamResult instance of file + StreamResult streamResult = new StreamResult(file); + try { + // set output properties + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(); + Properties properties = transformer.getOutputProperties(); + properties.setProperty(OutputKeys.ENCODING, VersionCompareConstants.ENCODING_TYPE); + transformer.setOutputProperties(properties); + // write out doc + transformer.transform(doms, streamResult); + } catch (TransformerConfigurationException tce) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_failedWriteXMLFileMsg, fileName), tce)); + } catch (TransformerException te) { + throw new CoreException(new Status(IStatus.ERROR, VersionCompareConstants.PLUGIN_ID, IStatus.ERROR, NLS.bind(Messages.VersionCompareDispatcher_failedWriteXMLFileMsg, fileName), te)); + } + } + + /** + * checks whether <code>file</code> represents a XML file + * @param fileName String name of a file + * @return <code>true</code> if <code>file</code> represents a XML file, + * <code>false</coc + */ + private boolean isXMLfile(String fileName) { + IPath path = new Path(fileName); + if (path.isValidPath(fileName)) { + String extension = path.getFileExtension(); + if (extension == null) + return false; + if (!extension.equals(VersionCompareConstants.XML_FILE_EXTENSION)) + return false; + return true; + } + return false; + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionVerifier.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionVerifier.java new file mode 100755 index 000000000..1e08b8d96 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersionVerifier.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.*; +import org.eclipse.core.runtime.*; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.tools.versioning.IVersionCompare; +import org.eclipse.pde.tools.versioning.VersionCompareFactory; + +/** + * This is the headless version compare application. + */ +public class VersionVerifier implements IPlatformRunnable { + // path of new configuration file or feature directory + File newPath; + // path of old configuration file or feature directory + File oldPath; + // if need to compare plugins as objects + boolean needPluginCompare = false; + // path of the option file + String optionFilePath; + // path of the result file + String resultPath; + boolean isConfiguration = false; + boolean isDir = false; + boolean consoleOutput = false; + + /* + * Prints the results using the given print writer. Closes the writer + * after processing. + */ + private void printResult(IStatus status, PrintWriter writer) { + try { + // write out compare results to result file and also display them on screen + writer.println(Messages.VersionVerifier_summaryMsg); + if (status.isOK()) { + writer.println(Messages.VersionVerifier_compareOKMsg); + } else { + IStatus[] childStatus = status.getChildren(); + writer.println(); + if (childStatus.length == 0) { + writer.println(status.getMessage()); + } else { + for (int i = 0; i < childStatus.length; i++) { + if (childStatus[i].isOK()) { + continue; + } + if (childStatus[i].getException() != null) { + String msg = childStatus[i].getMessage(); + if (!msg.equals("")) { //$NON-NLS-1$ + writer.println(msg); + } + writer.println(childStatus[i].getException().getMessage()); + } else { + writer.println(childStatus[i].getMessage()); + } + } + writer.println(); + writer.println(NLS.bind(Messages.VersionVerifier_messageNumberMsg, String.valueOf(childStatus.length))); + } + } + } finally { + if (writer != null) { + writer.close(); + writer = null; + } + } + } + + /* + * Print out the given status object. + */ + private void printResult(IStatus status, IVersionCompare versionCompare) { + // create a File instance for the result file + File resultFilePath = new File(resultPath); + File parentFilePath = null; + try { + if (!resultFilePath.exists()) { + // if the result file is not exist, we need to check its parent file + parentFilePath = resultFilePath.getParentFile(); + if (parentFilePath.exists()) { + // if the parent file exists, we create the result file directly + resultFilePath.createNewFile(); + } else { + // if the parent file does not exist, we create the parent file(directory) first + parentFilePath.mkdirs(); + // and then create the result file + resultFilePath.createNewFile(); + } + } else { + if (!resultFilePath.isFile()) { + System.out.println(NLS.bind(Messages.VersionVerifier_createFileErrorMsg, Messages.VersionVerifier_pathIsNotFileMsg)); + return; + } + } + } catch (IOException ioe) { + System.out.println(NLS.bind(Messages.VersionVerifier_createFileErrorMsg, resultFilePath)); + return; + } + // write the results to a XML file + try { + versionCompare.writeToXML(status, resultFilePath.getAbsolutePath()); + } catch (CoreException ce) { + ce.printStackTrace(); + } + // check to see if the user requested console output as well + if (consoleOutput) + printResult(status, new PrintWriter(System.out)); + } + + /* + * Print out a message describing how to run the application. + */ + private void printUsage() { + StringBuffer buffer = new StringBuffer(); + buffer.append("Usage: java -cp startup.jar org.eclipse.core.launcher.Main -application org.eclipse.pde.tools.versioning.application -clean -new [path] -old [path] -option [path] -output [path] [-pluginCompare] [-consoleOutput]"); //$NON-NLS-1$ + buffer.append("\n-new: path of new configuration file or features directory"); //$NON-NLS-1$ + buffer.append("\n-old: path of old configuration file or features directory"); //$NON-NLS-1$ + buffer.append("\n-option: path of compare option file (optional)"); //$NON-NLS-1$ + buffer.append("\n-output: path of result XML file"); //$NON-NLS-1$ + buffer.append("\n-pluginCompare: if need to compare plugins as objects (optional)"); //$NON-NLS-1$ + buffer.append("\n-consoleOutput: print results to the system console (optional)"); //$NON-NLS-1$ + System.out.println(buffer.toString()); + } + + /* + * Look at the given file path and determine if it is a configuration file + * or a features directory. Return the associated File object. + */ + private File processPath(String pathString) { + IPath path = new Path(pathString); + File file = path.toFile(); + // check to see if we are pointing to a configuration file + if ("platform.xml".equalsIgnoreCase(path.lastSegment())) //$NON-NLS-1$ + isConfiguration = true; + // check if its a directory + isDir = file.isDirectory(); + return file; + } + + /* + * Process the command-line arguments and set up the local variables + * for the application. + */ + private boolean processCommandLine(String[] parameters) { + // get parameters + for (int i = 0; i < parameters.length; i++) { + if (parameters[i].equalsIgnoreCase("-new")) { //$NON-NLS-1$ + if ((i + 1 < parameters.length) && !parameters[i + 1].startsWith("-")) { //$NON-NLS-1$ + i++; + newPath = processPath(parameters[i]); + if (isConfiguration && isDir) { + System.out.println(Messages.VersionVerifier_mixedInputMsg); + return false; + } + } + } else if (parameters[i].equalsIgnoreCase("-old")) { //$NON-NLS-1$ + if ((i + 1 < parameters.length) && !parameters[i + 1].startsWith("-")) { //$NON-NLS-1$ + i++; + oldPath = processPath(parameters[i]); + if (isConfiguration && isDir) { + System.out.println(Messages.VersionVerifier_mixedInputMsg); + return false; + } + } + } else if (parameters[i].equalsIgnoreCase("-option")) { //$NON-NLS-1$ + if ((i + 1 < parameters.length) && !parameters[i + 1].startsWith("-")) { //$NON-NLS-1$ + i++; + optionFilePath = parameters[i]; + } + } else if (parameters[i].equalsIgnoreCase("-output")) { //$NON-NLS-1$ + if ((i + 1 < parameters.length) && !parameters[i + 1].startsWith("-")) { //$NON-NLS-1$ + i++; + resultPath = parameters[i]; + } + } else if (parameters[i].equalsIgnoreCase("-pluginCompare")) { //$NON-NLS-1$ + needPluginCompare = true; + } else if (parameters[i].equalsIgnoreCase("-consoleOutput")) { //$NON-NLS-1$ + consoleOutput = true; + } + } + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IPlatformRunnable#run(java.lang.Object) + */ + public Object run(Object args) { + // cast the args object into String array + boolean ok = processCommandLine((String[]) args); + // if any necessary parameter is missed, we display help message to tell + // user how to run this application in command line + if (!ok || newPath == null || oldPath == null || resultPath == null) { + printUsage(); + return null; + } + // do the work, compare features included in new configuration(or feature directory) + // with those included in old configuration(or feature directory) + IVersionCompare ivc = new VersionCompareFactory().getVersionCompare(); + IStatus status = null; + try { + status = ivc.checkFeatureVersions(newPath, oldPath, needPluginCompare, optionFilePath == null || optionFilePath.trim().equals("") ? null : new File(optionFilePath), null); //$NON-NLS-1$ + } catch (CoreException ce) { + System.out.print(Messages.VersionVerifier_coreExceptionMsg); + System.out.println(ce.getMessage()); + return null; + } catch (Exception e){ + e.printStackTrace(); + } + // print out the results + printResult(status, ivc); + return null; + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningDirFilter.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningDirFilter.java new file mode 100755 index 000000000..32428a457 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningDirFilter.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.File; +import java.io.FileFilter; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +/** + * This class implements the FileFilter interface. Instance of this class may be + * passed to the listFiles(FileFilter) method of the File class + * + */ +public class VersioningDirFilter implements FileFilter { + private String dirName; + + /** + * Constructor for the class. + */ + public VersioningDirFilter(String dirName) { + super(); + this.dirName = dirName; + } + + /** + * Constructor for the class. + */ + public VersioningDirFilter() { + super(); + this.dirName = null; + } + + /** + * Returns <code>true</code> if the given file is a directory and <code>false</code> otherwise. + * + * @param file the abstract pathname to be tested + * @return <code>true</code> if the file is a directory and <code>false</code> otherwise + */ + public boolean accept(File file) { + if (file.isDirectory()) { + if (dirName != null) { + IPath path = new Path(file.getAbsolutePath()); + if (path.lastSegment().equals(dirName)) + return true; + return false; + } + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningFeatureFileFilter.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningFeatureFileFilter.java new file mode 100755 index 000000000..217abc911 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningFeatureFileFilter.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.File; +import java.io.FileFilter; + +/** + * This class implements the FileFilter interface. Instance of this class may be + * passed to the listFiles(FileFilter) method of the File class + * + */ +public class VersioningFeatureFileFilter implements FileFilter { + private String fileName; + + /** + * Constructor for the class. + * + * @param fileName the filename to filter on + */ + public VersioningFeatureFileFilter(String fileName) { + super(); + this.fileName = fileName; + } + + /** + * Returns <code>true</code> if the given file matches the name for this + * filter and <code>false</code> otherwise. + * + * @param file the abstract pathname to be tested + * @return <code>true</code> if the file matches this filter's name and<code>false</code> otherwise + */ + public boolean accept(File file) { + if (file.isDirectory()) { + return false; + } + + String name = file.getName(); + if (name.equals(fileName)) { + return true; + } else { + return false; + } + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningFilenameFilter.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningFilenameFilter.java new file mode 100755 index 000000000..75789826f --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningFilenameFilter.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.regex.Pattern; + +/** + * This class implements the FilenameFilter interface. Instance of this class + * may be passed to the listFiles(FilenameFilter) method of the File class + * + */ +public class VersioningFilenameFilter implements FilenameFilter { + private Pattern pattern; + + /** + * Constructor for this class. Initialize it with the given regular expression. + * + * @param featureRegex the regular expression to use for this filter + */ + public VersioningFilenameFilter(String featureRegex) { + pattern = Pattern.compile(featureRegex); + } + + /** + * Returns <code>true</code> if the given filename matches the regular + * expression for this filter, and <code>false</code> otherwise. + * + * @param dir the directory in which the file was found + * @param name the name of the file to be tested + * @return <code>true</code> if the name should be included in the file list and<code>false</code> otherwise + */ + public boolean accept(File dir, String name) { + // Creates a matcher and matches the given input against the pattern. + return pattern.matcher(new File(name).getName()).matches(); + } +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningProgressMonitorWrapper.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningProgressMonitorWrapper.java new file mode 100755 index 000000000..6a6c41766 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/VersioningProgressMonitorWrapper.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.internal.versioning; + +import org.eclipse.core.runtime.*; + +/** + * a wrapper class of IProgressMonitor which provides methods to + * get access to the wrapped IProgressMonitor instance + */ +public class VersioningProgressMonitorWrapper extends ProgressMonitorWrapper { + // the wrapped monitor is not supposed to be used more than once, + // this flag will be set as true when the first time it is started, and + // will not be set as false when the monitor's task is done. + private boolean monitorStarted; + + /** + * constructor + * @param monitor + */ + public VersioningProgressMonitorWrapper(IProgressMonitor monitor) { + super(monitor); + monitorStarted = false; + } + + /** + * get a new sub monitor of wrapped progress monitor + * + * @return IProgressMonitor instance + */ + public IProgressMonitor getSubMonitor(int ticks) { + return new SubProgressMonitor(this.getWrappedProgressMonitor(), ticks); + } + + /** + * starts the wrapped monitor + * @see IProgressMonitor#beginTask(String, int) + */ + public void beginTask(String name, int totalWork) { + if (!monitorStarted) { + this.getWrappedProgressMonitor().beginTask(name, totalWork); + monitorStarted = true; + } + } + + /** + * if <code>monitor</code> is <code>null</code> return a new NullProgressMonitor instance, + * otherwise return <code>monitor</code> + * @param monitor IProgressMonitor instance + * @return IProgressMonitor instance + */ + public static IProgressMonitor monitorFor(IProgressMonitor monitor) { + return monitor == null ? new NullProgressMonitor() : monitor; + } +}
\ No newline at end of file diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/messages.properties b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/messages.properties new file mode 100755 index 000000000..92ebcecdb --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/internal/versioning/messages.properties @@ -0,0 +1,102 @@ +############################################################################### +# Copyright (c) 2006 IBM Corporation and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### +FeatureVersionCompare_errorInVerifyPluginMsg=Error occurred while verifying plugin "{0}" in feature (id "{1}"; version: "{2}"; location: "{3}"). +FeatureVersionCompare_errorReasonMsg=Errors occurred during feature verification. +FeatureModelTable_urlConvertErrorMsg=Could not convert "{0}" into a valid URL. +FeatureVersionCompare_featureFileErrorMsg=Feature manifest (feature.xml) file could not be successfully loaded from location "{0}". +FeatureVersionCompare_verifyingFeatureMsg=Comparing Features...... +FeatureVersionCompare_comparingFeatureMsg=Comparing Feature {0} +FeatureVersionCompare_comparingReferenceMsg=Comparing references...... +FeatureModelTable_featureFileParseErrorMsg=Could not parse feature file "{0}", it may not exist. +FeatureModelTable_featureSourceErrorMsg="{0}" is neither a configuration file nor a feature directory. +FeatureVersionCompare_incorrectNewVersionMsg=The version of the feature (id: "{0}"; version: "{1}"; location: "{2}") should be at least "{3}". +FeatureVersionCompare_incorrectNewVersionMsg2=The version of the feature (id: "{0}"; version: "{1}"; location: "{2}") should be higher than "{3}". +FeatureVersionCompare_incorrectPluginVersionMsg=The version of the plugin (id: "{0}"; version: "{1}"; location: "{2}") should be at least "{3}". +FeatureVersionCompare_incorrectPluginVersionChange=Plugin (id: "{0}"; version: "{1}") got "{2}" change, but its "{3}" version has been increased. +FeatureVersionCompare_inputErrorMsg=Inputs "{0}" or "{1}" could not be resolved correctly. They should be either both paths of configurations or both paths of feature directories. +FeatureVersionCompare_sourceIncludedFeatureNotFoundMsg=Compare source feature "{0}" is included in feature (id: "{1}"; version: "{2}", location: "{3}"), but its feature model has not been found. +FeatureVersionCompare_destIncludedFeatureNotFoundMsg=Compare destination feature "{0}" is included in feature (id: "{1}"; version: "{2}", location: "{3}"), but its feature model has not been found. +FeatureVersionCompare_lowerNewVersion=The version "{0}" of {1} "{2}" in feature (id: "{3}";version: "{4}"; location: "{5}") is lower than its version "{6}" in feature (id: "{3}";version: "{7}"; location: "{8}"). +FeatureVersionCompare_nestedErrorOccurredMsg=Feature (id: "{0}";version: "{1}"; location: "{2}") could not be verified since errors occurred while verifying its included features or plugins. +FeatureModelTable_couldNotReadConfigFileMsg=Could not read configuration file "{0}". +FeatureVersionCompare_newVersionLowerThanOldMsg=The Version of feature (id: "{0}"; version: "{1}", location: "{2}") is lower than the version of feature(id: "{0}"; version: "{3}", location: "{4}"). +FeatureVersionCompare_noFeaturesFoundInConfigMsg=No feature has been found in configuration: "{0}". +FeatureVersionCompare_noFeatureFoundMsg=No feature has been found under directory: "{0}". +FeatureVersionCompare_versionChangeIncorrectMsg=Feature (id: "{0}"; version: "{1}", location: "{2}") got "{3}" change, but its "{4}" version has been increased. +FeatureVersionCompare_pluginNotFoundMsg=Plugin "{0}" is included in feature (id: "{1}"; version: "{2}"; location: "{3}"), but it has not been found under "{4}". +FeatureVersionCompare_newAddedFeaturePlingMsg= {0} "{1}" is new added into feature (id: "{2}"; version: "{3}", location: "{4}"). +FeatureVersionCompare_newIntroducedFeatureMsg=Feature (id: "{0}"; version: "{1}"; location: "{2}") is a new introduced feature. +FeatureVersionCompare_deletedFeaturePluginMsg= {0} "{1}" no longer exists in feature (id: "{2}"; version: "{3}", location: "{4}"). + +VersionVerifier_compareOKMsg=All of the versions are correct. +VersionVerifier_coreExceptionMsg=CoreException occurred: +VersionVerifier_createFileErrorMsg=Exception occurred while creating the output file "{0}". +VersionVerifier_mixedInputMsg=Inputs should be either two paths of configurations or two paths of feature directories, but not mix of them. +VersionVerifier_pathIsNotFileMsg=The specified output file path is not a file. +VersionVerifier_messageNumberMsg=Number of message: {0} +VersionVerifier_summaryMsg=Comparison result Summary: + +VersionCompareDispatcher_fileNotFoundMsg=Could not find exclusion list file "{0}". +VersionCompareDispatcher_readPropertyFailedMsg=Could not read property from file "{0}". +VersionCompareDispatcher_invalidXMLFileNameMsg="{0}" is not a valid XML file. +VersionCompareDispatcher_failedWriteXMLFileMsg=Failed to write out the compare result to "{0}". +VersionCompareDispatcher_closeFileFailedMsg=Failed to close file "{0}". +VersionCompareDispatcher_failedCreateDocMsg=Failed to create XML file for the compare result. + +JavaClassVersionCompare_classFileNotLoadedMsg=Could not read class file from: "{0}". +JavaClassVersionCompare_classErrorOccurredMsg=Error occurred while comparing class "{0}". +JavaClassVersionCompare_classMajorChangeMsg=Class "{0}" has a major change. +JavaClassVersionCompare_classMinorChangeMsg=Class "{0}" has a minor change. +JavaClassVersionCompare_classModifierChangedMsg=Modifier of "{0}" has been changed from "{1}" to "{2}". +JavaClassVersionCompare_differentClassNameMsg=Selected two classes(or java sources) do not have the same class name. One is "{0}", another is "{1}". +JavaClassVersionCompare_differentSuperClassMsg=Super class of "{0}" has been changed from "{1}" to "{2}". +JavaClassVersionCompare_deletedInterfaceMsg=Interface "{0}" has no longer been implemented in class "{1}". +JavaClassVersionCompare_deprecatedChangedMsg={0} "{1}" has been deprecated in class "{2}". +JavaClassVersionCompare_descriptorChangedMsg=Type of field "{0}" has been changed from "{1}" to "{2}" in class "{3}". +JavaClassVersionCompare_inputStreamErrMsg=Could not read class file from provided InputSteam. +JavaClassVersionCompare_ModifierChangedMsg=Modifier of {0} "{1}" has been changed from "{2}" to "{3}". +JavaClassVersionCompare_newAddedInterfaceMsg=Interface "{0}" has been new implemented in class "{1}". +JavaClassVersionCompare_newAddedMsg={0} "{1}" is new added into class "{2}". +JavaClassVersionCompare_noLongerExistMsg={0} "{1}" no longer exists in class "{2}". +JavaClassVersionCompare_classMicroChange=Class "{0}" has a micro change. +JavaClassVersionCompare_newAddedExceptionMsg=Exceptions "{0}" have been new added to method "{1}" in class "{2}". +JavaClassVersionCompare_noLongerExistExceptionMsg= Exceptions "{0}" are no longer thrown from method "{1}" in class "{2}". +JavaClassVersionCompare_unexpectedTypeMsg= "{0}" is not an expected input type. +JavaClassVersionCompare_comparingClassMsg=Comparing class...... + +PluginVersionCompare_classPathJarNotFoundMsg=Corresponding jar file has not been found for class path element "{0}" in plugin "{1}". +PluginVersionCompare_couldNotReadManifestMsg=Could not read manifest file of plugin "{0}" correctly. +PluginVersionCompare_couldNotOpenJarMsg=Could not open jar file "{0}". +PluginVersionCompare_comparingPluginMsg=Comparing Plugin +PluginVersionCompare_couldNotReadAttributesMsg=Could not read attributes from manifest file of bundle "{0}". +PluginVersionCompare_couldNotReadClassPathMsg=Could not read Bundle-ClassPath attributes from manifest file of plugin "{0}". +PluginVersionCompare_couldNotReadClassMsg=Could not read class file "{0}". +PluginVersionCompare_couldNotParseHeaderMsg=Could not parse manifest header "{0}". +PluginVersionCompare_couldNotConvertManifestMsg=Could not convert "{0}" into manifest file. +PluginVersionCompare_destinationClassNotFoundMsg=Class "{0}" is a new added class. +PluginVersionCompare_noPluginConverterInstanceMsg=Could not get PluginConverter instance from OSGi framework. +PluginVersionCompare_failCloseJarMsg=Failed to close jar file "{0}". +PluginVersionCompare_failCloseJarEntryAfterExtractMsg=Failed to close jar entry "{0}". +PluginVersionCompare_failExtractJarEntryMsg=Failed to extract jar entry "{0}" to temporary file. +PluginVersionCompare_finishedProcessPluginMsg=Finished process plugin "{0}", used {1} milliseconds. +PluginVersionCompare_failOpenJarEntryMsg=Failed to open jar entry "{0}". +PluginVersionCompare_failOpenTmpJarMsg=Failed to open temporary jar file "{0}" of jar entry "{1}" of "{2}". +PluginVersionCompare_failCloseTmpAfterCreateMsg=Failed to close temporary file "{0}" after creating. +PluginVersionCompare_errorWhenCompareClassesMsg=Error occurred while comparing {0}. +PluginVersionCompare_failCreatTmpJarMsg=Failed to create temporary file "{0}". +PluginVersionCompare_inValidClassPathMsg="{0}" is not a valid class path element in manifest file of "{1}". +PluginVersionCompare_inputNotExistMsg="{0}" does not represent an exist file or directory. +PluginVersionCompare_noValidClassFoundMsg=No class in "{0}" is need to be compared. +PluginVersionCompare_pluginMajorChangeMsg=Plugin "{0}" has a major change. +PluginVersionCompare_pluginMinorChangeMsg=Plugin "{0}" has a minor change. +PluginVersionCompare_pluginMicroChangeMsg=Plugin "{0}" has a micro change. +PluginVersionCompare_pluginErrorOccurredMsg=Error occurred while comparing plugin "{0}". +PluginVersionCompare_sourceClassNotFoundMsg=Class "{0}" is no longer existed. diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Entries b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Entries new file mode 100644 index 000000000..0e2e75717 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Entries @@ -0,0 +1,4 @@ +/ICompareResult.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +/IVersionCompare.java/1.1/Wed Dec 28 16:06:22 2011//Tr20080922 +/VersionCompareFactory.java/1.1/Thu Oct 5 17:45:07 2006//Tr20080922 +D diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Repository b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Repository new file mode 100644 index 000000000..a8f63a18f --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Repository @@ -0,0 +1 @@ +org.eclipse.sdk.tests-feature/plugins/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Root b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Root new file mode 100644 index 000000000..2d37d165b --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@dev.eclipse.org:/cvsroot/eclipse diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Tag b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Tag new file mode 100644 index 000000000..25d2caaa5 --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/CVS/Tag @@ -0,0 +1 @@ +Nr20080922 diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/ICompareResult.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/ICompareResult.java new file mode 100755 index 000000000..0263956bb --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/ICompareResult.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.versioning; + +import org.eclipse.core.runtime.MultiStatus; + +/** + * This interface provides methods to access to compare result information of a plugin or class + * <p> + * <b>Note:</b> This interface should not be implemented by clients. + * </p><p> + * <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to + * change significantly before reaching stability. It is being made available at this early stage to solicit feedback + * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken + * (repeatedly) as the API evolves. + * </p> + */ +public interface ICompareResult { + /** + * gets MuliStatus instance which contains messages created when verify a plugin or class + * @return MultiStatus instance + */ + public MultiStatus getResultStatus(); + + /** + * gets overall change happened on a plugin or class + * <p> + * The value of change is an int number of the following: + * <ul> + * <li>{@link IVersionCompare#ERROR_OCCURRED}</li> + * <li>{@link IVersionCompare#MAJOR_CHANGE}</li> + * <li>{@link IVersionCompare#MINOR_CHANGE}</li> + * <li>{@link IVersionCompare#NEW_ADDED}</li> + * <li>{@link IVersionCompare#NO_LONGER_EXIST}</li> + * <li>{@link IVersionCompare#MICRO_CHANGE}</li> + * <li>{@link IVersionCompare#QUALIFIER_CHANGE}</li> + * <li>{@link IVersionCompare#NO_CHANGE}</li> + * </ul> + * </p> + * @return change int number which indicates the overall change happened on a plugin or class + * @see IVersionCompare#ERROR_OCCURRED + * @see IVersionCompare#MAJOR_CHANGE + * @see IVersionCompare#MINOR_CHANGE + * @see IVersionCompare#NEW_ADDED + * @see IVersionCompare#NO_LONGER_EXIST + * @see IVersionCompare#MICRO_CHANGE + * @see IVersionCompare#QUALIFIER_CHANGE + * @see IVersionCompare#NO_CHANGE + */ + public int getChange(); +} diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/IVersionCompare.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/IVersionCompare.java new file mode 100644 index 000000000..faf0eebde --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/IVersionCompare.java @@ -0,0 +1,547 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.versioning; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import org.eclipse.core.runtime.*; +import org.eclipse.jdt.core.util.IClassFileReader; + +/** + * This interface provides methods to clients for verification of version numbers + * for features and plug-ins contained within an Eclipse configuration or under a directory. + * <p> + * Clients may implement this interface. However, in most cases clients will use the provided + * factory to acquire an implementation for use. + * </p><p> + * <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to + * change significantly before reaching stability. It is being made available at this early stage to solicit feedback + * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken + * (repeatedly) as the API evolves. + * </p> + */ +public interface IVersionCompare { + + /** + * This String type constant is a property name in compare option file; + * It's value indicates exclusive feature ids. + */ + public static final String EXCLUDE_FEATURES_OPTION = "exclude.features"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates inclusive feature ids. + */ + public static final String INCLUDE_FEATURES_OPTION = "include.features"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates exclusive plugin ids. + */ + public static final String EXCLUDE_PLUGINS_OPTION = "exclude.plugins"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates inclusive plugin ids. + */ + public static final String INCLUDE_PLUGINS_OPTION = "include.plugins"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates exclusive operation systems. + */ + public static final String EXCLUDE_OS_OPTION = "exclude.os"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates inclusive operation systems. + */ + public static final String INCLUDE_OS_OPTION = "include.os"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates exclusive windows systems. + */ + public static final String EXCLUDE_WS_OPTION = "exclude.ws"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates inclusive windows systems. + */ + public static final String INCLUDE_WS_OPTION = "include.ws"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates exclusive system architecture specifications. + */ + public static final String EXCLUDE_ARCH_OPTION = "exclude.arc"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates inclusive system architecture specifications. + */ + public static final String INCLUDE_ARCH_OPTION = "include.arc"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates exclusive locale language specifications. + */ + public static final String EXCLUDE_NL_OPTION = "exclude.nl"; //$NON-NLS-1$ + + /** + * This String type constant is a property name in compare option file; + * It's value indicates inclusive locale language specifications + */ + public static final String INCLUDE_NL_OPTION = "include.nl"; //$NON-NLS-1$ + + /** + * This int type constant indicates overall compare result of a feature + */ + public static final int FEATURE_OVERALL_STATUS = 0x0001; + + /** + * This int type constant indicates detail compare messages of a feature + */ + public static final int FEATURE_DETAIL_STATUS = 0x0002; + + /** + * This int type constant indicates overall compare result of a plugin + */ + public static final int PLUGIN_OVERALL_STATUS = 0x0004; + + /** + * This int type constant indicates detail compare messages of a plugin + */ + public static final int PLUGIN_DETAIL_STATUS = 0x0008; + + /** + * This int type constant indicates overall compare result of a class + */ + public static final int CLASS_OVERALL_STATUS = 0x0010; + + /** + * This int type constant indicates detail compare messages of a class + */ + public static final int CLASS_DETAIL_STATUS = 0x0020; + + /** + * This int type constant indicates process error messages (e.g. IO error) + */ + public static final int PROCESS_ERROR_STATUS = 0x0040; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates some error occurred during comparing + */ + public final static int ERROR_OCCURRED = 0x0080; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates a major change of a feature, plugin, or class + */ + public final static int MAJOR_CHANGE = 0x0100; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates a minor change of a feature, plugin, or class + */ + public final static int MINOR_CHANGE = 0x0200; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates there is one or more new added attribute in a feature, plugin, or class + */ + public final static int NEW_ADDED = 0x0400; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates there is one or more attribute have been deleted from a feature, plugin, or class + */ + public final static int NO_LONGER_EXIST = 0x0800; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates a minor change of a feature, plugin, or class + */ + public final static int MICRO_CHANGE = 0x1000; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates a qualifier change of a feature, plugin, or class + */ + public final static int QUALIFIER_CHANGE = 0x2000; + + /** + * This int type constant is a compare result of feature, plugin, class; + * It indicates no change in a feature, plugin, or class + */ + public final static int NO_CHANGE = 0x4000; + + /** + * Verify two sets of features and return a status object indicating whether or not the version numbers + * of the features (an optionally of their included plug-ins) have been incremented appropriated based + * on the changes between the two sets. + * <p> + * Feature Version update priority: + * Versions have four parts: major, minor, micro, and qualifier. + * There are four corresponding kinds of version update: major update, + * minor update, micro update, qualifier update. Besides, there are two + * other kinds of update: new_added update(if some features or plugins + * have been new added to the feature), and no_longer_exist update(if + * some features or plugins have been deleted from the feature). Consequently, + * major update has the highest priority, then minor update (currently + * new_added and no_longer_exist are considered as minor updates), then + * micro update, and qualifier update has the lowest priority. If we talk + * about version update of a feature or a plugin, we mean the update with + * the highest priority. + * </p><p> + * Feature Version update rule: + * Usually, a feature includes other features and plug-ins, + * e.g. : + * old version of "f1": + * <pre> + * <?xml version="1.0" encoding="UTF-8"?> + * <feature id="f1" label="aaa" version="1.0.0.v2005"> + * <includes id="f2" version="1.0.0.v2005" name="bbb"/> + * <includes id="f3" version="1.0.0.v2005" name="ccc"/> + * <plugin id="p1" version="1.0.0.v2005"/> + * <plugin id="p2" version="1.0.0.v2005"/> + * <plugin id="p3" version="1.0.0.v2005"/> + * </feature> + * </pre> + * new version of "f1": + * <pre> + * <?xml version="1.0" encoding="UTF-8"?> + * <feature id="f1" label="aaa" version="3.0.0"> + * <includes id="f2" version="2.0.0.v2005" name="bbb"/> + * <includes id="f3" version="1.1.0.v2005" name="ccc"/> + * <plugin id="p1" version="1.0.1.v2005"/> + * <plugin id="p2" version="1.0.0.v2006"/> + * <plugin id="p4" version="1.0.0.v2006"/> + * </feature> + * </pre></p><p> + * The basic rule of version update is that if we have updated versions of + * any include features(f2,f3) or plugins(p1,p2), we need to update the version + * of f1. Update of f1 should be at least the highest update of its included + * features and plug-ins. For instance, "f2" has a major update, "f3" has a minor + * update, "p1" has a micro update, and "p2" has a qualifier update, "p3" no + * longer exists in "f1", "p4" has been new added to "f1". In conclusion, "f1" + * should at least have a major change(it's ok if "f1" also has a minor, micro, + * or qualifier update). + * </p><p> + * Feature Verification process: + * To verify the new version of "f1", we need to compare its included features + * and plug-ins with those included in its old version and find out the update + * with the highest priority. For included features, such as "f2" and "f3", before + * comparing them with their corresponding ones in old "f1", we need to verify + * their new versions first through recursion, and then, + * <ul> + * <li>if the new versions of "f2" is correct, we use the new version of "f2" to + * compare with the version of "f2" in old "f1". + * <li>if the new versions of "f2" is wrong, we try to generate a recommended new + * version for "f2", and then, + *<li>if we can generate a recommended new version for "f2", we use the recommended + * new version to compare with the version of "f2" in old "f1". + * <li>if we can not generate a recommended new version for "f2", there must be + * some error occurred. We will create a warning message for that error and + * we will also create a message to tell user that "f1" can not be verified + * correctly, since we can't verify some of its included features. (The process + * will not stop if we find an error. It will continually to compare and try + * to find problems as more as possible, and warning messages will be collected, + * and displayed at last.) + * </ul> + * </p><p> + * We must increment the major part if: + * <ul> + * <li>an included feature has incremented the major part of its version + * <li>an included plug-in has incremented the major part of its version + * </ul> + * </p><p> + * We must increment the minor part if: + * <ul> + * <li>an included feature has incremented the minor part of its version + * <li>an included plug-in has incremented the minor part of its version + * <li>if a new plug-in was added to the feature + * <li>if a plug-in was removed from the feature + * <li>if a new included feature was added to the feature + * <li>if an included feature was removed from the feature + * </ul> + * </p><p> + * We must increment the micro part if: + * <ul> + * <li>an included feature has incremented the micro part of its version + * <li>an included plug-in has incremented the micro part of its version + * </ul> + * </p><p> + * We must increment the qualifier if: + * <ul> + * <li>an included feature has incremented the qualifier part of its version + * <li>an included plug-in has incremented the qualifier part of its version + * </ul> + * </p> + * + * @param file1 File instance which denotes a configuration file or a feature directory + * @param file2 File instance which denotes a configuration file or a feature directory + * @param needPluginCompare if <code>true</code>, the method will compare plugin objects; if <code>false</code> + * it just compares plugin names and versions. + * @return IStatus instance which contains child status. Each child status indicates + * an error or warning of one included feature or plug-in + * @param compareOptionFile a property file which indicates exclusion and inclusion for feature compare + * @param monitor IProgressMonitor instance which will monitor the progress during feature comparing + * @throws CoreException + * @see #checkFeatureVersions(String, String, boolean, File, IProgressMonitor) + * @see #checkFeatureVersions(URL, URL, boolean, File, IProgressMonitor) + */ + public IStatus checkFeatureVersions(File file1, File file2, boolean needPluginCompare, File compareOptionFile, IProgressMonitor monitor) throws CoreException; + + /** + * As per {@link #checkFeatureVersions(File, File, boolean, File, IProgressMonitor)} except the given parameters are strings + * pointing to the configuration file or features directory. + * + * @param path1 path which denotes an eclipse configuration XML file or a feature directory + * @param path2 path which denotes an eclipse configuration XML file or a feature directory + * @param needPluginCompare if <code>true</code>, the method will compare plugin objects; if <code>false</code> + * it just compares plugin names and versions. + * @param compareOptionFile a property file which indicates exclusion and inclusion for feature compare + * @param monitor IProgressMonitor instance which will monitor the progress during feature comparing + * @return a status containing the comparison result + * @throws CoreException + * @see #checkFeatureVersions(File, File, boolean, File, IProgressMonitor) + * @see #checkFeatureVersions(URL, URL, boolean, File, IProgressMonitor) + */ + public IStatus checkFeatureVersions(String path1, String path2, boolean needPluginCompare, File compareOptionFile, IProgressMonitor monitor) throws CoreException; + + /** + * As per {@link #checkFeatureVersions(File, File, boolean, File, IProgressMonitor)} except the given parameters are urls + * pointing to configuration files. + * + * @param configURL1 reference to an eclipse configuration XML file + * @param configURL2 reference to an eclipse configuration XML file + * @param needPluginCompare if <code>true</code>, the method will compare plugin objects; if <code>false</code> + * it just compares plugin names and versions. + * @param compareOptionFile a property file which indicates exclusion and inclusion for feature compare + * @param monitor IProgressMonitor instance which will monitor the progress during feature comparing + * @return a status containing the comparison result + * @throws CoreException + * @see #checkFeatureVersions(File, File, boolean, File, IProgressMonitor) + * @see #checkFeatureVersions(String, String, boolean, File, IProgressMonitor) + */ + public IStatus checkFeatureVersions(URL configURL1, URL configURL2, boolean needPluginCompare, File compareOptionFile, IProgressMonitor monitor) throws CoreException; + + /** + * <p> + * Compares the two given Java class files and returns a ICompareResult object containing a summary + * of the changes between the two files. + * </P><p> + * Steps to compare two classes: + * <ul> + * <li>1. compares class names. If two classes do not have the same class name, we don't do + * further compare.</li> + * <li>2. compares class modifiers. + * <ul> + * <li>A. If the modifier of a class has been changed from public to non-public</li> + * or from protected to modifier other than public and protected, it is a major change.</li> + * <li>B. If the modifier of a class has been changed from non-abstract to abstract, it is a major change.</li> + * <li>C. If the modifier of a class has been changed from non-final to final, it is a major change.</li></ul> + * </li> + * <li>3. compares super classes. If the super class has been changed, it is a major change.</li> + * <li>4. compares implemented interfaces. If implemented interfaces have been changed, it is a major change.</li> + * <li>5. compares fields. + * <ul> + * <li>A. If the modifier of a field has been changed from public to non-public + * or from protected to modifier other than public and protected, it is a major change.</li> + * <li>B. If the modifier of a field has been changed from non-final to final, it is a major change.</li> + * <li>C. If the modifier of a field has been changed from static to non-static, it is a major change.</li> + * <li>D. If the modifier of a field has been changed from volatile to non-volatile or vis-verser, it is a major change.</li> + * <li>E. If a public or protected field has been deprecated, it is a micro change.</li> + * <li>F. If a public or protected field has been new added into the class, it is a minor change.</li> + * <li>G. If a public or protected field has been deleted from the class, it is a major change.</li> + * <li>H. If the type of a field has been changed, it is a major change.</li> + * </ul> + * <li>6. compares methods. + * <ul> + * <li>A. If the modifier of a method has been changed from public to non-public + * or from protected to modifier other than public and protected, it is a major change.</li> + * <li>B. If the modifier of a method has been changed from non-abstract to abstract, it is a major change.</li> + * <li>C. If the modifier of a method has been changed from non-final to final, it is a major change.</li> + * <li>D. If the modifier of a method has been changed from static to non-static, it is a major change.</li> + * <li>E. If the modifier of a method has been changed from volatile to non-volatile or vis-verser, it is a major change.</li> + * <li>F. If a public or protected method has been deprecated, it is a micro change.</li> + * <li>G. If a public or protected method has been new added into the class, it is a minor change.</li> + * <li>H. If a public or protected method has been deleted from the class, it is a major change.</li> + * <li>I. If return type, number of args, or any type of args has been changed, it is considered + * as the combination of a method has been deleted and a new method has been added(major change).</li> + * <li>J. If thrown exceptions of a method have been changed, it is a minor change.</li> + * </ul> + * </li> + * </ul></p> + * + * @param javaClass1 Sting which denotes the full path of a java class file + * @param javaClass2 Sting which denotes the full path of a java class file + * @param monitor IProgressMonitor instance which will monitor the progress during class comparing + * @return ICompareResult instance which contains change on the class and messages generated when class is compared + * @throws CoreException if there was an exception during the comparison + */ + public ICompareResult checkJavaClassVersions(String javaClass1, String javaClass2, IProgressMonitor monitor) throws CoreException; + + /** + * Compares the two given Java class files and returns a ICompareResult object containing a summary + * of the changes between the two files.{@link #checkJavaClassVersions(String, String, IProgressMonitor)} + * + * @param javaClassURL1 URL which denotes a java class file + * @param javaClassURL2 URL which denotes a java class file + * @param monitor IProgressMonitor instance which will monitor the progress during class comparing + * @return ICompareResult instance which contains change on the class and messages generated when class is compared + * @throws CoreException if there was an exception during the comparison + */ + public ICompareResult checkJavaClassVersions(URL javaClassURL1, URL javaClassURL2, IProgressMonitor monitor) throws CoreException; + + /** + * Compares the two given Java class files and returns a ICompareResult object containing a summary + * of the changes between the two files.{@link #checkJavaClassVersions(String, String, IProgressMonitor)} + * + * @param javaClassFile1 File which denotes a java class file + * @param javaClassFile2 File which denotes a java class file + * @param monitor IProgressMonitor instance which will monitor the progress during class comparing + * @return ICompareResult instance which contains change on the class and messages generated when class is compared + * @throws CoreException if there was an exception during the comparison + */ + public ICompareResult checkJavaClassVersions(File javaClassFile1, File javaClassFile2, IProgressMonitor monitor) throws CoreException; + + /** + * Compares the two given Java class files and returns a ICompareResult object containing a summary + * of the changes between the two files.{@link #checkJavaClassVersions(String, String, IProgressMonitor)} + * <p> + * This method does <em>not</em> close the InputStreams and users should handle stream closure in the calling code. + * </p> + * + * @param javaClassInputStream1 InputStream which denotes a java class file + * @param javaClassInputStream2 InputStream which denotes a java class file + * @param monitor IProgressMonitor instance which will monitor the progress during class comparing + * @return ICompareResult instance which contains change on the class and messages generated when class is compared + * @throws CoreException if there was an exception during the comparison + */ + public ICompareResult checkJavaClassVersions(InputStream javaClassInputStream1, InputStream javaClassInputStream2, IProgressMonitor monitor) throws CoreException; + + /** + * Compares the two given Java class files and returns a ICompareResult object containing a summary + * of the changes between the two files.{@link #checkJavaClassVersions(String, String, IProgressMonitor)} + * + * @param classFileReader1 IClassFileReader instance which denotes a java class file + * @param classFileReader2 IClassFileReader instance which denotes a java class file + * @param monitor IProgressMonitor instance which will monitor the progress during class comparing + * @return ICompareResult instance which contains change on the class and messages generated when class is compared + * @throws CoreException if there was an exception during the comparison + */ + public ICompareResult checkJavaClassVersions(IClassFileReader classFileReader1, IClassFileReader classFileReader2, IProgressMonitor monitor) throws CoreException; + + /** + * <p> + * Compares the two given plug-ins which each other and reports a ICompareResult object indicating + * whether or not the version number of the plug-ins have been incremented appropriately + * based on the relative changes.<p> + * <p> + * Steps to compare two plugins: + * <ul> + * <li>1. compares all classes in the class path of the plugin to check if they have any change.</li> + * <li>2. if a new class has been added into the plugin, it is a minor change.</li> + * <li>3. if a class has been deleted from the plugin, it is a major change.</li> + * </ul> + * </p> + * @param plugin1 String which denotes the plug-in's jar file name or directory + * @param plugin2 String which denotes the plug-in's jar file name or directory + * @param monitor IProgressMonitor instance which will monitor the progress during plugin comparing + * @return ICompareResult instance which contains change on the plugin and messages generated when plugin is compared + * @throws CoreException if an error occurred during the comparison + */ + public ICompareResult checkPluginVersions(String plugin1, String plugin2, IProgressMonitor monitor) throws CoreException; + + /** + * Compares the two given plug-ins which each other and reports a ICompareResult object indicating + * whether or not the version number of the plug-ins have been incremented appropriately + * based on the relative changes.{@link #checkPluginVersions(String, String, IProgressMonitor)} + * + * @param pluginURL1 URL which denotes a plug-in's jar file name or directory + * @param pluginURL2 URL which denotes a plug-in's jar file name or directory + * @param monitor IProgressMonitor instance which will monitor the progress during plugin comparing + * @return ICompareResult instance which contains change on the plugin and messages generated when plugin is compared + * @throws CoreException if an error occurred during the comparison + */ + public ICompareResult checkPluginVersions(URL pluginURL1, URL pluginURL2, IProgressMonitor monitor) throws CoreException; + + /** + * Compares the two given plug-ins which each other and reports a ICompareResult object indicating + * whether or not the version number of the plug-ins have been incremented appropriately + * based on the relative changes.{@link #checkPluginVersions(String, String, IProgressMonitor)} + * + * @param pluginFile1 File which denotes a plug-in's jar file name or directory + * @param pluginFile2 File which denotes a plug-in's jar file name or directory + * @param monitor IProgressMonitor instance which will monitor the progress during plugin comparing + * @return ICompareResult instance which contains change on the plugin and messages generated when plugin is compared + * @throws CoreException if an error occurred during the comparison + */ + public ICompareResult checkPluginVersions(File pluginFile1, File pluginFile2, IProgressMonitor monitor) throws CoreException; + + /** + * Return a status object whose contents are filtered based on the given bit masks. + * <p> + * The value for <code>infoChoice</code> is an integer constructor from any combination + * of the following bit masks: + * <ul> + * <li>{@link #FEATURE_OVERALL_STATUS}</li> + * <li>{@link #FEATURE_DETAIL_STATUS}</li> + * <li>{@link #PLUGIN_OVERALL_STATUS}</li> + * <li>{@link #PLUGIN_DETAIL_STATUS}</li> + * <li>{@link #CLASS_OVERALL_STATUS}</li> + * <li>{@link #CLASS_DETAIL_STATUS}</li> + * <li>{@link #PROCESS_ERROR_STATUS}</li> + * </ul> + * </p> + * + * @param status IStatus instance of compare result + * @param infoChoice integer specifying the filter to use + * @see #FEATURE_OVERALL_STATUS + * @see #FEATURE_DETAIL_STATUS + * @see #PLUGIN_OVERALL_STATUS + * @see #PLUGIN_DETAIL_STATUS + * @see #CLASS_OVERALL_STATUS + * @see #CLASS_DETAIL_STATUS + * @see #PROCESS_ERROR_STATUS + * @return the filtered status object + */ + public IStatus processCompareResult(IStatus status, int infoChoice); + + /** + * writes out children statuses of <code>status</code> to XML file denoted by <code>fileName</code> + * <p> + * The format of the XML file is as following: + * <pre> + * <CompareResult Version="1.0"> + * <Category Name="Error" SeverityCode="4"> + * <Info Code="1" Message="The version of the feature "org.eclipse.rcp" should be at least "3.3.0"." /> + * ... + * </Category> + * <Category Name="Warning" SeverityCode="2"> + * ... + * </Category> + * <Category Name="Information" SeverityCode="1"> + * ... + * </Category> + * </CompareResult> + * </pre> + * </p> + * @param status IStatus instance + * @param fileName String name of a XML file + * @throws CoreException <p>if any nested CoreException has been caught</p> + */ + public void writeToXML(IStatus status, String fileName) throws CoreException; +}
\ No newline at end of file diff --git a/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/VersionCompareFactory.java b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/VersionCompareFactory.java new file mode 100755 index 000000000..181e009ff --- /dev/null +++ b/FROMCVS/org.eclipse.pde.tools.versioning/src/org/eclipse/pde/tools/versioning/VersionCompareFactory.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.tools.versioning; + +import org.eclipse.pde.tools.internal.versioning.VersionCompareDispatcher; + +/** + * Factory class to create objects used for comparing versions. + * <p> + * This class is not intended to be sub-classed by clients. + * </p> + */ +public final class VersionCompareFactory { + + /** + * Constructor + */ + public VersionCompareFactory() { + super(); + } + + /** + * Return an instance of a class which can be used for version comparison. + * + * @return version compare class + */ + public IVersionCompare getVersionCompare() { + return new VersionCompareDispatcher(); + } + +} |