Restored 'org.eclipse.update.core'
diff --git a/update/org.eclipse.update.core/.classpath b/update/org.eclipse.update.core/.classpath
new file mode 100644
index 0000000..b54a094
--- /dev/null
+++ b/update/org.eclipse.update.core/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="src" path="src"/>

+	<classpathentry kind="src" path="jarprocessor"/>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/update/org.eclipse.update.core/.cvsignore b/update/org.eclipse.update.core/.cvsignore
new file mode 100644
index 0000000..c5e82d7
--- /dev/null
+++ b/update/org.eclipse.update.core/.cvsignore
@@ -0,0 +1 @@
+bin
\ No newline at end of file
diff --git a/update/org.eclipse.update.core/.options b/update/org.eclipse.update.core/.options
new file mode 100644
index 0000000..7bf6197
--- /dev/null
+++ b/update/org.eclipse.update.core/.options
@@ -0,0 +1,9 @@
+org.eclipse.update.core/debug=true

+org.eclipse.update.core/debug/warning=false

+org.eclipse.update.core/debug/parsing=false

+org.eclipse.update.core/debug/install=false

+org.eclipse.update.core/debug/configuration=false

+org.eclipse.update.core/debug/type=false

+org.eclipse.update.core/debug/web=false

+org.eclipse.update.core/debug/installhandler=false

+org.eclipse.update.core/debug/reconciler=false

diff --git a/update/org.eclipse.update.core/.project b/update/org.eclipse.update.core/.project
index dec3727..e358fd4 100644
--- a/update/org.eclipse.update.core/.project
+++ b/update/org.eclipse.update.core/.project
@@ -1,11 +1,26 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>org.eclipse.update.core</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-	</buildSpec>
-	<natures>
-	</natures>
-</projectDescription>
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.update.core</name>

+	<comment></comment>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.ManifestBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.SchemaBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+		<nature>org.eclipse.pde.PluginNature</nature>

+	</natures>

+</projectDescription>

diff --git a/update/org.eclipse.update.core/.settings/org.eclipse.jdt.core.prefs b/update/org.eclipse.update.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..23d6e87
--- /dev/null
+++ b/update/org.eclipse.update.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,86 @@
+#Wed Feb 11 12:06:10 EST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=ignore
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=ignore
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/update/org.eclipse.update.core/META-INF/MANIFEST.MF b/update/org.eclipse.update.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..62ba95b
--- /dev/null
+++ b/update/org.eclipse.update.core/META-INF/MANIFEST.MF
@@ -0,0 +1,34 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.update.core; singleton:=true
+Bundle-Version: 3.2.600.qualifier
+Bundle-Activator: org.eclipse.update.internal.core.UpdateCore
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.update.configuration,
+ org.eclipse.update.core,
+ org.eclipse.update.core.model,
+ org.eclipse.update.internal.core;x-friends:="org.eclipse.update.ui,org.eclipse.update.scheduler,org.eclipse.pde.build",
+ org.eclipse.update.internal.core.connection;x-internal:=true,
+ org.eclipse.update.internal.jarprocessor;x-friends:="org.eclipse.pde.build",
+ org.eclipse.update.internal.mirror;x-internal:=true,
+ org.eclipse.update.internal.model;x-friends:="org.eclipse.update.ui",
+ org.eclipse.update.internal.operations;x-friends:="org.eclipse.update.ui,org.eclipse.update.scheduler",
+ org.eclipse.update.internal.provisional;x-internal:=true,
+ org.eclipse.update.internal.search;x-friends:="org.eclipse.update.ui",
+ org.eclipse.update.internal.security;x-internal:=true,
+ org.eclipse.update.internal.verifier;x-internal:=true,
+ org.eclipse.update.operations,
+ org.eclipse.update.search,
+ org.eclipse.update.standalone
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.update.configurator;bundle-version="[3.1.0,4.0.0)",
+ org.eclipse.core.net;bundle-version="[1.0.0,2.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.4,
+ CDC-1.1/Foundation-1.1
+Import-Package: javax.xml.parsers,
+ org.w3c.dom,
+ org.xml.sax,
+ org.xml.sax.helpers
diff --git a/update/org.eclipse.update.core/README.TXT b/update/org.eclipse.update.core/README.TXT
deleted file mode 100644
index c809c74..0000000
--- a/update/org.eclipse.update.core/README.TXT
+++ /dev/null
@@ -1,2 +0,0 @@
-This bundle is obsolete.
-Its functionality is provided by p2.
\ No newline at end of file
diff --git a/update/org.eclipse.update.core/about.html b/update/org.eclipse.update.core/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/update/org.eclipse.update.core/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/update/org.eclipse.update.core/build.properties b/update/org.eclipse.update.core/build.properties
new file mode 100644
index 0000000..cc35d07
--- /dev/null
+++ b/update/org.eclipse.update.core/build.properties
@@ -0,0 +1,22 @@
+###############################################################################

+# Copyright (c) 2000, 2008 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

+###############################################################################

+source.. = src/,\

+           jarprocessor/

+bin.includes = plugin.xml,\

+               plugin.properties,\

+               .,\

+               about.html,\

+               .options,\

+               META-INF/

+src.includes = about.html,\

+               schema/

+output.. = bin/

+customBuildCallbacks=customBuildCallbacks.xml

diff --git a/update/org.eclipse.update.core/customBuildCallbacks.xml b/update/org.eclipse.update.core/customBuildCallbacks.xml
new file mode 100644
index 0000000..a26456f
--- /dev/null
+++ b/update/org.eclipse.update.core/customBuildCallbacks.xml
Binary files differ
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/CommandStep.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/CommandStep.java
new file mode 100644
index 0000000..39c17d3
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/CommandStep.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * @author aniefer@ca.ibm.com
+ *
+ */
+public abstract class CommandStep implements IProcessStep {
+	protected String command = null;
+	protected String extension = null;
+	private  Properties options = null;
+	protected boolean verbose = false;
+	
+	public CommandStep(Properties options, String command, String extension, boolean verbose) {
+		this.command = command;
+		this.extension = extension;
+		this.options = options;
+		this.verbose = verbose;
+	}
+
+	protected static int execute(String[] cmd) {
+		return execute(cmd, false);
+	}
+	
+	protected static int execute(String[] cmd, boolean verbose) {
+		Runtime runtime = Runtime.getRuntime();
+		Process proc = null;
+		try {
+			proc = runtime.exec(cmd);
+			StreamProcessor errorStreamProcessor = new StreamProcessor(proc.getErrorStream(), StreamProcessor.STDERR, verbose); //$NON-NLS-1$
+			StreamProcessor outputStreamProcessor = new StreamProcessor(proc.getInputStream(), StreamProcessor.STDOUT, verbose); //$NON-NLS-1$
+			errorStreamProcessor.start();
+			outputStreamProcessor.start();
+		} catch (Exception e) {
+			if(verbose) {
+				System.out.println("Error executing command " + Utils.concat(cmd)); //$NON-NLS-1$
+				e.printStackTrace();
+			}
+			return -1;
+		}
+		try {
+			int result = proc.waitFor();
+			return result;
+		} catch (InterruptedException e) {
+			if(verbose)
+				e.printStackTrace();
+		}
+		return -1;
+	}
+	
+	public Properties getOptions() {
+		if(options == null)
+			options = new Properties();
+		return options;
+	}
+	
+	public void adjustInf(File input, Properties inf, List containers) {
+		//nothing
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/IProcessStep.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/IProcessStep.java
new file mode 100644
index 0000000..18c438e
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/IProcessStep.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * @author aniefer@ca.ibm.com
+ *
+ */
+public interface IProcessStep {
+	
+	/**
+	 * The effect of this processing step if the JarProcessor was to recurse on this entry.
+	 * Return null if this step will not do anything with this entry.
+	 * Return the new entryName if this step will modify this entry on recursion.
+	 * @param entryName
+	 * @return
+	 */
+	public String recursionEffect(String entryName);
+	
+	/**
+	 * Perform some processing on the input file before the JarProcessor considers the entries for recursion.
+	 *  return the file containing the result of the processing
+	 * @param input
+	 * @param workingDirectory
+	 * @param containers: inf properties for containing jars, innermost jar is first on the list
+	 * @return
+	 */
+	public File preProcess(File input, File workingDirectory, List containers);
+	
+	/**
+	 * Perform some processing on the input file after the JarProcessor returns from recursion
+	 * return the file containing the result of the processing
+	 * @param input
+	 * @param workingDirectory
+	 * @param containers: inf properties for containing jars, innermost jar is first on the list
+	 * @return
+	 */
+	public File postProcess(File input, File workingDirectory, List containers);
+	
+	/**
+	 * Return the name of this process step
+	 * @return
+	 */
+	public String getStepName();
+
+	/**
+	 * Adjust any properties in the eclipse.inf as appropriate for this step
+	 * @param input
+	 * @param inf
+	 * @param containers: inf properties for containing jars, innermost jar is first on the list
+	 */
+	public void adjustInf(File input, Properties inf, List containers);
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/JarProcessor.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/JarProcessor.java
new file mode 100644
index 0000000..aef3704
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/JarProcessor.java
@@ -0,0 +1,393 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+
+/**
+ * @author aniefer@ca.ibm.com
+ *
+ */
+public class JarProcessor {
+	private List steps = new ArrayList();
+	private String workingDirectory = ""; //$NON-NLS-1$
+	private int depth = -1;
+	private boolean verbose = false;
+	private boolean processAll = false;
+	private LinkedList containingInfs = new LinkedList();
+
+	static public JarProcessor getUnpackProcessor(Properties properties) {
+		if (!canPerformUnpack())
+			throw new UnsupportedOperationException();
+		JarProcessor processor = new JarProcessor();
+		processor.addProcessStep(new UnpackStep(properties));
+		return processor;
+	}
+
+	static public JarProcessor getPackProcessor(Properties properties) {
+		if (!canPerformPack())
+			throw new UnsupportedOperationException();
+		JarProcessor processor = new JarProcessor();
+		processor.addProcessStep(new PackStep(properties));
+		return processor;
+	}
+
+	static public boolean canPerformPack() {
+		return PackStep.canPack();
+	}
+
+	static public boolean canPerformUnpack() {
+		return UnpackStep.canUnpack();
+	}
+
+	public String getWorkingDirectory() {
+		return workingDirectory;
+	}
+
+	public void setWorkingDirectory(String dir) {
+		if(dir != null)
+			workingDirectory = dir;
+	}
+	
+	public void setVerbose(boolean verbose) {
+		this.verbose = verbose;
+	}
+	
+	public void setProcessAll(boolean all){
+		this.processAll = all;
+	}
+
+	public void addProcessStep(IProcessStep step) {
+		steps.add(step);
+	}
+
+	public void clearProcessSteps() {
+		steps.clear();
+	}
+
+	/**
+	 * Recreate a jar file.  The replacements map specifies entry names to be replaced, the replacements are
+	 * expected to be found in directory.
+	 * 
+	 * @param jar - The input jar
+	 * @param outputJar - the output
+	 * @param replacements - map of entryName -> new entryName
+	 * @param directory - location to find file for new entryName
+	 * @throws IOException
+	 */
+	private void recreateJar(JarFile jar, JarOutputStream outputJar, Map replacements, File directory, Properties inf) throws IOException {
+		InputStream in = null;
+		boolean marked = false;
+		try {
+			Enumeration entries = jar.entries();
+			for (JarEntry entry = (JarEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (JarEntry) entries.nextElement() : null) {
+				File replacement = null;
+				JarEntry newEntry = null;
+				if (replacements.containsKey(entry.getName())) {
+					String name = (String) replacements.get(entry.getName());
+					replacement = new File(directory, name);
+					if (name != null) {
+						if (replacement.exists()) {
+							try {
+								in = new BufferedInputStream(new FileInputStream(replacement));
+								newEntry = new JarEntry(name);
+							} catch (Exception e) {
+								if (verbose) {
+									e.printStackTrace();
+									System.out.println("Warning: Problem reading " +replacement.getPath() + ", using " + jar.getName() + File.separator + entry.getName()  + " instead.");
+								}
+							}
+						} else if (verbose) {
+							System.out.println("Warning: " + replacement.getPath() + " not found, using " + jar.getName() + File.separator + entry.getName() + " instead.");	
+						}
+					}
+				}
+				if (newEntry == null) {
+					try {
+						in = new BufferedInputStream(jar.getInputStream(entry));
+						newEntry = new JarEntry(entry.getName());
+					} catch( Exception e ) {
+						if(verbose) {
+							e.printStackTrace();
+							System.out.println("ERROR: problem reading " + entry.getName() + " from " + jar.getName());
+						}
+						continue;
+					}
+				}
+				newEntry.setTime(entry.getTime());
+				outputJar.putNextEntry(newEntry);
+				if (entry.getName().equals(Utils.MARK_FILE_NAME)) {
+					//The eclipse.inf file was read in earlier, don't need to reread it, just write it out now
+					Utils.storeProperties(inf, outputJar);
+					marked = true;
+				} else {
+					Utils.transferStreams(in, outputJar, false);
+				}
+				outputJar.closeEntry();
+				in.close();
+
+				//delete the nested jar file
+				if (replacement != null) {
+					replacement.delete();
+				}
+			}
+			if (!marked) {
+				JarEntry entry = new JarEntry(Utils.MARK_FILE_NAME);
+				outputJar.putNextEntry(entry);
+				Utils.storeProperties(inf, outputJar);
+				outputJar.closeEntry();
+			}
+		} finally {
+			Utils.close(outputJar);
+			Utils.close(jar);
+			Utils.close(in);
+		}
+	}
+
+	private String recursionEffect(String entryName) {
+		String result = null;
+		for (Iterator iter = steps.iterator(); iter.hasNext();) {
+			IProcessStep step = (IProcessStep) iter.next();
+
+			result = step.recursionEffect(entryName);
+			if (result != null)
+				entryName = result;
+		}
+		return result;
+	}
+
+	private void extractEntries(JarFile jar, File tempDir, Map data, Properties inf) throws IOException {
+		if(inf != null ) {
+			//skip if excluding children
+			if(inf.containsKey(Utils.MARK_EXCLUDE_CHILDREN)){
+				String excludeChildren = inf.getProperty(Utils.MARK_EXCLUDE_CHILDREN);
+				if( Boolean.valueOf(excludeChildren).booleanValue() )
+					if(verbose){
+						for(int i = 0; i <= depth; i++)
+							System.out.print("  "); //$NON-NLS-1$
+						System.out.println("Children of " + jar.getName() + "are excluded from processing.");
+					}
+					return;
+			}
+		}
+		
+		Enumeration entries = jar.entries();
+		if (entries.hasMoreElements()) {
+			for (JarEntry entry = (JarEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (JarEntry) entries.nextElement() : null) {
+				String name = entry.getName();
+				String newName = recursionEffect(name);
+				if (newName != null) {
+					if(verbose){
+						for(int i = 0; i <= depth; i++)
+							System.out.print("  "); //$NON-NLS-1$
+						System.out.println("Processing nested file: " + name); //$NON-NLS-1$
+					}
+					//extract entry to temp directory
+					File extracted = new File(tempDir, name);
+					File parentDir = extracted.getParentFile();
+					if (!parentDir.exists())
+						parentDir.mkdirs();
+
+					InputStream in = null;
+					OutputStream out = null;
+					try {
+						in = jar.getInputStream(entry);
+						out = new BufferedOutputStream(new FileOutputStream(extracted));
+						Utils.transferStreams(in, out, true); //this will close both streams
+					} finally {
+						Utils.close(in);
+						Utils.close(out);
+					}
+					extracted.setLastModified(entry.getTime());
+
+					//recurse
+					containingInfs.addFirst(inf);
+					String dir = getWorkingDirectory();
+					setWorkingDirectory(parentDir.getCanonicalPath());
+					File result = processJar(extracted);
+
+					newName = name.substring(0, name.length() - extracted.getName().length()) + result.getName();
+					data.put(name, newName);
+
+					setWorkingDirectory(dir);
+					containingInfs.removeFirst();
+
+					//delete the extracted item leaving the recursion result
+					if (!name.equals(newName))
+						extracted.delete();
+				}
+			}
+		}
+	}
+
+	private File preProcess(File input, File tempDir) {
+		File result = null;
+		for (Iterator iter = steps.iterator(); iter.hasNext();) {
+			IProcessStep step = (IProcessStep) iter.next();
+			result = step.preProcess(input, tempDir, containingInfs);
+			if (result != null)
+				input = result;
+		}
+		return input;
+	}
+
+	private File postProcess(File input, File tempDir) {
+		File result = null;
+		for (Iterator iter = steps.iterator(); iter.hasNext();) {
+			IProcessStep step = (IProcessStep) iter.next();
+			result = step.postProcess(input, tempDir, containingInfs);
+			if (result != null)
+				input = result;
+		}
+		return input;
+	}
+
+	private void adjustInf(File input, Properties inf) {
+		for (Iterator iter = steps.iterator(); iter.hasNext();) {
+			IProcessStep step = (IProcessStep) iter.next();
+			step.adjustInf(input, inf, containingInfs);
+		}
+	}
+
+	public File processJar(File input) throws IOException {
+		++depth;
+		long lastModified = input.lastModified();
+		File workingDir = new File(getWorkingDirectory());
+		if (!workingDir.exists())
+			workingDir.mkdirs();
+
+		boolean skip = Utils.shouldSkipJar(input, processAll, verbose);
+		if (depth == 0 && verbose) {
+			if (skip)
+				System.out.println("Skipping " + input.getPath()); //$NON-NLS-1$
+			else {
+				System.out.print("Running "); //$NON-NLS-1$ 
+				for (Iterator iter = steps.iterator(); iter.hasNext();) {
+					IProcessStep step = (IProcessStep) iter.next();
+					System.out.print(step.getStepName() + " "); //$NON-NLS-1$
+				}
+				System.out.println("on " + input.getPath()); //$NON-NLS-1$
+			}
+		}
+
+		if (skip) {
+			//This jar was not marked as conditioned, and we are only processing conditioned jars, so do nothing
+			--depth;
+			return input;
+		}
+
+		//pre
+		File workingFile = preProcess(input, workingDir);
+
+		//Extract entries from jar and recurse on them
+		File tempDir = null;
+		if (depth == 0) {
+			tempDir = new File(workingDir, "temp." + workingFile.getName()); //$NON-NLS-1$
+		} else {
+			File parent = workingDir.getParentFile();
+			tempDir = new File(parent, "temp_" + depth + '_' + workingFile.getName()); //$NON-NLS-1$
+		}
+
+		JarFile jar = new JarFile(workingFile, false);
+		Map replacements = new HashMap();
+		Properties inf = Utils.getEclipseInf(workingFile, verbose);
+		extractEntries(jar, tempDir, replacements, inf);
+
+		if (inf != null)
+			adjustInf(workingFile, inf);
+
+		//Recreate the jar with replacements. 
+		//TODO: This is not strictly necessary if we didn't change the inf file and didn't change any content
+		File tempJar = null;
+		tempJar = new File(tempDir, workingFile.getName());
+		File parent = tempJar.getParentFile();
+		if (!parent.exists())
+			parent.mkdirs();
+		JarOutputStream jarOut = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(tempJar)));
+		recreateJar(jar, jarOut, replacements, tempDir, inf);
+
+		jar.close();
+		if (tempJar != null) {
+			if (!workingFile.equals(input)) {
+				workingFile.delete();
+			}
+			workingFile = tempJar;
+		}
+
+		//post
+		File result = postProcess(workingFile, workingDir);
+		
+		//have to normalize after the post steps
+		normalize(result, workingDir);
+		
+		if (!result.equals(workingFile) && !workingFile.equals(input))
+			workingFile.delete();
+		if (!result.getParentFile().equals(workingDir)) {
+			File finalFile = new File(workingDir, result.getName());
+			if (finalFile.exists())
+				finalFile.delete();
+			result.renameTo(finalFile);
+			result = finalFile;
+		}
+
+		if (tempDir.exists())
+			Utils.clear(tempDir);
+
+		result.setLastModified(lastModified);
+		--depth;
+		return result;
+	}
+	
+	private void normalize(File input, File workingDirectory) {
+		if(input.getName().endsWith(Utils.PACKED_SUFFIX)) {
+			//not a jar
+			return;
+		}
+		try {
+			File tempJar = new File(workingDirectory, "temp_" + input.getName()); //$NON-NLS-1$
+			JarFile jar = null;
+			try {
+				jar = new JarFile(input, false);
+			} catch (JarException e) {
+				//not a jar
+				return ;
+			}
+			JarOutputStream jarOut = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(tempJar)));
+			InputStream in = null;
+			try {
+				Enumeration entries = jar.entries();
+				for (JarEntry entry = (JarEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (JarEntry) entries.nextElement() : null) {
+					JarEntry newEntry = new JarEntry(entry.getName());
+					newEntry.setTime(entry.getTime());
+					in = new BufferedInputStream(jar.getInputStream(entry));
+					jarOut.putNextEntry(newEntry);
+					Utils.transferStreams(in, jarOut, false);
+					jarOut.closeEntry();
+					in.close();
+				}
+			} finally {
+				Utils.close(jarOut);
+				Utils.close(jar);
+				Utils.close(in);
+			}
+			tempJar.setLastModified(input.lastModified());
+			input.delete();
+			tempJar.renameTo(input);
+		} catch (IOException e) {
+			if (verbose) {
+				System.out.println("Error normalizing jar " + input.getName()); //$NON-NLS-1$
+				e.printStackTrace();
+			}
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/JarProcessorExecutor.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/JarProcessorExecutor.java
new file mode 100644
index 0000000..160785d
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/JarProcessorExecutor.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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 - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.*;
+import java.util.Properties;
+import java.util.zip.ZipException;
+import org.eclipse.update.internal.jarprocessor.Main.Options;
+
+public class JarProcessorExecutor {
+	public void runJarProcessor(Options options) {
+		if (options.input.isFile() && options.input.getName().endsWith(".zip")) { //$NON-NLS-1$
+			ZipProcessor processor = new ZipProcessor();
+			processor.setWorkingDirectory(options.outputDir);
+			processor.setSignCommand(options.signCommand);
+			processor.setPack(options.pack);
+			processor.setRepack(options.repack || (options.pack && options.signCommand != null));
+			processor.setUnpack(options.unpack);
+			processor.setVerbose(options.verbose);
+			processor.setProcessAll(options.processAll);
+			try {
+				processor.processZip(options.input);
+			} catch (ZipException e) {
+				if (options.verbose)
+					e.printStackTrace();
+			} catch (IOException e) {
+				if (options.verbose)
+					e.printStackTrace();
+			}
+		} else {
+			JarProcessor processor = new JarProcessor();
+			JarProcessor packProcessor = null;
+
+			processor.setWorkingDirectory(options.outputDir);
+			processor.setProcessAll(options.processAll);
+			processor.setVerbose(options.verbose);
+
+			//load options file
+			Properties properties = new Properties();
+			if (options.input.isDirectory()) {
+				File packProperties = new File(options.input, "pack.properties");
+				if (packProperties.exists() && packProperties.isFile()) {
+					InputStream in = null;
+					try {
+						in = new BufferedInputStream(new FileInputStream(packProperties));
+						properties.load(in);
+					} catch (IOException e) {
+						if (options.verbose)
+							e.printStackTrace();
+					} finally {
+						Utils.close(in);
+					}
+				}
+			}
+
+			if (options.unpack)
+				addUnpackStep(processor, properties, options);
+
+			if (options.repack || (options.pack && options.signCommand != null))
+				addPackUnpackStep(processor, properties, options);
+
+			if (options.signCommand != null)
+				addSignStep(processor, properties, options);
+
+			if (options.pack) {
+				packProcessor = new JarProcessor();
+				packProcessor.setWorkingDirectory(options.outputDir);
+				packProcessor.setProcessAll(options.processAll);
+				packProcessor.setVerbose(options.verbose);
+				addPackStep(packProcessor, properties, options);
+			}
+
+			try {
+				process(options.input, options.unpack ? Utils.PACK_GZ_FILTER : Utils.JAR_FILTER, options.verbose, processor, packProcessor);
+			} catch (FileNotFoundException e) {
+				if (options.verbose)
+					e.printStackTrace();
+			}
+		}
+	}
+
+	protected void process(File input, FileFilter filter, boolean verbose, JarProcessor processor, JarProcessor packProcessor) throws FileNotFoundException {
+		if (!input.exists())
+			throw new FileNotFoundException();
+
+		File[] files = null;
+		if (input.isDirectory()) {
+			files = input.listFiles();
+		} else if (filter.accept(input)) {
+			files = new File[] {input};
+		}
+		for (int i = 0; i < files.length; i++) {
+			if (files[i].isDirectory()) {
+				String dir = processor.getWorkingDirectory();
+				processor.setWorkingDirectory(dir + "/" + files[i].getName()); //$NON-NLS-1$
+				if (packProcessor != null)
+					packProcessor.setWorkingDirectory(dir + "/" + files[i].getName());
+				process(files[i], filter, verbose, processor, packProcessor);
+				processor.setWorkingDirectory(dir);
+				if (packProcessor != null)
+					packProcessor.setWorkingDirectory(dir);
+			} else if (filter.accept(files[i])) {
+				try {
+					File result = processor.processJar(files[i]);
+					if (packProcessor != null && result != null && result.exists()) {
+						packProcessor.processJar(result);
+					}
+				} catch (IOException e) {
+					if (verbose)
+						e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	public void addPackUnpackStep(JarProcessor processor, Properties properties, Options options) {
+		processor.addProcessStep(new PackUnpackStep(properties, options.verbose));
+	}
+
+	public void addSignStep(JarProcessor processor, Properties properties, Options options) {
+		processor.addProcessStep(new SignCommandStep(properties, options.signCommand, options.verbose));
+	}
+
+	public void addPackStep(JarProcessor processor, Properties properties, Options options) {
+		processor.addProcessStep(new PackStep(properties, options.verbose));
+	}
+
+	public void addUnpackStep(JarProcessor processor, Properties properties, Options options) {
+		processor.addProcessStep(new UnpackStep(properties, options.verbose));
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/Main.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/Main.java
new file mode 100644
index 0000000..43d8e0a
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/Main.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2006-2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.File;
+
+public class Main {
+
+	public static class Options {
+		public String outputDir = "."; //$NON-NLS-1$
+		public String signCommand = null;
+		public boolean pack = false;
+		public boolean repack = false;
+		public boolean unpack = false;
+		public boolean verbose = false;
+		public boolean processAll = false;
+		public File input = null;
+	}
+
+	private static void printUsage() {
+		System.out.println("[-option ...]... input"); //$NON-NLS-1$
+		System.out.println("The following options are supported:"); //$NON-NLS-1$
+		System.out.println("-processAll     process all jars, regardless of whether they were previously normalized"); //$NON-NLS-1$
+		System.out.println("                By default only normalized jars will be processed."); //$NON-NLS-1$
+		System.out.println("-repack         normalize jars "); //$NON-NLS-1$
+		System.out.println("-sign <command> sign jars using <command>"); //$NON-NLS-1$
+		System.out.println("-pack           pack the jars.  pack and repack are redundant unless"); //$NON-NLS-1$
+		System.out.println("                sign is also specified."); //$NON-NLS-1$
+		System.out.println("-unpack         unpack pack.gz files. Unpack is mutually exclusive"); //$NON-NLS-1$
+		System.out.println("                with repack, sign and pack."); //$NON-NLS-1$
+		System.out.println();
+		System.out.println("-outputDir <dir>  the output directory"); //$NON-NLS-1$
+		System.out.println("-verbose        verbose mode "); //$NON-NLS-1$
+	}
+
+	public static Options processArguments(String[] args) {
+		if (args.length == 0) {
+			printUsage();
+			return null;
+		}
+
+		Options options = new Options();
+		int i = 0;
+		for (; i < args.length - 1; i++) {
+			if (args[i].equals("-pack")) {//$NON-NLS-1$
+				options.pack = true;
+			} else if (args[i].equals("-unpack")) { //$NON-NLS-1$
+				options.unpack = true;
+			} else if (args[i].equals("-sign") && i < args.length - 2) { //$NON-NLS-1$
+				if (args[i + 1].startsWith("-")) { //$NON-NLS-1$
+					printUsage();
+					return null;
+				}
+				options.signCommand = args[++i];
+			} else if (args[i].equals("-repack")) { //$NON-NLS-1$
+				options.repack = true;
+			} else if (args[i].equals("-outputDir") && i < args.length - 2) { //$NON-NLS-1$
+				if (args[i + 1].startsWith("-")) { //$NON-NLS-1$
+					printUsage();
+					return null;
+				}
+				options.outputDir = args[++i];
+			} else if (args[i].equals("-verbose")) { //$NON-NLS-1$
+				options.verbose = true;
+			}  else if (args[i].equals("-processAll")) { //$NON-NLS-1$
+				options.processAll = true;
+			} 
+		}
+
+		options.input = new File(args[i]);
+
+		String problemMessage = null;
+		String inputName = options.input.getName();
+		if (options.unpack) {
+			if (!JarProcessor.canPerformUnpack()) {
+				problemMessage = "The unpack200 command cannot be found."; //$NON-NLS-1$
+			} else 	if (options.input.isFile() && !inputName.endsWith(".zip") && !inputName.endsWith(".pack.gz")) { //$NON-NLS-1$ //$NON-NLS-2$
+				problemMessage = "Input file is not a pack.gz file."; //$NON-NLS-1$
+			} else 	if (options.pack || options.repack || options.signCommand != null) {
+				problemMessage = "Pack, repack or sign cannot be specified with unpack."; //$NON-NLS-1$
+			}
+		} else {
+			if (options.input.isFile() && !inputName.endsWith(".zip") && !inputName.endsWith(".jar")) { //$NON-NLS-1$ //$NON-NLS-2$
+				problemMessage = "Input file is not a jar file."; //$NON-NLS-1$
+			} else	if ((options.pack || options.repack) && !JarProcessor.canPerformPack()) {
+				problemMessage = "The pack200 command can not be found."; //$NON-NLS-1$
+			}
+		}
+		if(problemMessage != null){
+			System.out.println(problemMessage);
+			System.out.println();
+			printUsage();
+			return null;
+		}
+
+		return options;
+	}
+	
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		Options options = processArguments(args);
+		if (options == null)
+			return;
+		new JarProcessorExecutor().runJarProcessor(options);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/PackStep.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/PackStep.java
new file mode 100644
index 0000000..4afd638
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/PackStep.java
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+public class PackStep extends CommandStep {
+
+	protected static String packCommand = null;
+	private static Boolean canPack = null;
+
+	private Set exclusions = Collections.EMPTY_SET;
+
+	public static boolean canPack() {
+		if (canPack != null)
+			return canPack.booleanValue();
+
+		String[] locations = Utils.getPack200Commands("pack200"); //$NON-NLS-1$
+		if (locations == null) {
+			canPack = Boolean.FALSE;
+			packCommand = null;
+			return false;
+		}
+
+		int result;
+		for (int i = 0; i < locations.length; i++) {
+			if (locations[i] == null)
+				continue;
+			result = execute(new String[] {locations[i], "-V"}); //$NON-NLS-1$
+			if (result == 0) {
+				packCommand = locations[i];
+				canPack = Boolean.TRUE;
+				return true;
+			}
+		}
+
+		canPack = Boolean.FALSE;
+		return false;
+	}
+
+	public PackStep(Properties options) {
+		super(options, null, null, false);
+		exclusions = Utils.getPackExclusions(options);
+	}
+
+	public PackStep(Properties options, boolean verbose) {
+		super(options, null, null, verbose);
+		exclusions = Utils.getPackExclusions(options);
+	}
+
+	public String recursionEffect(String entryName) {
+		if (canPack() && entryName.endsWith(".jar") && !exclusions.contains(entryName)) { //$NON-NLS-1$
+			return entryName + Utils.PACKED_SUFFIX;
+		}
+		return null;
+	}
+
+	public File preProcess(File input, File workingDirectory, List containers) {
+		return null;
+	}
+
+	public File postProcess(File input, File workingDirectory, List containers) {
+		if (canPack() && packCommand != null) {
+			Properties inf = Utils.getEclipseInf(input, verbose);
+			if (!shouldPack(input, containers, inf))
+				return null;
+			File outputFile = new File(workingDirectory, input.getName() + Utils.PACKED_SUFFIX);
+			try {
+				String[] cmd = getCommand(input, outputFile, inf, containers);
+				int result = execute(cmd, verbose);
+				if (result != 0 && verbose)
+					System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$
+			} catch (IOException e) {
+				if (verbose)
+					e.printStackTrace();
+				return null;
+			}
+			return outputFile;
+		}
+		return null;
+	}
+
+	protected boolean shouldPack(File input, List containers, Properties inf) {
+		//1: exclude by containers
+		// innermost jar is first on the list, it can override outer jars
+		for (Iterator iterator = containers.iterator(); iterator.hasNext();) {
+			Properties container = (Properties) iterator.next();
+			if (container.containsKey(Utils.MARK_EXCLUDE_CHILDREN_PACK)) {
+				if (Boolean.valueOf(container.getProperty(Utils.MARK_EXCLUDE_CHILDREN_PACK)).booleanValue()) {
+					if (verbose)
+						System.out.println(input.getName() + " is excluded from pack200 by its containers.");
+					return false;
+				}
+				break;
+			}
+		}
+
+		//2: excluded by self
+		if (inf != null && inf.containsKey(Utils.MARK_EXCLUDE_PACK) && Boolean.valueOf(inf.getProperty(Utils.MARK_EXCLUDE_PACK)).booleanValue()) {
+			if (verbose)
+				System.out.println("Excluding " + input.getName() + " from " + getStepName()); //$NON-NLS-1$ //$NON-NLS-2$
+			return false;
+		}
+
+		return true;
+	}
+
+	protected String[] getCommand(File input, File outputFile, Properties inf, List containers) throws IOException {
+		String[] cmd = null;
+		String arguments = getArguments(input, inf, containers);
+		if (arguments != null && arguments.length() > 0) {
+			String[] args = Utils.toStringArray(arguments, ","); //$NON-NLS-1$
+			cmd = new String[3 + args.length];
+			cmd[0] = packCommand;
+			System.arraycopy(args, 0, cmd, 1, args.length);
+			cmd[cmd.length - 2] = outputFile.getCanonicalPath();
+			cmd[cmd.length - 1] = input.getCanonicalPath();
+		} else {
+			cmd = new String[] {packCommand, outputFile.getCanonicalPath(), input.getCanonicalPath()};
+		}
+		return cmd;
+	}
+
+	protected String getArguments(File input, Properties inf, List containers) {	
+		//1: Explicitly marked in our .inf file
+		if (inf != null && inf.containsKey(Utils.PACK_ARGS)) {
+			return  inf.getProperty(Utils.PACK_ARGS);
+		}
+
+		//2: Defaults set in one of our containing jars
+		for (Iterator iterator = containers.iterator(); iterator.hasNext();) {
+			Properties container = (Properties) iterator.next();
+			if (container.containsKey(Utils.DEFAULT_PACK_ARGS)) {
+				return container.getProperty(Utils.DEFAULT_PACK_ARGS);
+			}
+		}
+
+		//3: Set by name in outside pack.properties file
+		Properties options = getOptions();
+		String argsKey = input.getName() + Utils.PACK_ARGS_SUFFIX;
+		if (options.containsKey(argsKey)) {
+			return options.getProperty(argsKey);
+		}
+
+		//4: Set by default in outside pack.properties file
+		if (options.containsKey(Utils.DEFAULT_PACK_ARGS)) {
+			return options.getProperty(Utils.DEFAULT_PACK_ARGS);
+		}
+
+		return ""; //$NON-NLS-1$
+	}
+
+	public String getStepName() {
+		return "Pack"; //$NON-NLS-1$
+	}
+
+	public void adjustInf(File input, Properties inf, List containers) {
+		if (input == null || inf == null)
+			return;
+
+		//don't be verbose to check if we should mark the inf
+		boolean v = verbose;
+		verbose = false;
+		if (!shouldPack(input, containers, inf)) {
+			verbose = v;
+			return;
+		}
+		verbose = v;
+
+		//mark as conditioned if not previously marked.  A signed jar is assumed to be previously conditioned.
+		if (inf.getProperty(Utils.MARK_PROPERTY) == null) {
+			inf.put(Utils.MARK_PROPERTY, "true"); //$NON-NLS-1$
+	
+			//record arguments used
+			String arguments = inf.getProperty(Utils.PACK_ARGS);
+			if (arguments == null) {
+				arguments = getArguments(input, inf, containers);
+				if (arguments != null && arguments.length() > 0)
+					inf.put(Utils.PACK_ARGS, arguments);
+			}
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/PackUnpackStep.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/PackUnpackStep.java
new file mode 100644
index 0000000..019812b
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/PackUnpackStep.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * @author aniefer@ca.ibm.com
+ *
+ */
+public class PackUnpackStep extends PackStep {
+	private Set exclusions = null;
+
+	public PackUnpackStep(Properties options) {
+		super(options);
+		exclusions = Utils.getPackExclusions(options);
+	}
+
+	public PackUnpackStep(Properties options, boolean verbose) {
+		super(options, verbose);
+		exclusions = Utils.getPackExclusions(options);
+	}
+
+	public String recursionEffect(String entryName) {
+		if (canPack() && entryName.endsWith(".jar") && !exclusions.contains(entryName)) { //$NON-NLS-1$
+			return entryName;
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.internal.jarprocessor.PackStep#postProcess(java.io.File, java.io.File, java.util.LinkedList)
+	 */
+	public File postProcess(File input, File workingDirectory, List containers) {
+		if (canPack() && packCommand != null && input != null) {
+			Properties inf = Utils.getEclipseInf(input, verbose);
+			if (!shouldPack(input, containers, inf))
+				return null;
+			File tempFile = new File(workingDirectory, "temp_" + input.getName()); //$NON-NLS-1$
+			try {
+				String[] tmp = getCommand(input, tempFile, inf, containers);
+				String[] cmd = new String[tmp.length + 1];
+				cmd[0] = tmp[0];
+				cmd[1] = "-r"; //$NON-NLS-1$
+				System.arraycopy(tmp, 1, cmd, 2, tmp.length - 1);
+
+				int result = execute(cmd, verbose);
+				if (result == 0 && tempFile.exists()) {
+					File finalFile = new File(workingDirectory, input.getName());
+					if (finalFile.exists())
+						finalFile.delete();
+					tempFile.renameTo(finalFile);
+					return finalFile;
+				} else if (verbose) {
+					System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+			} catch (IOException e) {
+				if (verbose)
+					e.printStackTrace();
+				return null;
+			}
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.internal.jarprocessor.PackStep#preProcess(java.io.File, java.io.File, java.util.LinkedList)
+	 */
+	public File preProcess(File input, File workingDirectory, List containers) {
+		return null;
+	}
+
+	public String getStepName() {
+		return "Repack"; //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/SignCommandStep.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/SignCommandStep.java
new file mode 100644
index 0000000..282ed75
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/SignCommandStep.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2006-2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+public class SignCommandStep extends CommandStep {
+	private Set exclusions = null;
+
+	public SignCommandStep(Properties options, String command) {
+		super(options, command, ".jar", false); //$NON-NLS-1$
+		exclusions = Utils.getSignExclusions(options);
+	}
+
+	public SignCommandStep(Properties options, String command, boolean verbose) {
+		super(options, command, ".jar", verbose); //$NON-NLS-1$
+		exclusions = Utils.getSignExclusions(options);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.jarprocessor.IProcessStep#recursionEffect(java.lang.String)
+	 */
+	public String recursionEffect(String entryName) {
+		if (entryName.endsWith(extension) && !exclusions.contains(entryName))
+			return entryName;
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.jarprocessor.IProcessStep#preProcess(java.io.File, java.io.File)
+	 */
+	public File preProcess(File input, File workingDirectory, List containers) {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.jarprocessor.IProcessStep#postProcess(java.io.File, java.io.File)
+	 */
+	public File postProcess(File input, File workingDirectory, List containers) {
+		if (command != null && input != null && shouldSign(input, containers)) {
+			try {
+				String[] cmd = new String[] {command, input.getCanonicalPath()};
+				int result = execute(cmd, verbose);
+				if (result == 0) {
+					return input;
+				} else if (verbose) {
+					System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+			} catch (IOException e) {
+				if (verbose) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return null;
+	}
+
+	public boolean shouldSign(File input, List containers) {
+		Properties inf = null;
+
+		//1: Are we excluded from signing by our parents?
+		//innermost jar is first on the list, it overrides outer jars
+		for (Iterator iterator = containers.iterator(); iterator.hasNext();) {
+			inf = (Properties) iterator.next();
+			if (inf.containsKey(Utils.MARK_EXCLUDE_CHILDREN_SIGN)){
+				if(Boolean.valueOf(inf.getProperty(Utils.MARK_EXCLUDE_CHILDREN_SIGN)).booleanValue()) {
+					if (verbose)
+						System.out.println(input.getName() + "is excluded from signing by its containers."); //$NON-NLS-1$ //$NON-NLS-2$
+					return false;
+				}
+				break;
+			}
+		}
+
+		//2: Is this jar itself marked as exclude?
+		inf = Utils.getEclipseInf(input, verbose);
+		if (inf != null && inf.containsKey(Utils.MARK_EXCLUDE_SIGN) && Boolean.valueOf(inf.getProperty(Utils.MARK_EXCLUDE_SIGN)).booleanValue()) {
+			if (verbose)
+				System.out.println("Excluding " + input.getName() + " from signing."); //$NON-NLS-1$ //$NON-NLS-2$
+			return false;
+		}
+		return true;
+	}
+
+	public String getStepName() {
+		return "Sign"; //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/StreamProcessor.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/StreamProcessor.java
new file mode 100644
index 0000000..e0b5c3a
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/StreamProcessor.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class StreamProcessor extends Thread {
+	public static final String STDERR = "STDERR"; //$NON-NLS-1$
+	public static final String STDOUT = "STDOUT"; //$NON-NLS-1$
+
+	private InputStream inputStream;
+	private String name;
+	private boolean verbose;
+
+	public StreamProcessor(InputStream is, String name, boolean verbose) {
+		this.inputStream = is;
+		this.name = name;
+		this.verbose = verbose;
+	}
+
+	public void run() {
+		try {
+			InputStreamReader isr = new InputStreamReader(inputStream);
+			BufferedReader br = new BufferedReader(isr);
+			while (true) {
+				String s = br.readLine();
+				if (s == null) {
+					break;
+				}
+				if (verbose) {
+					if (STDERR.equals(name))
+						System.err.println(name + ": " + s); //$NON-NLS-1$
+					else
+						System.out.println(name + ": " + s); //$NON-NLS-1$
+				}
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/UnpackStep.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/UnpackStep.java
new file mode 100644
index 0000000..1d30dd8
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/UnpackStep.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * @author aniefer
+ *
+ */
+public class UnpackStep extends CommandStep {
+	public static final String UNPACKER_PROPERTY = "org.eclipse.update.jarprocessor.Unpacker"; //$NON-NLS-1$
+	private static Boolean canUnpack = null;
+	private static String unpackCommand = null;
+
+	public static boolean canUnpack() {
+		if (canUnpack != null)
+			return canUnpack.booleanValue();
+
+		String[] locations = Utils.getPack200Commands("unpack200"); //$NON-NLS-1$
+		if (locations == null) {
+			canUnpack = Boolean.FALSE;
+			unpackCommand = null;
+			return false;
+		}
+
+		int result;
+		for (int i = 0; i < locations.length; i++) {
+			if (locations[i] == null)
+				continue;
+			result = execute(new String[] {locations[i], "-V"}); //$NON-NLS-1$
+			if (result == 0) {
+				unpackCommand = locations[i];
+				canUnpack = Boolean.TRUE;
+				return true;
+			}
+		}
+
+		canUnpack = Boolean.FALSE;
+		return false;
+	}
+
+	public UnpackStep(Properties options) {
+		super(options, null, null, false);
+	}
+
+	public UnpackStep(Properties options, boolean verbose) {
+		super(options, null, null, verbose);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.jarprocessor.IProcessStep#recursionEffect(java.lang.String)
+	 */
+	public String recursionEffect(String entryName) {
+		if (canUnpack() && entryName.endsWith(Utils.PACKED_SUFFIX)) {
+			return entryName.substring(0, entryName.length() - Utils.PACKED_SUFFIX.length());
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.jarprocessor.IProcessStep#preProcess(java.io.File, java.io.File)
+	 */
+	public File preProcess(File input, File workingDirectory, List containers) {
+		if (canUnpack() && unpackCommand != null) {
+			String name = input.getName();
+			if (name.endsWith(Utils.PACKED_SUFFIX)) {
+				name = name.substring(0, name.length() - Utils.PACKED_SUFFIX.length());
+
+				File unpacked = new File(workingDirectory, name);
+				File parent = unpacked.getParentFile();
+				if (!parent.exists())
+					parent.mkdirs();
+				try {
+					String options = getOptions().getProperty(input.getName() + ".unpack.args"); //$NON-NLS-1$
+					String[] cmd = null;
+					if (options != null) {
+						cmd = new String[] {unpackCommand, options, input.getCanonicalPath(), unpacked.getCanonicalPath()};
+					} else {
+						cmd = new String[] {unpackCommand, input.getCanonicalPath(), unpacked.getCanonicalPath()};
+					}
+					int result = execute(cmd, verbose);
+					if (result != 0 && verbose)
+						System.out.println("Error: " + result + " was returned from command: " + Utils.concat(cmd)); //$NON-NLS-1$ //$NON-NLS-2$
+				} catch (IOException e) {
+					if (verbose)
+						e.printStackTrace();
+					return null;
+				}
+				return unpacked;
+			}
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.jarprocessor.IProcessStep#postProcess(java.io.File, java.io.File)
+	 */
+	public File postProcess(File input, File workingDirectory, List containers) {
+		return null;
+	}
+
+	public String getStepName() {
+		return "Unpack"; //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/Utils.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/Utils.java
new file mode 100644
index 0000000..0f4ebbf
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/Utils.java
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.zip.ZipException;
+
+/**
+ * @author aniefer@ca.ibm.com
+ *
+ */
+public class Utils {
+	public static final String MARK_FILE_NAME = "META-INF/eclipse.inf"; //$NON-NLS-1$
+	
+	/*
+	 * Properties found in outer pack.properties file
+	 */
+	//comma separated list of jars to exclude from sigining
+	public static final String SIGN_EXCLUDES = "sign.excludes"; //$NON-NLS-1$
+	//comma separated list of jars to exlclude from packing
+	public static final String PACK_EXCLUDES = "pack.excludes"; //$NON-NLS-1$
+	//Suffix used when specifying arguments to use when running pack200 on a jar
+	public static final String PACK_ARGS_SUFFIX = ".pack.args"; //$NON-NLS-1$
+	
+	/*
+	 * Properties found in both pack.properties and eclipse.inf
+	 */
+	//	Default arguments to use when running pack200.
+	// Affects all jars when specified in pack.properties, affects children when specified in eclipse.inf
+	public static final String DEFAULT_PACK_ARGS = "pack200.default.args"; //$NON-NLS-1$
+	
+	/*
+	 * Properties found in eclipse.inf file
+	 */
+	//This jar has been conditioned with pack200
+	public static final String MARK_PROPERTY = "pack200.conditioned"; //$NON-NLS-1$
+	//Exclude this jar from processing
+	public static final String MARK_EXCLUDE = "jarprocessor.exclude"; //$NON-NLS-1$
+	//Exclude this jar from pack200
+	public static final String MARK_EXCLUDE_PACK = "jarprocessor.exclude.pack"; //$NON-NLS-1$
+	//Exclude this jar from signing
+	public static final String MARK_EXCLUDE_SIGN = "jarprocessor.exclude.sign"; //$NON-NLS-1$
+	//Exclude this jar's children from processing
+	public static final String MARK_EXCLUDE_CHILDREN = "jarprocessor.exclude.children";
+	//Exclude this jar's children from pack200
+	public static final String MARK_EXCLUDE_CHILDREN_PACK = "jarprocessor.exclude.children.pack";
+	//Exclude this jar's children from signing
+	public static final String MARK_EXCLUDE_CHILDREN_SIGN = "jarprocessor.exclude.children.sign";
+	//Arguments used in pack200 for this jar
+	public static final String PACK_ARGS = "pack200.args"; //$NON-NLS-1$
+	
+	public static final String PACK200_PROPERTY = "org.eclipse.update.jarprocessor.pack200"; //$NON-NLS-1$
+	public static final String JRE = "@jre"; //$NON-NLS-1$
+	public static final String PATH = "@path"; //$NON-NLS-1$
+	public static final String NONE = "@none"; //$NON-NLS-1$
+
+	public static final String PACKED_SUFFIX = ".pack.gz"; //$NON-NLS-1$
+	public static final String JAR_SUFFIX = ".jar"; //$NON-NLS-1$
+
+	public static final FileFilter JAR_FILTER = new FileFilter() {
+		public boolean accept(File pathname) {
+			return pathname.isFile() && pathname.getName().endsWith(".jar"); //$NON-NLS-1$
+		}
+	};
+
+	public static final FileFilter PACK_GZ_FILTER = new FileFilter() {
+		public boolean accept(File pathname) {
+			return pathname.isFile() && pathname.getName().endsWith(PACKED_SUFFIX);
+		}
+	};
+
+	public static void close(Object stream) {
+		if (stream != null) {
+			try {
+				if (stream instanceof InputStream)
+					((InputStream) stream).close();
+				else if (stream instanceof OutputStream)
+					((OutputStream) stream).close();
+				else if (stream instanceof JarFile)
+					((JarFile) stream).close();
+			} catch (IOException e) {
+				//ignore
+			}
+		}
+	}
+
+	/**
+	 * get the set of commands to try to execute pack/unpack 
+	 * @param cmd, the command, either "pack200" or "unpack200"
+	 * @return String [] or null
+	 */
+	public static String[] getPack200Commands(String cmd) {
+		String[] locations = null;
+		String prop = System.getProperty(PACK200_PROPERTY);
+		String javaHome = System.getProperty("java.home"); //$NON-NLS-1$
+		if (NONE.equals(prop)) {
+			return null;
+		} else if (JRE.equals(prop)) {
+			locations = new String[] {javaHome + "/bin/" + cmd}; //$NON-NLS-1$
+		} else if (PATH.equals(prop)) {
+			locations = new String[] {cmd};
+		} else if (prop == null) {
+			locations = new String[] {javaHome + "/bin/" + cmd, cmd}; //$NON-NLS-1$ 
+		} else {
+			locations = new String[] {prop + "/" + cmd}; //$NON-NLS-1$
+		}
+		return locations;
+	}
+
+	/**
+	 * Transfers all available bytes from the given input stream to the given
+	 * output stream. Closes both streams if close == true, regardless of failure. 
+	 * Flushes the destination stream if close == false
+	 * 
+	 * @param source
+	 * @param destination
+	 * @param close 
+	 * @throws IOException
+	 */
+	public static void transferStreams(InputStream source, OutputStream destination, boolean close) throws IOException {
+		source = new BufferedInputStream(source);
+		destination = new BufferedOutputStream(destination);
+		try {
+			byte[] buffer = new byte[8192];
+			while (true) {
+				int bytesRead = -1;
+				if ((bytesRead = source.read(buffer)) == -1)
+					break;
+				destination.write(buffer, 0, bytesRead);
+			}
+		} finally {
+			if (close) {
+				close(source);
+				close(destination);
+			} else {
+				destination.flush();
+			}
+		}
+	}
+
+	/**
+	 * Deletes all the files and directories from the given root down (inclusive).
+	 * Returns false if we could not delete some file or an exception occurred
+	 * at any point in the deletion.
+	 * Even if an exception occurs, a best effort is made to continue deleting.
+	 */
+	public static boolean clear(java.io.File root) {
+		boolean result = clearChildren(root);
+		try {
+			if (root.exists())
+				result &= root.delete();
+		} catch (Exception e) {
+			result = false;
+		}
+		return result;
+	}
+
+	/**
+	 * Deletes all the files and directories from the given root down, except for 
+	 * the root itself.
+	 * Returns false if we could not delete some file or an exception occurred
+	 * at any point in the deletion.
+	 * Even if an exception occurs, a best effort is made to continue deleting.
+	 */
+	public static boolean clearChildren(java.io.File root) {
+		boolean result = true;
+		if (root.isDirectory()) {
+			String[] list = root.list();
+			// for some unknown reason, list() can return null.  
+			// Just skip the children If it does.
+			if (list != null)
+				for (int i = 0; i < list.length; i++)
+					result &= clear(new java.io.File(root, list[i]));
+		}
+		return result;
+	}
+
+	public static Set getPackExclusions(Properties properties) {
+		if (properties == null)
+			return Collections.EMPTY_SET;
+
+		String packExcludes = properties.getProperty(PACK_EXCLUDES);
+		if (packExcludes != null) {
+			String[] excludes = toStringArray(packExcludes, ","); //$NON-NLS-1$
+			Set packExclusions = new HashSet();
+			for (int i = 0; i < excludes.length; i++) {
+				packExclusions.add(excludes[i]);
+			}
+			return packExclusions;
+		}
+		return Collections.EMPTY_SET;
+	}
+
+	public static Set getSignExclusions(Properties properties) {
+		if (properties == null)
+			return Collections.EMPTY_SET;
+		String signExcludes = properties.getProperty(SIGN_EXCLUDES);
+		if (signExcludes != null) {
+			String[] excludes = toStringArray(signExcludes, ","); //$NON-NLS-1$
+			Set signExclusions = new HashSet();
+			for (int i = 0; i < excludes.length; i++) {
+				signExclusions.add(excludes[i]);
+			}
+			return signExclusions;
+		}
+		return Collections.EMPTY_SET;
+	}
+
+	public static String concat(String[] array) {
+		StringBuffer buffer = new StringBuffer();
+		for (int i = 0; i < array.length; i++) {
+			if (i > 0)
+				buffer.append(' ');
+			buffer.append(array[i]);
+		}
+		return buffer.toString();
+	}
+
+	public static String[] toStringArray(String input, String separator) {
+		StringTokenizer tokenizer = new StringTokenizer(input, separator);
+		int count = tokenizer.countTokens();
+		String[] result = new String[count];
+		for (int i = 0; i < count; i++) {
+			result[i] = tokenizer.nextToken().trim();
+		}
+		return result;
+	}
+
+	/**
+	 * Get the properties from the eclipse.inf file from the given jar.  If the file is not a jar, null is returned.
+	 * If the file is a jar, but does not contain an eclipse.inf file, an empty Properties object is returned.
+	 * @param jarFile
+	 * @return The eclipse.inf properties for the given jar file
+	 */
+	public static Properties getEclipseInf(File jarFile, boolean verbose) {
+		if (jarFile == null || !jarFile.exists()) {
+			if (verbose)
+				System.out.println("Failed to obtain eclipse.inf due to missing jar file: " + jarFile);
+			return null;
+		}
+		JarFile jar = null;
+		try {
+			jar = new JarFile(jarFile, false);
+		} catch (ZipException e) {
+			//not a jar, don't bother logging this.
+			return null;
+		} catch (IOException e) {
+			if (verbose) {
+				System.out.println("Failed to obtain eclipse.inf due to IOException: " + jarFile);
+				e.printStackTrace();
+			}
+			return null;
+		}
+		try {
+			JarEntry mark = jar.getJarEntry(MARK_FILE_NAME);
+			if (mark != null) {
+				InputStream in = jar.getInputStream(mark);
+				Properties props = new Properties();
+				props.load(in);
+				in.close();
+				return props;
+			}
+			return new Properties();
+		} catch (IOException e) {
+			if (verbose) {
+				System.out.println("Failed to obtain eclipse.inf due to IOException: " + jarFile);
+				e.printStackTrace();
+			}
+			return null;
+		} finally {
+			close(jar);
+		}
+	}
+
+	public static boolean shouldSkipJar(File input, boolean processAll, boolean verbose) {
+		Properties inf = getEclipseInf(input, verbose);
+		if (inf == null) {
+			//not a jar, could be a pack.gz
+			return false;
+		}
+		String exclude = inf.getProperty(MARK_EXCLUDE);
+
+		//was marked as exclude, we should skip
+		if (exclude != null && Boolean.valueOf(exclude).booleanValue())
+			return true;
+
+		//process all was set, don't skip
+		if (processAll)
+			return false;
+
+		//otherwise, we skip if not marked marked
+		String marked = inf.getProperty(MARK_PROPERTY);
+		return !Boolean.valueOf(marked).booleanValue();
+	}
+
+	/**
+	 * Stores the given properties in the output stream.  We store the properties 
+	 * in sorted order so that the signing hash doesn't change if the properties didn't change. 
+	 * @param props
+	 * @param stream
+	 */
+	public static void storeProperties(Properties props, OutputStream stream) {
+		PrintStream printStream = new PrintStream(stream);
+		printStream.print("#Processed using Jarprocessor\n"); //$NON-NLS-1$
+		SortedMap sorted = new TreeMap(props);
+		for (Iterator iter = sorted.keySet().iterator(); iter.hasNext();) {
+			String key = (String) iter.next();
+			printStream.print(key);
+			printStream.print(" = "); //$NON-NLS-1$
+			printStream.print(sorted.get(key));
+			printStream.print("\n");
+
+		}
+		printStream.flush();
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/ZipProcessor.java b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/ZipProcessor.java
new file mode 100644
index 0000000..118b667
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/ZipProcessor.java
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.jarprocessor;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+/**
+ * @author aniefer@ca.ibm.com
+ *
+ */
+public class ZipProcessor {
+
+	private IProcessStep signStep = null;
+	private IProcessStep packStep = null;
+	private IProcessStep packUnpackStep = null;
+	private IProcessStep unpackStep = null;
+
+	private String workingDirectory = null;
+	private Properties properties = null;
+	private Set packExclusions = null;
+	private Set signExclusions = null;
+	private String command = null;
+	private boolean packing = false;
+	private boolean signing = false;
+	private boolean repacking = false;
+	private boolean unpacking = false;
+	private boolean verbose = false;
+	private boolean processAll = false;
+
+	public void setWorkingDirectory(String dir) {
+		workingDirectory = dir;
+	}
+
+	public String getWorkingDirectory() {
+		if (workingDirectory == null)
+			workingDirectory = "."; //$NON-NLS-1$
+		return workingDirectory;
+	}
+
+	public void setSignCommand(String command) {
+		this.command = command;
+		this.signing = (command != null);
+	}
+
+	public void setPack(boolean pack) {
+		this.packing = pack;
+	}
+
+	public void setRepack(boolean repack) {
+		this.repacking = repack;
+	}
+
+	public void setUnpack(boolean unpack) {
+		this.unpacking = unpack;
+	}
+
+	public void setVerbose(boolean verbose) {
+		this.verbose = verbose;
+	}
+
+	public void setProcessAll(boolean all) {
+		this.processAll = all;
+	}
+
+	public void processZip(File zipFile) throws ZipException, IOException {
+		if (verbose)
+			System.out.println("Processing " + zipFile.getPath()); //$NON-NLS-1$
+		ZipFile zip = new ZipFile(zipFile);
+		initialize(zip);
+
+		String extension = unpacking ? "pack.gz" : ".jar"; //$NON-NLS-1$ //$NON-NLS-2$
+		File tempDir = new File(getWorkingDirectory(), "temp_" + zipFile.getName()); //$NON-NLS-1$
+		JarProcessor processor = new JarProcessor();
+		processor.setVerbose(verbose);
+		processor.setProcessAll(processAll);
+		processor.setWorkingDirectory(tempDir.getCanonicalPath());
+		if (unpacking) {
+			processor.addProcessStep(unpackStep);
+		}
+
+		File outputFile = new File(getWorkingDirectory(), zipFile.getName() + ".temp"); //$NON-NLS-1$
+		File parent = outputFile.getParentFile();
+		if (!parent.exists())
+			parent.mkdirs();
+		ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(outputFile));
+		Enumeration entries = zip.entries();
+		if (entries.hasMoreElements()) {
+			for (ZipEntry entry = (ZipEntry) entries.nextElement(); entry != null; entry = entries.hasMoreElements() ? (ZipEntry) entries.nextElement() : null) {
+				String name = entry.getName();
+
+				InputStream entryStream = zip.getInputStream(entry);
+
+				boolean pack = packing && !packExclusions.contains(name);
+				boolean sign = signing && !signExclusions.contains(name);
+				boolean repack = repacking && !packExclusions.contains(name);
+
+				File extractedFile = null;
+
+				if (entry.getName().endsWith(extension) && (pack || sign || repack || unpacking)) {
+					extractedFile = new File(tempDir, name);
+					parent = extractedFile.getParentFile();
+					if (!parent.exists())
+						parent.mkdirs();
+					if (verbose)
+						System.out.println("Extracting " + entry.getName()); //$NON-NLS-1$
+					FileOutputStream extracted = new FileOutputStream(extractedFile);
+					Utils.transferStreams(entryStream, extracted, true); // this will close the stream
+					entryStream = null;
+
+					boolean skip = Utils.shouldSkipJar(extractedFile, processAll, verbose);
+					if (skip) {
+						//skipping this file 
+						entryStream = new FileInputStream(extractedFile);
+						if (verbose)
+							System.out.println(entry.getName() + " is not marked, skipping."); //$NON-NLS-1$
+					} else {
+						if (unpacking) {
+							File result = processor.processJar(extractedFile);
+							name = name.substring(0, name.length() - extractedFile.getName().length()) + result.getName();
+							extractedFile = result;
+						} else {
+							if (repack || sign) {
+								processor.clearProcessSteps();
+								if (repack)
+									processor.addProcessStep(packUnpackStep);
+								if (sign)
+									processor.addProcessStep(signStep);
+								extractedFile = processor.processJar(extractedFile);
+							}
+							if (pack) {
+								processor.clearProcessSteps();
+								processor.addProcessStep(packStep);
+								File modifiedFile = processor.processJar(extractedFile);
+								if (modifiedFile.exists()) {
+									try {
+										String newName = name.substring(0, name.length() - extractedFile.getName().length()) + modifiedFile.getName();
+										if (verbose) {
+											System.out.println("Adding " + newName + " to " + outputFile.getPath()); //$NON-NLS-1$ //$NON-NLS-2$
+											System.out.println();
+										}
+										ZipEntry zipEntry = new ZipEntry(newName);
+										entryStream = new FileInputStream(modifiedFile);
+										zipOut.putNextEntry(zipEntry);
+										Utils.transferStreams(entryStream, zipOut, false); //we want to keep zipOut open
+										entryStream.close();
+										Utils.clear(modifiedFile);
+									} catch (IOException e) {
+										Utils.close(entryStream);
+										if (verbose) {
+											e.printStackTrace();
+											System.out.println("Warning: Problem reading " + modifiedFile.getPath() + ".");
+										}
+									}
+									entryStream = null;
+								} else if (verbose) {
+									System.out.println("Warning: " + modifiedFile.getPath() + " not found.");
+								}
+							}
+						}
+						if (extractedFile.exists()) {
+							try {
+								entryStream = new FileInputStream(extractedFile);
+							} catch (IOException e) {
+								if (verbose) {
+									e.printStackTrace();
+									System.out.println("Warning: Problem reading " + extractedFile.getPath() + ".");
+								}
+							}
+						}
+
+						if (verbose && entryStream != null) {
+							System.out.println("Adding " + name + " to " + outputFile.getPath()); //$NON-NLS-1$ //$NON-NLS-2$
+						}
+					}
+				}
+				if (entryStream != null) {
+					ZipEntry newEntry = new ZipEntry(name);
+					try {
+						zipOut.putNextEntry(newEntry);
+						Utils.transferStreams(entryStream, zipOut, false);
+						zipOut.closeEntry();
+					} catch (ZipException e) {
+						if(verbose) {
+							System.out.println("Warning: " + name + " already exists in " + outputFile.getName() + ".  Skipping.");
+						}
+					}
+					entryStream.close();
+				}
+
+				if (extractedFile != null)
+					Utils.clear(extractedFile);
+				
+				if (verbose) {
+					System.out.println();
+					System.out.println("Processing " + zipFile.getPath()); //$NON-NLS-1$
+				}
+			}
+		}
+		zipOut.close();
+		zip.close();
+
+		File finalFile = new File(getWorkingDirectory(), zipFile.getName());
+		if (finalFile.exists())
+			finalFile.delete();
+		outputFile.renameTo(finalFile);
+		Utils.clear(tempDir);
+	}
+
+	private void initialize(ZipFile zip) {
+		ZipEntry entry = zip.getEntry("pack.properties"); //$NON-NLS-1$
+		properties = new Properties();
+		if (entry != null) {
+			InputStream stream = null;
+			try {
+				stream = zip.getInputStream(entry);
+				properties.load(stream);
+			} catch (IOException e) {
+				if (verbose)
+					e.printStackTrace();
+			} finally {
+				Utils.close(stream);
+			}
+		}
+
+		packExclusions = Utils.getPackExclusions(properties);
+		signExclusions = Utils.getSignExclusions(properties);
+
+		packUnpackStep = new PackUnpackStep(properties, verbose);
+		packStep = new PackStep(properties, verbose);
+		signStep = new SignCommandStep(properties, command, verbose);
+		unpackStep = new UnpackStep(properties, verbose);
+	}
+}
diff --git a/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/pack-readme.html b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/pack-readme.html
new file mode 100644
index 0000000..ef5fe5e
--- /dev/null
+++ b/update/org.eclipse.update.core/jarprocessor/org/eclipse/update/internal/jarprocessor/pack-readme.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+  <title>Eclipse update packing tool readme</title>
+</head>
+<body>
+<h1>Eclipse update packing tool</h1>
+
+<h3>Overview</h3>
+The update packing tool processes a hierarchy of arbitrarily nested
+JARs and ZIP files.  It is a generic utility that performs a depth first traversal of 
+a nested hierarchy of ZIPs and JARs, performs various commands on
+each of the JARs in the hierarchy, and then rebuilds the same hierarchy
+of ZIPs and JARs again.  Currently its main functions are:
+<ul>
+	<li>Packing JARs using the Java 1.5 <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/pack200.html">pack200</a>
+	 command.</li>
+	 <li>Unpacking PACK.GZs using the Java 1.5 <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/unpack200.html">unpack200</a>
+	 command.</li>
+	 <li>Normalizing JARs for future compression by pack200. This is accomplished
+	 by running the pack200 command with the <tt>--repack</tt> command line argument.</li>
+	 <li>Signing JARs to allow for authentication of the origin of JARs. This is accomplished by
+	 running a supplied command (typically the command will just be a wrapper around
+	 the Java <a href="http://java.sun.com/j2se/1.3/docs/tooldocs/win32/jarsigner.html">jarsigner</a> tool).</li>
+</ul>
+The packing tool is used in the following contexts:
+<ul>
+	<li>During a PDE build, to prepare JARs for uploading to an Eclipse
+	update site.  In this usage, it is used to both nomalize JAR contents
+	(pack200 -repack), and sign JARs.</li>
+	<li>On an update site, to convert traditional JAR content into the 
+	compressed pack200 format.</li>
+	<li>From an Eclipse client application during update, to convert
+	compressed pack200 format content into executable JAR files.</li>
+</ul>
+<h3>Tool usage</h3>
+To run the packing tool, you need a 1.5 JRE installed. The tool is run
+by invoking Java as follows:
+
+<pre>
+	java jarprocessor.jar [options] input
+</pre>
+
+Where <tt>input</tt> is either a zip file, a directory, or a JAR (or a pack.gz file). All files ending 
+in ".jar" or ".pack.gz" in the provided zip or directory hierarchy
+will be processed. 
+The following additional command line arguments are supported:
+<ul>
+<li>-repack : Normalize the jars using pack200 <tt>--repack</tt></li>
+<li>-sign &lt;cmd&gt; : signs the jars by executing the provided command.  
+The command will be provided a single argument that will be the full path of the JAR to process.
+</li>
+<li>-pack : for each input in JAR form, produce a corresponding output
+in packed form.  For an input "a.jar", the output is a.jar.pack.gz.  
+</li>
+<li>-unpack : for each input in packed form, produce a corresponding output
+in unpacked form.  For an input "a.jar.pack.gz", the output is "a.jar". -unpack is mutually exclusive with -repack, -pack and -sign.</li>
+<li>-outputDir &lt;dir&gt; : The directory to put the tool's output into.  If the input was a zip file, then an output zip file will be
+created containg all the output files.  If the input was a directory, for each input file there is a corresponding output file in the output directory. By default the current working directory is used.  If the input is in the same
+directory as the output, the input files may be overwritten.</li>
+</ul>
+
+Additionally, when the input is a zip file, it may contain a file called
+<tt>pack.properties</tt>.  The pack.properties file supports the following values:
+<ul>
+<li>pack.excludes =  jarName[, jarName]* : A comma-delimited list of JARs that should not be packed or repacked.
+</li>
+<li>sign.excludes =  jarName[, jarName]* : A comma-delimited list of JARs that should not be signed.
+</li>
+<li>&lt;jarname&gt;.pack.args = option[, option]* : A comma-delimited list of additional arguments that should
+be passed to pack200 when packing any jar with name &lt;jarname&gt;.
+</ul>
+</p>
+<p>
+<font size=-1>
+Copyright (c) IBM Corporation and others 2006. All rights reserved. This program and the accompanying materials
+are made available under the terms of the 
+<a href="http://www.eclipse.org/legal/epl-v10.html">Eclipse Public License v1.0</a>.
+</font>
+</body>
+</html>
diff --git a/update/org.eclipse.update.core/plugin.properties b/update/org.eclipse.update.core/plugin.properties
new file mode 100644
index 0000000..baa2ab4
--- /dev/null
+++ b/update/org.eclipse.update.core/plugin.properties
@@ -0,0 +1,20 @@
+###############################################################################

+# Copyright (c) 2000, 2011 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

+###############################################################################

+pluginName= Install/Update Core

+providerName= Eclipse.org

+fragmentNameWin= Install/Update Core for Windows

+fragmentNameLinux= Install/Update Core for Linux

+featureTypes.name=Install/Update Concrete Feature Type Implementation Factory

+siteTypes.name=Install/Update Concrete Site Type Implementation Factory

+installHandlers.name=Install/Update Global Install Handlers

+deltaHandler.name=Install/Update Delta Handler

+productProviderName= Update Core Product Provider

+

diff --git a/update/org.eclipse.update.core/plugin.xml b/update/org.eclipse.update.core/plugin.xml
new file mode 100644
index 0000000..db0d7fd
--- /dev/null
+++ b/update/org.eclipse.update.core/plugin.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<?eclipse version="3.0"?>

+<!--

+     Copyright (c) 2005, 2010 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

+ -->

+

+<plugin>

+

+   <extension-point id="featureTypes" name="%featureTypes.name" schema="schema/featureTypes.exsd"/>

+   <extension-point id="siteTypes" name="%siteTypes.name" schema="schema/siteTypes.exsd"/>

+   <extension-point id="installHandlers" name="%installHandlers.name" schema="schema/installHandlers.exsd"/>

+   <!-- The deltaHandler extension point is deprecated. Do not use it -->

+   <extension-point id="deltaHandler" name="%deltaHandler.name"/>

+

+   <extension

+         id="preferenceInitializer"

+         point="org.eclipse.core.runtime.preferences">

+      <initializer class="org.eclipse.update.internal.core.UpdatePreferencesInitializer"/>

+   </extension>

+

+   <extension

+         id="packaged"

+         point="org.eclipse.update.core.featureTypes">

+      <feature-factory

+            class="org.eclipse.update.internal.core.FeaturePackagedFactory">

+      </feature-factory>

+   </extension>

+   <extension

+         id="installed"

+         point="org.eclipse.update.core.featureTypes">

+      <feature-factory

+            class="org.eclipse.update.internal.core.FeatureExecutableFactory">

+      </feature-factory>

+   </extension>

+   <extension

+         id="http"

+         point="org.eclipse.update.core.siteTypes">

+      <site-factory

+            class="org.eclipse.update.internal.core.ExtendedSiteURLFactory">

+      </site-factory>

+   </extension>

+   <extension

+         id="file"

+         point="org.eclipse.update.core.siteTypes">

+      <site-factory

+            class="org.eclipse.update.internal.core.SiteFileFactory">

+      </site-factory>

+   </extension>

+   <extension

+         id="DefaultInstallHandler"

+         point="org.eclipse.update.core.installHandlers">

+      <install-handler

+            class="org.eclipse.update.internal.core.DefaultInstallHandler">

+      </install-handler>

+   </extension>

+   <extension

+         id="DeltaInstallHandler"

+         point="org.eclipse.update.core.installHandlers">

+      <install-handler

+            class="org.eclipse.update.internal.core.DeltaInstallHandler">

+      </install-handler>

+   </extension>

+   <extension

+         id="JarDeltaInstallHandler"

+         point="org.eclipse.update.core.installHandlers">

+      <install-handler

+            class="org.eclipse.update.internal.core.JarDeltaInstallHandler">

+      </install-handler>

+   </extension>

+   <extension

+         id="standaloneUpdate"

+         point="org.eclipse.core.runtime.applications">

+      <application>

+         <run

+               class="org.eclipse.update.standalone.StandaloneUpdateApplication">

+         </run>

+      </application>

+   </extension>

+      <extension

+         id="siteOptimizer"

+         point="org.eclipse.core.runtime.applications">

+      <application>

+         <run

+               class="org.eclipse.update.internal.provisional.SiteOptimizerApplication">

+         </run>

+      </application>

+   </extension>

+	<extension

+			id="updateCoreProductProvider"

+			name="%productProviderName"

+			point="org.eclipse.core.runtime.products">

+		<provider> 

+			<run class="org.eclipse.update.internal.core.ProductProvider"/> 

+		</provider> 

+	</extension>

+

+</plugin>

diff --git a/update/org.eclipse.update.core/pom.xml b/update/org.eclipse.update.core/pom.xml
new file mode 100644
index 0000000..dd8679e
--- /dev/null
+++ b/update/org.eclipse.update.core/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2012 Eclipse Foundation.
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Distribution License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/org/documents/edl-v10.php
+ 
+  Contributors:
+     Igor Fedorenko - initial implementation
+-->
+
+<project
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>eclipse.platform</artifactId>
+    <groupId>org.eclipse.platform</groupId>
+    <version>3.8.0-SNAPSHOT</version>
+    <relativePath>../../</relativePath>
+  </parent>
+  <groupId>org.eclipse.platform</groupId>
+  <artifactId>org.eclipse.update.core</artifactId>
+  <version>3.2.600-SNAPSHOT</version>
+  <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/update/org.eclipse.update.core/preferences.ini b/update/org.eclipse.update.core/preferences.ini
new file mode 100644
index 0000000..7936e7f
--- /dev/null
+++ b/update/org.eclipse.update.core/preferences.ini
@@ -0,0 +1,8 @@
+# The port number on which the sever listens for http requests.
+# If the port is 0 and arbitrary port is picked by the system.
+
+# Defines the policy url that contains directives for redirecting 
+# updates to different update sites.
+#updatePolicyURL=
+
+
diff --git a/update/org.eclipse.update.core/schema/featureTypes.exsd b/update/org.eclipse.update.core/schema/featureTypes.exsd
new file mode 100644
index 0000000..0517f10
--- /dev/null
+++ b/update/org.eclipse.update.core/schema/featureTypes.exsd
@@ -0,0 +1,122 @@
+<?xml version='1.0' encoding='UTF-8'?>

+<!-- Schema file written by PDE -->

+<schema targetNamespace="org.eclipse.update.core">

+<annotation>

+      <appInfo>

+         <meta.schema plugin="org.eclipse.update.core" id="featureTypes" name="Feature Type Factory"/>

+      </appInfo>

+      <documentation>

+         The platform update mechanism supports pluggable feature type
+implementations. A new feature type can be registered in order
+to support
+alternate packaging and verification schemes.
+&lt;p&gt;
+The &lt;code&gt;featureTypes&lt;/code&gt;
+extension point allows alternate feature implementations to be
+registered using a symbolic type identifier. Whenever the 
+type is referenced using this identifier, the supplied factory
+is used to create the correct concrete feature implementation.
+&lt;/p&gt;

+      </documentation>

+   </annotation>

+

+   <element name="extension">

+      <complexType>

+         <sequence>

+            <element ref="feature-factory" minOccurs="1" maxOccurs="unbounded"/>

+         </sequence>

+         <attribute name="point" type="string" use="required">

+            <annotation>

+               <documentation>

+                  must be specified as &lt;b&gt;org.eclipse.update.core.featureTypes&lt;/b&gt;

+               </documentation>

+            </annotation>

+         </attribute>

+         <attribute name="id" type="string">

+            <annotation>

+               <documentation>

+                  must be specified. Identifies the new feature type

+               </documentation>

+            </annotation>

+         </attribute>

+         <attribute name="name" type="string">

+            <annotation>

+               <documentation>

+                  optional displayable label for the new feature type

+               </documentation>

+               <appInfo>

+                  <meta.attribute translatable="true"/>

+               </appInfo>

+            </annotation>

+         </attribute>

+      </complexType>

+   </element>

+

+   <element name="feature-factory">

+      <complexType>

+         <attribute name="class" type="string" use="required">

+            <annotation>

+               <documentation>

+                  fully qualified name of the factory class for the identified feature type

+               </documentation>

+               <appInfo>

+                  <meta.attribute kind="java" basedOn="org.eclipse.update.configuration.IInstallDeltaHandler"/>

+               </appInfo>

+            </annotation>

+         </attribute>

+      </complexType>

+   </element>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="examples"/>

+      </appInfo>

+      <documentation>

+         The following is an example of new feature type registration:
+&lt;p&gt;
+&lt;pre&gt;
+   &lt;extension
+      id=&quot;custom&quot;
+      point=&quot;org.eclipse.update.core.featureTypes&quot;
+      name=&quot;Custom packaged feature&quot;&gt;
+      &lt;feature-factory
+          class=&quot;com.xyz.update.CustomFeatureFactory&quot;&gt;
+      &lt;/feature-factory&gt;
+   &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="apiInfo"/>

+      </appInfo>

+      <documentation>

+         Registered factory classes must implement 
+&lt;code&gt;&lt;b&gt;org.eclipse.update.core.IFeatureFactory&lt;/b&gt;&lt;/code&gt;

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="implementation"/>

+      </appInfo>

+      <documentation>

+         The platform supplies two standard implementations of feature
+types. One representing the default packaged feature type, and
+the other representing an installed feature type.

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="copyright"/>

+      </appInfo>

+      <documentation>

+         Copyright (c) 2004 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 &lt;a href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;.

+      </documentation>

+   </annotation>

+

+</schema>

diff --git a/update/org.eclipse.update.core/schema/installHandlers.exsd b/update/org.eclipse.update.core/schema/installHandlers.exsd
new file mode 100644
index 0000000..084a25f
--- /dev/null
+++ b/update/org.eclipse.update.core/schema/installHandlers.exsd
@@ -0,0 +1,113 @@
+<?xml version='1.0' encoding='UTF-8'?>

+<!-- Schema file written by PDE -->

+<schema targetNamespace="org.eclipse.update.core">

+<annotation>

+      <appInfo>

+         <meta.schema plugin="org.eclipse.update.core" id="installHandlers" name="Global Install Handlers"/>

+      </appInfo>

+      <documentation>

+         Extension point for registering global install handlers. Global
+install handlers can be referenced by features 
+(using the &lt;code&gt;&amp;lt;feature&amp;gt; &amp;lt;install-handler&amp;gt;&lt;/code&gt;
+tags) without having to include a copy of the handler code as
+part of the downloadable feature.

+      </documentation>

+   </annotation>

+

+   <element name="extension">

+      <complexType>

+         <sequence>

+            <element ref="install-handler" minOccurs="1" maxOccurs="unbounded"/>

+         </sequence>

+         <attribute name="point" type="string" use="required">

+            <annotation>

+               <documentation>

+                  must be specified as &lt;b&gt;org.eclipse.update.core.installHandlers&lt;/b&gt;

+               </documentation>

+            </annotation>

+         </attribute>

+         <attribute name="id" type="string">

+            <annotation>

+               <documentation>

+                  must be specified. Identifies the new install handler

+               </documentation>

+            </annotation>

+         </attribute>

+         <attribute name="name" type="string">

+            <annotation>

+               <documentation>

+                  optional displayable label for the new install handler

+               </documentation>

+               <appInfo>

+                  <meta.attribute translatable="true"/>

+               </appInfo>

+            </annotation>

+         </attribute>

+      </complexType>

+   </element>

+

+   <element name="install-handler">

+      <complexType>

+         <attribute name="class" type="string" use="required">

+            <annotation>

+               <documentation>

+                  fully qualified name of the handler implementation class for the identified
+install handler

+               </documentation>

+            </annotation>

+         </attribute>

+      </complexType>

+   </element>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="examples"/>

+      </appInfo>

+      <documentation>

+         The following is an example of new global install handler registration:
+&lt;p&gt;
+&lt;pre&gt;
+   &lt;extension
+        id=&quot;custom&quot;
+        point=&quot;org.eclipse.update.core.installHandlers&quot;
+        name=&quot;Custom install handler&quot;&gt;
+     &lt;install-handler
+        class=&quot;com.xyz.update.CustomInstallHandler&quot;&gt;
+     &lt;/install-handler&gt;
+   &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="apiInfo"/>

+      </appInfo>

+      <documentation>

+         Registered install handler classes must implement 
+&lt;code&gt;org.eclipse.update.core.IInstallHandler&lt;/code&gt; interface.
+Implementers should extend base class &lt;code&gt;org.eclipse.update.core.BaseInstallHandler&lt;/code&gt;.

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="implementation"/>

+      </appInfo>

+      <documentation>

+         The platform supplies a simple install handler that is registered as &lt;code&gt;org.eclipse.update.core.DefaultInstallHandler&lt;/code&gt;. If used, it will copy any non-plug-in data entries provided with the feature into the feature installation directory.

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="copyright"/>

+      </appInfo>

+      <documentation>

+         Copyright (c) 2004 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 &lt;a href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;.

+      </documentation>

+   </annotation>

+

+</schema>

diff --git a/update/org.eclipse.update.core/schema/siteTypes.exsd b/update/org.eclipse.update.core/schema/siteTypes.exsd
new file mode 100644
index 0000000..102dc16
--- /dev/null
+++ b/update/org.eclipse.update.core/schema/siteTypes.exsd
@@ -0,0 +1,119 @@
+<?xml version='1.0' encoding='UTF-8'?>

+<!-- Schema file written by PDE -->

+<schema targetNamespace="org.eclipse.update.core">

+<annotation>

+      <appInfo>

+         <meta.schema plugin="org.eclipse.update.core" id="siteTypes" name="Site Type Factory"/>

+      </appInfo>

+      <documentation>

+         The platform update mechanism supports pluggable site type
+implementations. A new site type can be registered in order
+to support alternate site layout schemes.
+&lt;p&gt;
+The &lt;code&gt;siteTypes&lt;/code&gt;
+extension point allows alternate site implementations to be
+registered using a symbolic type identifier. Whenever the 
+type is referenced using this identifier, the supplied factory
+is used to create the correct concrete site implementation.
+&lt;/p&gt;

+      </documentation>

+   </annotation>

+

+   <element name="extension">

+      <complexType>

+         <sequence>

+            <element ref="site-factory" minOccurs="1" maxOccurs="unbounded"/>

+         </sequence>

+         <attribute name="point" type="string" use="required">

+            <annotation>

+               <documentation>

+                  must be specified as &lt;b&gt;org.eclipse.update.core.siteTypes&lt;/b&gt;

+               </documentation>

+            </annotation>

+         </attribute>

+         <attribute name="id" type="string">

+            <annotation>

+               <documentation>

+                  must be specified. Identifies the new site type

+               </documentation>

+            </annotation>

+         </attribute>

+         <attribute name="name" type="string">

+            <annotation>

+               <documentation>

+                  optional displayable label for the new site type

+               </documentation>

+               <appInfo>

+                  <meta.attribute translatable="true"/>

+               </appInfo>

+            </annotation>

+         </attribute>

+      </complexType>

+   </element>

+

+   <element name="site-factory">

+      <complexType>

+         <attribute name="class" type="string" use="required">

+            <annotation>

+               <documentation>

+                  fully qualified name of the factory class for the identified
+site type

+               </documentation>

+            </annotation>

+         </attribute>

+      </complexType>

+   </element>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="examples"/>

+      </appInfo>

+      <documentation>

+         The following is an example of new site type registration.
+&lt;p&gt;
+&lt;pre&gt;
+   &lt;extension
+        id=&quot;custom&quot;
+        point=&quot;org.eclipse.update.core.siteTypes&quot;
+        name=&quot;Custom site&quot;&gt;
+      &lt;site-factory
+          class=&quot;com.xyz.update.CustomSiteFactory&quot;&gt;
+      &lt;/site-factory&gt;
+   &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="apiInfo"/>

+      </appInfo>

+      <documentation>

+         Registered factory classes must implement 
+&lt;code&gt;&lt;b&gt;org.eclipse.update.core.ISiteFactory&lt;/b&gt;&lt;/code&gt;

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="implementation"/>

+      </appInfo>

+      <documentation>

+         The platform supplies two standard implementations of site
+types. One representing the default update server type, and
+the other representing the local file system site.

+      </documentation>

+   </annotation>

+

+   <annotation>

+      <appInfo>

+         <meta.section type="copyright"/>

+      </appInfo>

+      <documentation>

+         Copyright (c) 2004 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 &lt;a href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;.

+      </documentation>

+   </annotation>

+

+</schema>

diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IActivity.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IActivity.java
new file mode 100644
index 0000000..6c18192
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IActivity.java
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+import java.util.Date;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Install activity.
+ * Represents a record of an installation action performed
+ * on a particular installation configuration.
+ * <p>
+ * This interface is not intended to 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>
+ * @see org.eclipse.update.configuration.IInstallConfiguration
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IActivity extends IAdaptable {
+
+	/**
+	 * Indicates feature installation action
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_FEATURE_INSTALL = 1;
+
+	/**
+	 * Indicates feature removal (uninstallation) action
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_FEATURE_REMOVE = 2;
+
+	/**
+	 * Indicates an addition of a site to a configuration
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_SITE_INSTALL = 3;
+
+	/**
+	 * Indicates removal of a site from a configuration
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_SITE_REMOVE = 4;
+
+	/**
+	 * Indicates feature unconfiguration action
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_UNCONFIGURE = 5;
+
+	/**
+	 * Indicates feature configuration action
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_CONFIGURE = 6;
+
+	/**
+	 * Indicates reverting to a prior configuration state
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_REVERT = 7;
+
+	/**
+	 * Indicates reconcilliation with changes made directly to the site
+	 * installation directory
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_RECONCILIATION = 8;
+
+	/**
+	 * Indicates adding the configuration to a preserved state
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int ACTION_ADD_PRESERVED = 9;
+	
+	/**
+	 * Indicates the action completed cussessfully
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int STATUS_OK = 0;
+
+	/**
+	 * Indicates the action did not complete successfully
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int STATUS_NOK = 1;
+
+	/**
+	 * Returns the action code for this activity
+	 * 
+	 * @see #ACTION_FEATURE_INSTALL
+	 * @see #ACTION_FEATURE_REMOVE
+	 * @see #ACTION_SITE_INSTALL
+	 * @see #ACTION_SITE_REMOVE
+	 * @see #ACTION_UNCONFIGURE
+	 * @see #ACTION_CONFIGURE
+	 * @see #ACTION_REVERT
+	 * @see #ACTION_RECONCILIATION
+	 * @return action code, as defined in this interface
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 int getAction();
+
+	/**
+	 * Returns the displayable label for this action
+	 * 
+	 * @return diplayable label for action
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 String getLabel();
+
+	/**
+	 * Returns the creation date of this activity
+	 * 
+	 * @return activity date
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 Date getDate();
+
+	/** 
+	 * Returns the activity completion status
+	 * 
+	 * @see #STATUS_OK
+	 * @see #STATUS_NOK
+	 * @return completion status, as defined in this interface
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 int getStatus();
+
+	/**
+	 * Returns the installation configuration that was the result of 
+	 * this action
+	 * 
+	 * @return installation configuration
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 IInstallConfiguration getInstallConfiguration();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IConfiguredSite.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IConfiguredSite.java
new file mode 100644
index 0000000..8c5aaf8
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IConfiguredSite.java
@@ -0,0 +1,380 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.IVerificationListener;
+
+/**
+ * Configured Site.
+ * Represents an installation site "filtered" by configuration information.
+ * Configured site is the target of the feature update operations (install
+ * feature, remove feature, configure feature, unconfigure feature).
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IConfiguredSite extends IAdaptable {
+
+	/**
+	 * Returns the underlying "unfiltered" site.
+	 * 
+	 * @return the underlying site 
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 ISite getSite();
+
+	/**
+	 * Indicates whether updates can be applied to the site.
+	 * 
+	 * <code>IStatus.isOk()</code> return <code>true</code> if
+	 * the site can be updated, <code>false</code> otherwise.
+	 * 
+	 * If updates cannot be aplied, the status contains the error message, and
+	 * the possible exception. 
+	 * 
+	 * @see IStatus
+	 * @return an IStatus
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IStatus verifyUpdatableStatus();
+
+	/**
+	 * Indicates whether updates can be applied to the site.
+	 * 
+	 * A configuration site is tagged a non-updatable by reading
+	 * the platform configuration for this site.
+	 * 
+	 * @return <code>true</code> if the site can be updated, 
+	 * <code>false</code> otherwise
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean isUpdatable();
+
+	/**
+	 * Install the specified feature on this site.
+	 * 
+	 * @param feature feature to install
+	 * @param verificationListener verification listener, or <code>null</code>
+	 * @param monitor progress monitor, or <code>null</code>
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IFeatureReference install(IFeature feature, IVerificationListener verificationListener, IProgressMonitor monitor) throws CoreException;
+
+	/**
+	 * Install the specified feature on this site.
+	 * Only the specified optional features will be installed
+	 * 
+	 * @param feature feature to install
+	 * @param optionalFeatures optional features to install
+	 * @param verificationListener verification listener, or <code>null</code>
+	 * @param monitor progress monitor, or <code>null</code>
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IFeatureReference install(IFeature feature, IFeatureReference[] optionalFeatures, IVerificationListener verificationListener, IProgressMonitor monitor) throws CoreException;
+
+
+	/**
+	 * Remove (uninstall) the specified feature from this site
+	 * 
+	 * @param feature feature to remove
+	 * @param monitor progress monitor, or <code>null</code>
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void remove(IFeature feature, IProgressMonitor monitor) throws CoreException;
+
+	/**
+	 * Indicates if the specified feature is "broken". A feature is considered
+	 * to be broken in the context of this site, if some of the plug-ins
+	 * referenced by the feature are not installed on this site.
+	 * 
+	 * The status code is <code>IStatus.ERROR</code> if the feature is considered
+	 * broken. The Status may contain the reason why the feature is broken.
+	 * The status code is <code>IStatus.OK</code> if the feature is not considered
+	 * broken.
+	 * 
+	 * @param feature the feature
+	 * @return the status for this feature on this configured site
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 IStatus getBrokenStatus(IFeature feature);
+
+	/**
+	 * Indicates if the specified feature is configured on this site.
+	 * 
+	 * @param feature the feature
+	 * @return <code>true</code> if the feature is configured,
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean isConfigured(IFeature feature);
+
+	/**
+	 * Configure the specified feature on this site. The configured
+	 * feature will be included on next startup.
+	 * 
+	 * @param feature the feature
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void configure(IFeature feature) throws CoreException;
+
+	/**
+	 * Unconfigure the specified feature from this site. The unconfigured
+	 * feature will be omitted on the next startup.
+	 * 
+	 * @param feature the feature
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean unconfigure(IFeature feature) throws CoreException;
+
+	/**
+	 * Return references to features configured on this site.
+	 * 
+	 * @return an array of feature references, or an empty array.
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IFeatureReference[] getConfiguredFeatures();
+
+	/**
+	 * Return all features installed on this site (configured as well
+	 * as unconfigured). Note, that if the site requires reconciliation,
+	 * the result may not match the result of the corresponding method
+	 * on the underlying site.
+	 * 
+	 * @see ISite#getFeatureReferences()
+	 * @return an array of site feature references, or an empty array.
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IFeatureReference[] getFeatureReferences();
+
+	/**
+	 * Returns the install configuration object this site is part of.
+	 * 
+	 * @return install configuration object
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 IInstallConfiguration getInstallConfiguration();
+
+	/**
+	 * Adds a change listener to the configured site.
+	 * 
+	 * @param listener the listener to add
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void addConfiguredSiteChangedListener(IConfiguredSiteChangedListener listener);
+
+	/**
+	 * Removes a change listener from the configured site.
+	 * 
+	 * @param listener the listener to remove
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void removeConfiguredSiteChangedListener(IConfiguredSiteChangedListener listener);
+
+	/**
+	 * Indicates if the site is an extension site.
+	 * 
+	 * @return <code>true</code> if the site is an extension site,
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean isExtensionSite();
+
+	/**
+	 * Indicates if the site is a product site.
+	 * 
+	 * @return <code>true</code> if the site is a product site,
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean isProductSite();
+
+	/**
+	 * Indicates if the site is a private site.
+	 * This does not check if this private site belongs to the
+	 * product that is running.
+	 * 
+	 * @return <code>true</code> if the site is a private site,
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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>
+	 * @deprecated private site are considered the same as extension site (3.0)
+	 */
+	public boolean isPrivateSite();
+
+	/**
+	 * Indicates if the site has been linked by a native
+	 * installer.
+	 * 
+	 * @return <code>true</code> if the site is a natively linked site,
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean isNativelyLinked() throws CoreException;
+
+	/**
+	 * Sets if the site is enabled
+	 * 
+	 * @param value <code>true</code> if the site is enable, <code>false</code>
+	 * otherwise
+	 * @since 2.1
+	 * <p>
+	 * <b>Note:</b> This method 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 void setEnabled(boolean value);
+
+	/**
+	 * Indicates if the site is enabled. 
+	 * If a site is not enable, all teh features are considered disabled.
+	 * 
+	 * @return <code>true</code> if the site is enable, <code>false</code>
+	 * otherwise
+	 * @since 2.1
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean isEnabled();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IConfiguredSiteChangedListener.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IConfiguredSiteChangedListener.java
new file mode 100644
index 0000000..7266208
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IConfiguredSiteChangedListener.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+import org.eclipse.update.core.IFeature;
+
+/**
+ * Configuration change listener. * 
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IConfiguredSiteChangedListener {
+	
+	/**
+	 * Indicates the specified feature was installed.
+	 * 
+	 * @param feature the feature
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void featureInstalled(IFeature feature);
+	
+	/**
+	 * Indicates the specified feature was removed (uninstalled)
+	 * 
+	 * @param feature the feature
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void featureRemoved(IFeature feature);
+	
+	/**
+	 * Indicates the specified feature was configured.
+	 * 
+	 * @param feature the feature
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void featureConfigured(IFeature feature);
+	
+	/**
+	 * Indicates the specified feature was unconfigured.
+	 * 
+	 * @param feature the feature
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void featureUnconfigured(IFeature feature);
+}
+
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallConfiguration.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallConfiguration.java
new file mode 100644
index 0000000..473b970
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallConfiguration.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+import java.io.File;
+import java.util.Date;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Installation configuration.
+ * Represents a specific configuration of a number of sites as a point
+ * in time. Maintains a record of the specific activities that resulted
+ * in this configuration. Current installation configuration is
+ * the configuration the platform was started with.
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IInstallConfiguration extends IAdaptable {
+
+	/**
+	 * Indicates if this is the current configuration
+	 * 
+	 * @return <code>true</code> if this is the current configuration,
+	 * <code>false</code> otherwise
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 boolean isCurrent();
+
+	/**
+	 * Return the sites that are part of this configuration.
+	 * 
+	 * @return an array of configured sites, or an empty array.
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IConfiguredSite[] getConfiguredSites();
+
+	/**
+	 * Create a new installation site, based on a local file 
+	 * system directory. Note, the site is not added to the
+	 * configuration as a result of this call.
+	 * 
+	 * @param directory file directory
+	 * @return new site
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IConfiguredSite createConfiguredSite(File directory) throws CoreException;
+
+	/**
+	 * Create a new linked site, based on a local file 
+	 * system directory. Note, the site is not added to the
+	 * configuration as a result of this call.
+	 * The linked site is only created if the directory is an
+	 * already existing extension site and if it is not already
+	 * natively linked to the local site.
+	 * 
+	 * @param directory file directory
+	 * @return new linked site
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IConfiguredSite createLinkedConfiguredSite(File directory) throws CoreException;
+
+	/**
+	 * Adds the specified site to this configuration.
+	 * 
+	 * @param site new site
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void addConfiguredSite(IConfiguredSite site);
+
+	/**
+	 * Removes the specified site from this configuration.
+	 * 
+	 * @param site site to remove
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void removeConfiguredSite(IConfiguredSite site);
+
+	/**
+	 * Adds a configuration change listener.
+	 * 
+	 * @param listener the listener
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void addInstallConfigurationChangedListener(IInstallConfigurationChangedListener listener);
+
+	/**
+	 * Removes a configuration change listener.
+	 * 
+	 * @param listener the listener
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void removeInstallConfigurationChangedListener(IInstallConfigurationChangedListener listener);
+
+	/**
+	 * Return the list of activities that resulted in this configuration.
+	 * There is always at least one activity
+	 * 
+	 * @return an array of activities
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IActivity[] getActivities();
+
+	/**
+	 * Retrun the date the configuration was created.
+	 * 
+	 * @return create date
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 Date getCreationDate();
+
+	/**
+	 * Return the configuration label.
+	 * 
+	 * @return the configuration label. If the configuration label was not
+	 * explicitly set, a default label is generated based on the creation
+	 * date
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 String getLabel();
+
+	/**
+	 * Sets the configuration label.
+	 * 
+	 * @param label the label
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void setLabel(String label);
+
+	/**
+	 * Returns an integer that represents a time stamp created at the beginning of a new configuration time line.
+	 * Time line is started when configuration state is created by a full file system reconciliation. All configurations
+	 * subsequently created will have the same time line until the next full reconciliation. Certain operations
+ 	 * (e.g. revert) make sense only between objects that belong to the same time line.
+ 	 * 
+	 * @since 2.0.2
+	 * @return the time stamp of the full system reconciliation
+	 * 
+	 * <b>Note:</b> This method 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 long getTimeline();
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallConfigurationChangedListener.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallConfigurationChangedListener.java
new file mode 100644
index 0000000..d6f02c2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallConfigurationChangedListener.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+/**
+ * Configuration change listener.
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IInstallConfigurationChangedListener {
+	
+	/**
+	 * Indicates the specified site was added to the configuration
+	 * 
+	 * @param site the site
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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>
+	 */
+	void installSiteAdded(IConfiguredSite site);
+	
+	/**
+	 * Indicates the specified site was removed from the configuration
+	 * 
+	 * @param site the site
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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>
+	 */
+	void installSiteRemoved(IConfiguredSite site);
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallDeltaHandler.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallDeltaHandler.java
new file mode 100644
index 0000000..71e92e2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IInstallDeltaHandler.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.update.configuration;
+
+/**
+ * Install Delta Handler.
+ * Presents the changes the reconciler found to the user
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IInstallDeltaHandler{
+
+	/**
+	 * Sets the list of session delta to present to the user
+	 * 
+	 * @param deltas an Array of <code>ISessionDelta</code>
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void init(ISessionDelta[] deltas);
+
+	/**
+	 * Prompt the user to configure or unconfigure
+	 * new features found during reconciliation
+	 * 
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void open();
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSite.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSite.java
new file mode 100644
index 0000000..bcc03e9
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSite.java
@@ -0,0 +1,274 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.update.core.IFeature;
+
+/**
+ * Local Site.
+ * Represents the local installation. It consists of the current
+ * configuration and the configuration history. A local site
+ * manages the number of configuration histories kept. It also allows
+ * specific configuration histories to be saved.
+ * <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> 
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ILocalSite extends IAdaptable {
+
+	/**
+	 * Return the current configuration.
+	 * 
+	 * @return current configuration
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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>
+	 */
+	IInstallConfiguration getCurrentConfiguration();
+
+	/**
+	 * Return configuration history.
+	 * 
+	 * @return an array of configurations, or an empty array.
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IInstallConfiguration[] getConfigurationHistory();
+
+	/**
+	 * Reverts the local site to use the specified configuration.
+	 * The result of this operation is a new configuration that
+	 * contains the same configured features as the specified configuration.
+	 * The new configuration becomes the current configuration.
+	 * 
+	 * @param configuration configuration state to revert to
+	 * @param monitor progress monitor
+	 * @param handler problem handler
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void revertTo(
+		IInstallConfiguration configuration,
+		IProgressMonitor monitor,
+		IProblemHandler handler)
+		throws CoreException;
+
+	/**
+	 * Creates a new configuration containing the same state as the 
+	 * specified configuration. The new configuration is not added to
+	 * this lical site.
+	 * 
+	 * @return cloned configuration
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IInstallConfiguration cloneCurrentConfiguration() throws CoreException;
+
+	/**
+	 * Adds the specified configuration to this local site.
+	 * The new configuration becomes the current one.
+	 * 
+	 * @param config the configuration
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void addConfiguration(IInstallConfiguration config);
+
+	/**
+	 * Saves the local site state
+	 * 
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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>
+	 * @return true if a restart is needed. This return code was added in 3.0.
+	 */
+	public boolean save() throws CoreException;
+
+	/**
+	 * Indicates how many configuration histories should be maintained.
+	 * Histories beyond the specified count are automatically deleted.
+	 * 
+	 * @return number of past configurations to keep as history
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 int getMaximumHistoryCount();
+
+	/**
+	 * Sets the number of past configurations to keep in history
+	 * 
+	 * @param history number of configuration to keep
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void setMaximumHistoryCount(int history);
+
+	/**
+	 * Adds a site change listener
+	 * 
+	 * @param listener the listener
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void addLocalSiteChangedListener(ILocalSiteChangedListener listener);
+
+	/**
+	 * Removes a site listener
+	 * 
+	 * @param listener the listener
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void removeLocalSiteChangedListener(ILocalSiteChangedListener listener);
+
+	/**
+	 * Save the specified configuration. Saved configurations are 
+	 * not deleted based on the history count. They must be explicitly
+	 * removed.
+	 * 
+	 * @param configuration the configuration to save
+	 * @return the preserved configuration or <code>null</code> if the configuration to save is <code>null</code>
+	 * @exception CoreException
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IInstallConfiguration addToPreservedConfigurations(IInstallConfiguration configuration)
+		throws CoreException;
+
+	/**
+	 * Removes the specified configuration from the list of previously
+	 * saved configurations.
+	 * 
+	 * @param configuration the configuration to remove
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void removeFromPreservedConfigurations(IInstallConfiguration configuration);
+
+	/**
+	 * Return the list of saved configurations
+	 * 
+	 * @return an array of configurations, or an empty array.
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IInstallConfiguration[] getPreservedConfigurations();
+	
+	/**
+	 * Indicates if the 'state' of the specified feature and its children features.
+	 * 
+	 * A feature is considered to be 'unhappy' in the context of this site,
+	 * if some of the plug-ins referenced by the feature, or any of its children,
+	 *  are not installed on this site.
+	 * 
+	 * A feature is considered to be 'happy' in the context of a local site
+	 * if all the plug-ins referenced by the feature, or any of its children,
+	 * are installed on the site and no other version of any of the plug-ins
+	 * are installed on any other site of the local site.
+	 * 
+	 * A feature is considered to be 'ambiguous' in the context of a local site
+	 * if all the plug-ins referenced by the feature, or any of its children,
+	 * are installed on the site and other version of any of the plug-ins
+	 * are installed on any other site of the local site.
+	 * 
+	 * @param feature the feature
+	 * @see IFeature#STATUS_HAPPY
+	 * @see IFeature#STATUS_UNHAPPY
+	 * @see IFeature#STATUS_AMBIGUOUS
+	 * @return the state of the feature
+	 * @exception CoreException
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 IStatus getFeatureStatus(IFeature feature) throws CoreException ;	
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSiteChangedListener.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSiteChangedListener.java
new file mode 100644
index 0000000..98cc3ea
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSiteChangedListener.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+/**
+ * Local site change listener. 
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ILocalSiteChangedListener {
+	
+	/**
+	 * Indicates the current configuration has changed.
+	 * 
+	 * @param configuration the current cunfiguration
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void currentInstallConfigurationChanged(IInstallConfiguration configuration);
+	
+	/**
+	 * Indicates the specified configuration was removed.
+	 * 
+	 * @param configuration the configuration
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void installConfigurationRemoved(IInstallConfiguration configuration);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSystemInfoListener.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSystemInfoListener.java
new file mode 100644
index 0000000..a2f3987
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ILocalSystemInfoListener.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+ 
+
+
+/**
+ * Local system change listener interface.
+ * <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>
+ * @see LocalSystemInfo#addInfoListener(ILocalSystemInfoListener)
+ * @see LocalSystemInfo#removeInfoListener(ILocalSystemInfoListener)
+ * @see LocalSystemInfo#fireSystemInfoChanged(IVolume,int)
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ILocalSystemInfoListener {
+	
+	/**
+	 * Volume change notification.
+	 * Called each time there are relevant volume changes
+	 * detected. This specifically includes changes to the
+	 * file system structure as a result of removable drive/ media
+	 * operations (eg. CD insertion), and changes to volume 
+	 * mount structure.
+	 * @param volume volume of the changed file
+	 * system structure. Any current paths beyond
+	 * the specified 'root' file of the volume are assumed to be invalidated.
+	 * @param changeType type of the change that occured.
+	 * @see LocalSystemInfo#VOLUME_ADDED
+	 * @see LocalSystemInfo#VOLUME_REMOVED
+	 * @see LocalSystemInfo#VOLUME_CHANGED
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 void systemInfoChanged(IVolume volume, int changeType);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IProblemHandler.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IProblemHandler.java
new file mode 100644
index 0000000..037d672
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IProblemHandler.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+ 
+/**
+ * Generic problem handler. Used to report status from specific
+ * install operations. The methods implemented by this interface
+ * are callbacks from the update support to the caller of the update
+ * methods.
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IProblemHandler {
+
+	/**
+	 * Report problem.
+	 * 
+	 * @param problemText problem text
+	 * @return <code>true</code> if the operation should continue,
+	 * <code>false</code> if the operation should be cancelled
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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>
+	 */
+	boolean reportProblem(String problemText);
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ISessionDelta.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ISessionDelta.java
new file mode 100644
index 0000000..1d843a5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/ISessionDelta.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.update.configuration;
+
+import java.util.Date;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+
+/**
+ * Installation Change.
+ * Represents the changes the reconciler found.
+ * <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>
+ * @since 2.0
+ * @deprecated Do not use this interface
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ISessionDelta extends IAdaptable {
+
+	/**
+	 * Indicates a processing type to enable the features
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 int ENABLE = 1;
+
+	/**
+	 * Indicates a processing type to disable the features
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 int DISABLE = 2;
+
+	/**
+	 * Returns the list of Features found during reconciliation
+	 * 
+	 * @return an array of feature references, or an empty array
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 IFeatureReference[] getFeatureReferences();
+
+	/**
+	 * Returns the date the reconciliation occured
+	 * 
+	 * @return the date of the reconciliation
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 Date getDate();
+
+	/**
+	 * Returns the type of the processing type
+	 * that will affect all the associated features. 
+	 * 
+	 * @return the processing type
+	 * @see ISessionDelta#ENABLE
+	 * @see ISessionDelta#DISABLE
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 int getType();
+
+	/**
+	 * Process all the feature references of the 
+	 * Session Delta. 
+	 * Removes the Session Delta from the file system after processing it.
+	 * 
+	 * @param progressMonitor the progress monitor
+	 * @throws CoreException if an error occurs. 
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void process(IProgressMonitor progressMonitor) throws CoreException;
+
+	/**
+	 * Process the selected feature references of the Session Delta.
+	 * Removes the Session Delta from the file system after processing it.
+	 *
+	 * @param selected list of selected feature references to be processed
+	 * @param monitor the progress monitor
+	 * @throws CoreException if an error occurs.
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 void process(IFeatureReference [] selected, IProgressMonitor monitor) throws CoreException;
+	
+	/**
+	 * Removes the Session Delta from the file system without processing it.
+	 * 
+	 * @since 2.0 
+	 * <p>
+	 * <b>Note:</b> This method 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 void delete();	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IVolume.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IVolume.java
new file mode 100644
index 0000000..c120efc
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/IVolume.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Local Volume Info.
+ * Represents local file system information for a specific volume.
+ * <p>
+ * This interface is not intended to 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>
+ * @see org.eclipse.update.configuration.LocalSystemInfo
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IVolume extends IAdaptable {
+
+	/**
+	 * Returns the available free space on this volume.
+	 * Returns the amount of free space available to this
+	 * user on the volume. The
+	 * method takes into account any space quotas or other
+	 * native mechanisms that may restrict space usage
+	 * on a given volume.
+	 * @return the amount of free space available (in units
+	 * of Kbyte), or an indication the size is not known 
+	 * @see LocalSystemInfo#SIZE_UNKNOWN
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 long getFreeSpace();
+	
+	/**
+	 * returns volume label.
+	 * Returns the label of the volume.
+	 * @return volume label (as string), or <code>null</code> if
+	 * the label cannot be determined.
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 String getLabel();
+	
+	/**
+	 * Returns volume type.
+	 * Returns the type of the volume.
+	 * @return volume type
+	 * @see LocalSystemInfo#VOLUME_UNKNOWN
+	 * @see LocalSystemInfo#VOLUME_INVALID_PATH
+	 * @see LocalSystemInfo#VOLUME_REMOVABLE
+	 * @see LocalSystemInfo#VOLUME_FIXED
+	 * @see LocalSystemInfo#VOLUME_REMOTE
+	 * @see LocalSystemInfo#VOLUME_CDROM
+	 * @see LocalSystemInfo#VOLUME_FLOPPY_3
+	 * @see LocalSystemInfo#VOLUME_FLOPPY_5
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 int getType();
+	
+	/**
+	 * Returns the volume path.
+	 * Returns the path that represents the mount point of the volume.
+	 * @return mount point file
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 File getFile();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/LocalSystemInfo.java b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/LocalSystemInfo.java
new file mode 100644
index 0000000..038cb4f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/LocalSystemInfo.java
@@ -0,0 +1,426 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.configuration;
+ 
+import java.io.File;
+import java.util.ArrayList;
+
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.Volume;
+ 
+/**
+ * Utility class providing local file system information.
+ * The class attempts to load a native library implementation
+ * of its methods. If successful, the method calls are delegated
+ * to the native implementation. Otherwise a default non-native
+ * implementation is used.  * 
+ * <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>
+ * @see ILocalSystemInfoListener
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class LocalSystemInfo {
+	
+	/**
+	 * Indicates the amount of available free space is not known
+	 * 
+	 * @see LocalSystemInfo#getFreeSpace(File)
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final long SIZE_UNKNOWN = -1;
+	
+	/**
+	 * Indicates the volume type is not known
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_UNKNOWN = -1;
+	
+	/**
+	 * Indicates the volume could not be determined from path
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_INVALID_PATH = -2;
+	
+	/**
+	 * Indicates the volume is removable (other than floppy disk)
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_REMOVABLE = 1;
+	
+	/**
+	 * Indicates the volume is fixed (hard drive)
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_FIXED = 2;
+	
+	/**
+	 * Indicates a remote (network) volume
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_REMOTE = 3;
+	
+	/**
+	 * Indicates a cdrom volume (compact disc)
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_CDROM = 4;	
+	
+	/**
+	 * Indicates a ramdisk volume (memory)
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_RAMDISK = 5;	
+
+	/**
+	 * Indicates the volume is removable (floppy disk 5 1/4)
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_FLOPPY_5 = 6;	
+	
+	/**
+	 * Indicates the volume is removable (floppy disk 3 1/2)
+	 * 
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_FLOPPY_3 = 7;
+	
+	/**
+	 * Indicates a new volume has been added
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_ADDED = 0;
+			
+	/**
+	 * Indicates a volume has been removed
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_REMOVED = 1;
+
+	/**
+	 * Indicates a volume has been changed
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This field 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 static final int VOLUME_CHANGED = 2;
+
+	
+	private static ArrayList listeners = new ArrayList();	
+	private static boolean hasNatives = false;	
+	static {
+		try {
+			System.loadLibrary("update"); //$NON-NLS-1$
+			hasNatives = true;
+		} catch (UnsatisfiedLinkError e) {
+			UpdateCore.warn("Unable to load native library 'update'."); //$NON-NLS-1$
+			hasNatives = false;
+		}
+	}
+	
+	/**
+	 * Determines available free space on a volume.
+	 * Returns the amount of free space available to this
+	 * user on the volume containing the specified path. The
+	 * method takes into account any space quotas or other
+	 * native mechanisms that may restrict space usage
+	 * on a given volume.
+	 * @param path file path. May contain path elements beyond
+	 * the volume "root"
+	 * @return the amount of free space available (in units
+	 * of Kbyte), or an indication the size is not known 
+	 * @see LocalSystemInfo#SIZE_UNKNOWN
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 static long getFreeSpace(File path) {
+		if (hasNatives) {
+			try {
+				long bytes = nativeGetFreeSpace(path);
+				return (bytes!=0)?bytes/1024:0;
+			} catch (UnsatisfiedLinkError e) {
+			}
+		}
+		return SIZE_UNKNOWN;
+	}
+
+
+	/**
+	 * Lists the file system volume.
+	 * @return array of volume representing mount
+	 * points, or <code>null</code> if none found
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 static IVolume[] getVolumes() {
+		String[] mountPoints = listMountPoints();
+		Volume[] vol = new Volume[0];
+		if (mountPoints!=null){
+			vol = new Volume[mountPoints.length];
+			for (int i = 0; i < mountPoints.length; i++) {
+				File root = new File(mountPoints[i]);
+				String label = getLabel(root);
+				int type = getType(root);
+				long size = getFreeSpace(root);
+				vol[i] = new Volume(root,label,type,size);
+				vol[i].markReadOnly();
+			}
+		} else {
+			UpdateCore.warn("Unable to find mount points"); //$NON-NLS-1$
+			// fallback
+			File [] roots = File.listRoots();
+			if (roots.length == 1) {
+				// just one root - skip it
+				File root = roots[0];
+				roots = root.listFiles();
+			}
+			vol = new Volume[roots.length];			
+			for (int i = 0; i < roots.length; i++) {
+				vol[i] = new Volume(roots[i],null,LocalSystemInfo.VOLUME_UNKNOWN,LocalSystemInfo.SIZE_UNKNOWN);
+				vol[i].markReadOnly();
+			}			
+		}
+		return vol;
+	}
+
+	
+	/**
+	 * Add local system change listener.
+	 * Allows a listener to be added for monitoring changes
+	 * in the local system information. The listener is notified
+	 * each time there are relevant volume changes
+	 * detected. This specifically includes changes to the
+	 * list of volumes as a result of removable drive/ media
+	 * operations (eg. CD insertion, removal), and changes to volume 
+	 * mount structure.
+	 * @param listener change listener
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 static void addInfoListener(ILocalSystemInfoListener listener) {
+		if (!listeners.contains(listener))
+			listeners.add(listener);
+	}
+	
+	/**
+	 * Remove local system change listener
+	 * @param listener change listener
+	 * @since 2.0
+	 * <p>
+	 * <b>Note:</b> This method 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 static void removeInfoListener(ILocalSystemInfoListener listener) {
+		listeners.remove(listener);
+	}
+		
+	/**
+	 * Notify listeners of change.
+	 * 
+	 * @param volume the volume representing the
+	 * change file system structure. Any current paths beyond
+	 * the specified "root" file of the volume are assumed to be invalidated.
+	 * @param changeType type of the change that occured.
+	 * @see LocalSystemInfo#VOLUME_ADDED
+	 * @see LocalSystemInfo#VOLUME_REMOVED
+	 * @see LocalSystemInfo#VOLUME_CHANGED
+	 * <p>
+	 * <b>Note:</b> This method 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 static void fireSystemInfoChanged(IVolume volume, int changeType) {
+		for (int i=0; i< listeners.size(); i++) {
+			((ILocalSystemInfoListener)listeners.get(i)).systemInfoChanged(volume,changeType);
+		}
+	}
+		
+	/*
+	 * Determines volume label.
+	 * Returns the label of the volume containing the specified
+	 * path.
+	 * @param path file path. May contain path elements beyond
+	 * the volume "root"
+	 * @return volume label (as string), or <code>null</code> if
+	 * the label cannot be determined.
+	 * @since 2.0
+	 */
+	private static String getLabel(File path) {
+		if (hasNatives) {
+			try {
+				return nativeGetLabel(path);
+			} catch (UnsatisfiedLinkError e) {
+			}
+		}
+		return null;
+	}
+	
+	/*
+	 * Determines volume type.
+	 * Returns the type of the volume containing the specified
+	 * path.
+	 * @param path file path. May contain path elements beyond
+	 * the volume "root"
+	 * @return volume type
+	 * @see LocalSystemInfo#VOLUME_UNKNOWN
+	 * @see LocalSystemInfo#VOLUME_INVALID_PATH
+	 * @see LocalSystemInfo#VOLUME_REMOVABLE
+	 * @see LocalSystemInfo#VOLUME_FIXED
+	 * @see LocalSystemInfo#VOLUME_REMOTE
+	 * @see LocalSystemInfo#VOLUME_CDROM
+	 * @see LocalSystemInfo#VOLUME_FLOPPY_3
+	 * @see LocalSystemInfo#VOLUME_FLOPPY_5
+	 * @since 2.0
+	 */
+	private static int getType(File path) {
+		if (hasNatives) {
+			try {
+				return nativeGetType(path);
+			} catch (UnsatisfiedLinkError e) {
+			}
+		}
+		return VOLUME_UNKNOWN;
+	}
+	
+	/*
+	 * Lists the file system mount points.
+	 * @return array of absolute file paths representing mount
+	 * points, or <code>null</code> if none found
+	 * @since 2.0
+	 */
+	private static String[] listMountPoints() {
+		if (hasNatives) {
+			try {
+				String[] mountPoints = nativeListMountPoints();
+				return mountPoints;
+			} catch (UnsatisfiedLinkError e) {
+			}
+		}
+		return null;
+	}
+		
+	/*
+	 * Native implementations.
+	 */
+	private static native long nativeGetFreeSpace(File path);
+	private static native String nativeGetLabel(File path);
+	private static native int nativeGetType(File path);
+	private static native String[] nativeListMountPoints();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/configuration/package.html b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/package.html
new file mode 100644
index 0000000..a35e812
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/configuration/package.html
@@ -0,0 +1,23 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.72 [en] (Windows NT 5.0; U) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides support for accessing local installation
+information.
+<h2>
+Package Specification</h2>
+This package contains interfaces for accessing and manipulating the local
+system installation and configuration information. In general, users extending
+the update support&nbsp; by writing additional feature and site implementation
+do not need to make use of interfaces defined in this package.
+<p>
+<b>Note:</b> This package has been deprecated and will be deleted in a future
+release.  See bug 311590 for details.
+</p>
+</body>
+</html>
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ArchiveReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ArchiveReference.java
new file mode 100644
index 0000000..0ee0248
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ArchiveReference.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.update.core.model.ArchiveReferenceModel;
+
+/**
+ * Convenience implementation of a site archive.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.IArchiveReference
+ * @see org.eclipse.update.core.model.ArchiveReferenceModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class ArchiveReference
+	extends ArchiveReferenceModel
+	implements IArchiveReference {
+
+	/**
+	 * Constructor for ArchiveReference
+	 * @since 2.0
+	 */
+	public ArchiveReference() {
+		super();
+	}
+
+	/**
+	 * @see Object#toString()
+	 * @since 2.0
+	 */
+	public String toString() {
+		String result = "IArchiveReference: "; //$NON-NLS-1$
+		result =
+			result
+				+ ((getPath() == null)
+					? getURL().toExternalForm()
+					: getPath() + " : " + getURL().toExternalForm()); //$NON-NLS-1$
+		return result;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseFeatureFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseFeatureFactory.java
new file mode 100644
index 0000000..79fe2df
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseFeatureFactory.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.core.model.FeatureModel;
+import org.eclipse.update.core.model.FeatureModelFactory;
+import org.eclipse.update.core.model.ImportModel;
+import org.eclipse.update.core.model.IncludedFeatureReferenceModel;
+import org.eclipse.update.core.model.InstallHandlerEntryModel;
+import org.eclipse.update.core.model.NonPluginEntryModel;
+import org.eclipse.update.core.model.PluginEntryModel;
+import org.eclipse.update.core.model.URLEntryModel;
+
+/**
+ * Base implementation of a feature factory.
+ * The factory is responsible for constructing the correct
+ * concrete implementation of the model objects for each particular
+ * feature type. This class creates model objects that correspond
+ * to the concrete implementation classes provided in this package.
+ * The actual feature creation method is subclass responsibility.
+ * <p>
+ * This class must be subclassed 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>
+ * @see org.eclipse.update.core.IFeatureFactory
+ * @see org.eclipse.update.core.model.FeatureModelFactory
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class BaseFeatureFactory extends FeatureModelFactory implements IFeatureFactory {
+
+	/**
+	 * 
+	 * @deprecated implement createFeature(URL, ISite, IProgressMonitor) instead
+	 * @see IFeatureFactory#createFeature(URL,ISite)
+	 * @since 2.0
+	 */
+	public IFeature createFeature(URL url, ISite site) throws CoreException {
+		return createFeature(url, site, null);
+	}
+
+	/**
+	 * Create feature. Implementation of this method must be provided by 
+	 * subclass
+	 * 
+	 * @see IFeatureFactory#createFeature(URL,ISite,IProgressMonitor)
+	 * @since 2.0
+	 */
+	public abstract IFeature createFeature(URL url, ISite site, IProgressMonitor monitor) throws CoreException;
+
+	/**
+	 * Create a concrete implementation of feature model.
+	 * 
+	 * @see Feature
+	 * @return feature model
+	 * @since 2.0
+	 */
+	public FeatureModel createFeatureModel() {
+		return new Feature();
+	}
+
+	/**
+	 * Create a concrete implementation of included feature reference model.
+	 * 
+	 * @see IncludedFeatureReference
+	 * @return feature model
+	 * @since 2.1
+	 */
+	public IncludedFeatureReferenceModel createIncludedFeatureReferenceModel() {
+		return new IncludedFeatureReference();
+	}
+
+	/**
+	 * Create a concrete implementation of install handler model.
+	 * 
+	 * @see InstallHandlerEntry
+	 * @return install handler entry model
+	 * @since 2.0
+	 */
+	public InstallHandlerEntryModel createInstallHandlerEntryModel() {
+		return new InstallHandlerEntry();
+	}
+
+	/**
+	 * Create a concrete implementation of import dependency model.
+	 * 
+	 * @see Import
+	 * @return import dependency model
+	 * @since 2.0
+	 */
+	public ImportModel createImportModel() {
+		return new Import();
+	}
+
+	/**
+	 * Create a concrete implementation of plug-in entry model.
+	 * 
+	 * @see PluginEntry
+	 * @return plug-in entry model
+	 * @since 2.0
+	 */
+	public PluginEntryModel createPluginEntryModel() {
+		return new PluginEntry();
+	}
+
+	/**
+	 * Create a concrete implementation of non-plug-in entry model.
+	 * 
+	 * @see NonPluginEntry
+	 * @return non-plug-in entry model
+	 * @since 2.0
+	 */
+	public NonPluginEntryModel createNonPluginEntryModel() {
+		return new NonPluginEntry();
+	}
+
+	/**
+	 * Create a concrete implementation of annotated URL model.
+	 * 
+	 * @see URLEntry
+	 * @return annotated URL model
+	 * @since 2.0
+	 */
+	public URLEntryModel createURLEntryModel() {
+		return new URLEntry();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseInstallHandler.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseInstallHandler.java
new file mode 100644
index 0000000..a3f5f11
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseInstallHandler.java
@@ -0,0 +1,252 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Base implementation of an install handler.
+ * This is a convenience implementation of an install handler with
+ * null implementation of its methods. It allows subclasses to selectively
+ * implement only the methods required for their installation tasks.
+ * <p>
+ * This class should be subclassed 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> 
+ * @see org.eclipse.update.core.IInstallHandler
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class BaseInstallHandler implements IInstallHandler {
+
+	/**
+	 * Update action type
+	 * 
+	 * @see IInstallHandler#HANDLER_ACTION_INSTALL
+	 * @see IInstallHandler#HANDLER_ACTION_CONFIGURE
+	 * @see IInstallHandler#HANDLER_ACTION_UNCONFIGURE
+	 * @see IInstallHandler#HANDLER_ACTION_UNINSTALL
+	 * @since 2.0
+	 */
+	protected int type;
+
+	/**
+	 * The target of the action
+	 * @since 2.0
+	 */
+	protected IFeature feature;
+
+	/**
+	 * Model entry that defines this handler
+	 * 
+	 * @since 2.0
+	 */
+	protected IInstallHandlerEntry entry;
+
+	/** 
+	 * Optional progress monitor, can be <code>null</code>
+	 * 
+	 * @since 2.0
+	 */
+	protected InstallMonitor monitor;
+
+	/**
+	 * Plug-in entries downloaded
+	 * 
+	 * @see IInstallHandler#HANDLER_ACTION_INSTALL
+	 * @since 2.0
+	 */
+	protected IPluginEntry[] pluginEntries;
+
+	/**
+	 * Non-plug-in entries downloaded
+	 * 
+	 * @see IInstallHandler#HANDLER_ACTION_INSTALL
+	 * @since 2.0
+	 */
+	protected INonPluginEntry[] nonPluginEntries;
+
+	/**
+	 * Indicates if handler has been initialized
+	 * 
+	 * @since 2.0
+	 */
+	protected boolean initialized = false;
+
+	/**
+	 * Initialize the install handler.
+	 * 
+	 * @see IInstallHandler#initialize(int, IFeature, IInstallHandlerEntry, InstallMonitor)
+	 * @since 2.0
+	 */
+	public void initialize(
+		int type,
+		IFeature feature,
+		IInstallHandlerEntry entry,
+		InstallMonitor monitor)
+		throws CoreException {
+
+		if (this.initialized)
+			return;
+		else {
+			if (feature == null)
+				throw new IllegalArgumentException();
+			this.type = type;
+			this.feature = feature;
+			this.entry = entry;
+			this.monitor = monitor;
+			this.initialized = true;
+		}
+	}
+
+	/**
+	 * Called at the start of the install action.
+	 * 
+	 * @see IInstallHandler#installInitiated
+	 * @since 2.0
+	 */
+	public void installInitiated() throws CoreException {
+	}
+
+	/**
+	 * Called after files corresponding to plug-in entries have been downloaded,
+	 * but before they are actually unpacked and installed.
+	 * 
+	 * @see IInstallHandler#pluginsDownloaded(IPluginEntry[])
+	 * @since 2.0
+	 */
+	public void pluginsDownloaded(IPluginEntry[] plugins) throws CoreException {
+
+		this.pluginEntries = plugins;
+	}
+
+	/**
+	 * Called after files corresponding to non-plug-in entries have been 
+	 * downloaded.
+	 * 
+	 * @see IInstallHandler#nonPluginDataDownloaded(INonPluginEntry[], IVerificationListener)
+	 * @since 2.0
+	 */
+	public void nonPluginDataDownloaded(
+		INonPluginEntry[] nonPluginData,
+		IVerificationListener listener)
+		throws CoreException {
+
+		this.nonPluginEntries = nonPluginData;
+	}
+
+	/**
+	 * Called after the feature files and any downloaded plug-ins have
+	 * been installed. 
+	 * 
+	 * @see IInstallHandler#completeInstall(IFeatureContentConsumer)
+	 * @since 2.0
+	 */
+	public void completeInstall(IFeatureContentConsumer consumer)
+		throws CoreException {
+	}
+
+	/**
+	 * Called at the end of the install action.
+	 * 
+	 * @see IInstallHandler#installCompleted(boolean)
+	 * @since 2.0
+	 */
+	public void installCompleted(boolean success) throws CoreException {
+	}
+
+	/**
+	 * Called at the start of the configure action.
+	 * 
+	 * @see IInstallHandler#configureInitiated()
+	 * @since 2.0
+	 */
+	public void configureInitiated() throws CoreException {
+	}
+
+	/**
+	 * Called after the feature has been configured.
+	 * 
+	 * @see IInstallHandler#completeConfigure()
+	 * @since 2.0
+	 */
+	public void completeConfigure() throws CoreException {
+	}
+
+	/**
+	 * Called at the end of the configure action.
+	 * 
+	 * @see IInstallHandler#configureCompleted(boolean)
+	 * @since 2.0
+	 */
+	public void configureCompleted(boolean success) throws CoreException {
+	}
+
+	/**
+	 * Called at the start of the unconfigure action.
+	 * 
+	 * @see IInstallHandler#unconfigureInitiated()
+	 * @since 2.0
+	 */
+	public void unconfigureInitiated() throws CoreException {
+	}
+
+	/**
+	 * Called after the feature has been unconfigured.
+	 * 
+	 * @see IInstallHandler#completeUnconfigure()
+	 * @since 2.0
+	 */
+	public void completeUnconfigure() throws CoreException {
+	}
+
+	/**
+	 * Called at the end of the unconfigure action.
+	 * 
+	 * @see IInstallHandler#unconfigureCompleted(boolean)
+	 * @since 2.0
+	 */
+	public void unconfigureCompleted(boolean success) throws CoreException {
+	}
+
+	/**
+	 * Called at the start of the uninstall action.
+	 * 
+	 * @see IInstallHandler#uninstallInitiated()
+	 * @since 2.0
+	 */
+	public void uninstallInitiated() throws CoreException {
+	}
+
+	/**
+	 * Called after the feature has been uninstalled.
+	 * 
+	 * @see IInstallHandler#completeUninstall()
+	 * @since 2.0
+	 */
+	public void completeUninstall() throws CoreException {
+	}
+
+	/**
+	 * Called at the end of the uninstall action.
+	 * 
+	 * @see IInstallHandler#uninstallCompleted(boolean)
+	 * @since 2.0
+	 */
+	public void uninstallCompleted(boolean success) throws CoreException {
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseSiteFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseSiteFactory.java
new file mode 100644
index 0000000..650bd1d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/BaseSiteFactory.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+ 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.model.ArchiveReferenceModel;
+import org.eclipse.update.core.model.CategoryModel;
+import org.eclipse.update.core.model.InvalidSiteTypeException;
+import org.eclipse.update.core.model.SiteModel;
+import org.eclipse.update.core.model.SiteModelFactory;
+import org.eclipse.update.core.model.URLEntryModel;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+import org.eclipse.update.internal.core.connection.IResponse;
+
+/**
+ * Base implementation of a site factory.
+ * The factory is responsible for constructing the correct
+ * concrete implementation of the model objects for each particular
+ * site type. This class creates model objects that correspond
+ * to the concrete implementation classes provided in this package.
+ * The actual site creation method is subclass responsibility.
+ * <p>
+ * This class must be subclassed 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>
+ * @see org.eclipse.update.core.ISiteFactory
+ * @see org.eclipse.update.core.model.SiteModelFactory
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class BaseSiteFactory extends SiteModelFactory implements ISiteFactory {
+
+
+	/**
+	 * Create site. Implementation of this method must be provided by 
+	 * subclass
+	 * 
+	 * @see ISiteFactory#createSite(URL)
+	 * @since 2.0
+	 */
+	public abstract ISite createSite(URL url) throws CoreException, InvalidSiteTypeException;
+
+	/**
+	 * Helper method to access resouce bundle for site. The default 
+	 * implementation attempts to load the appropriately localized 
+	 * site.properties file.
+	 * 
+	 * @param url base URL used to load the resource bundle.
+	 * @return resource bundle, or <code>null</code>.
+	 * @since 2.0
+	 */
+	protected ResourceBundle getResourceBundle(URL url) {
+		ResourceBundle bundle = null;
+
+		try {
+			url = UpdateManagerUtils.asDirectoryURL(url);
+			ClassLoader l = new URLClassLoader(new URL[] { url }, null);
+			bundle = ResourceBundle.getBundle(Site.SITE_FILE, Locale.getDefault(), l);
+		} catch (MissingResourceException e) {
+			UpdateCore.warn(e.getLocalizedMessage() + ":" + url.toExternalForm()); //$NON-NLS-1$
+		} catch (MalformedURLException e) {
+			UpdateCore.warn(NLS.bind(Messages.BaseSiteFactory_CannotRetriveParentDirectory, (new String[] { url.toExternalForm() })));
+		}
+
+		return bundle;
+	}
+
+	/**
+	 * Create a concrete implementation of site model.
+	 * 
+	 * @see Site
+	 * @return site model
+	 * @since 2.0
+	 */
+	public SiteModel createSiteMapModel() {
+		return new Site();
+	}
+
+
+	/**
+	 * Create a concrete implementation of feature reference model.
+	 * 
+	 * @see FeatureReference
+	 * @return feature reference model
+	 * @since 2.0
+	 */
+	public SiteFeatureReferenceModel createFeatureReferenceModel() {
+		return new SiteFeatureReference();
+	}
+
+	/**
+	 * Create a concrete implementation of archive reference model.
+	 * 
+	 * @see ArchiveReference
+	 * @return archive reference model
+	 * @since 2.0
+	 */
+	public ArchiveReferenceModel createArchiveReferenceModel() {
+		return new ArchiveReference();
+	}
+
+
+	/**
+	 * Create a concrete implementation of annotated URL model.
+	 * 
+	 * @see URLEntry
+	 * @return annotated URL model
+	 * @since 2.0
+	 */
+	public URLEntryModel createURLEntryModel() {
+		return new URLEntry();
+	}
+
+
+	/**
+	 * Create a concrete implementation of category model.
+	 * 
+	 * @see Category
+	 * @return category model
+	 * @since 2.0
+	 */
+	public CategoryModel createSiteCategoryModel() {
+		return new Category();
+	}
+
+	/**
+	 * Open a stream on a URL.
+	 * manages a time out if the connection is locked or fails
+	 * 
+	 * @param resolvedURL
+	 * @return InputStream
+	 */
+	protected InputStream openStream(URL resolvedURL)  throws IOException {
+		IResponse response = ConnectionFactory.get(resolvedURL);
+		return response.getInputStream();
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/Category.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/Category.java
new file mode 100644
index 0000000..73d1d56
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/Category.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.update.core.model.CategoryModel;
+
+/**
+ * Convenience implementation of feature category definition.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.ICategory
+ * @see org.eclipse.update.core.model.CategoryModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class Category extends CategoryModel implements ICategory {
+
+	/**
+	 * Default Constructor
+	 */
+	public Category() {
+	}
+
+	/**
+	 * Constructor
+	 */
+	public Category(String name, String label) {
+		setName(name);
+		setLabel(label);
+	}
+
+	/**
+	 * Retrieve the detailed category description
+	 * @see ICategory#getDescription()
+	 */
+	public IURLEntry getDescription() {
+		return (IURLEntry) getDescriptionModel();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ContentReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ContentReference.java
new file mode 100644
index 0000000..632e409
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ContentReference.java
@@ -0,0 +1,340 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.internal.core.FatalIOException;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.URLEncoder;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+import org.eclipse.update.internal.core.connection.HttpResponse;
+import org.eclipse.update.internal.core.connection.IResponse;
+
+/**
+ * Content reference implements a general access wrapper 
+ * to feature and site content. The reference specifies
+ * a "symbolic" path identifier for the content, and the actual
+ * reference as a file, or a URL.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.JarContentReference
+ * @see org.eclipse.update.core.JarEntryContentReference
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class ContentReference {
+
+	/**
+	 * Unknown size indication
+	 * @since 2.0
+	 */
+	public static final long UNKNOWN_SIZE = -1;
+
+	/**
+	 * Default executable permission when installing a content reference
+	 * Will add executable bit if necessary
+	 * 
+	 * @since 2.0.1
+	 */
+	public static final int DEFAULT_EXECUTABLE_PERMISSION = -1;
+
+	private static final String FILE_URL_PROTOCOL = "file"; //$NON-NLS-1$
+
+	private String id;
+	private URL url; // reference is either URL reference *OR*
+	private File file; //    local file reference
+	private IResponse response;
+	private int permission; 
+	private long length;
+	
+	// <true> if a copy of a Contentreferenec in a temp local directory
+	private boolean tempLocal = false;
+	
+	private long lastModified;
+
+	/**
+	 * Create content reference from URL.
+	 * 
+	 * @param id "symbolic" path identifier
+	 * @param url actual referenced URL
+	 * @since 2.0
+	 */
+	public ContentReference(String id, URL url) {
+		this.id = (id == null ? "" : id); //$NON-NLS-1$
+		this.url = url; // can be null
+		this.file = null;
+	}
+
+	/**
+	 * Create content reference from file.
+	 * 
+	 * @param id "symbolic" path identifier
+	 * @param file actual referenced file
+	 * @since 2.0
+	 */
+	public ContentReference(String id, File file) {
+		this.id = (id == null ? "" : id); //$NON-NLS-1$
+		this.file = file; // can be null
+		this.url = null;
+	}
+
+	/**
+	 * A factory method to create a content reference of
+	 * the same type.
+	 * 
+	 * @param id "symbolic" path identifier
+	 * @param file actual referenced file
+	 * @return content reference of the same type
+	 * @since 2.0
+	 */
+	public ContentReference createContentReference(String id, File file) {
+		return new ContentReference(id, file,true);
+	}
+	/**
+	 * 
+	 */
+	private ContentReference(String id, File file, boolean b) {
+		this(id,file);
+		setTempLocal(b);
+	}
+
+	/**
+	 * Retrieves the "symbolic" path identifier for the reference.
+	 * 
+	 * @return "symbolic" path identifier
+	 * @since 2.0
+	 */
+	public String getIdentifier() {
+		return id;
+	}
+
+	/**
+	 * Creates an input stream for the reference.
+	 * 
+	 * @return input stream
+	 * @exception IOException unable to create stream
+	 * @since 2.0
+	 */
+	public InputStream getInputStream() throws IOException {
+		if (file != null)
+			return new FileInputStream(file);
+		else if (url != null) {
+			if (response == null) {
+				URL resolvedURL = URLEncoder.encode(url);
+				response = ConnectionFactory.get(resolvedURL);
+				UpdateManagerUtils.checkConnectionResult(response,resolvedURL);
+			}
+			InputStream is=response.getInputStream();
+			length=response.getContentLength();
+			return is;
+		} else
+			throw new FatalIOException(NLS.bind(Messages.ContentReference_UnableToCreateInputStream, (new String[] { this.toString() })));
+	}
+	/**
+	 * Creates an input stream for the reference.
+	 * 
+	 * @return input stream
+	 * @exception IOException unable to create stream
+	 * @since 2.0
+	 */
+	InputStream getPartialInputStream(long offset) throws IOException {
+		if (url != null && "http".equals(url.getProtocol())) { //$NON-NLS-1$
+			URL resolvedURL = URLEncoder.encode(url);
+			response = ConnectionFactory.get(resolvedURL);
+			if(response instanceof HttpResponse)
+				((HttpResponse)response).setOffset(offset);
+			UpdateManagerUtils.checkConnectionResult(response,resolvedURL);
+			InputStream is = response.getInputStream();
+			length=offset + response.getContentLength();
+			return is;
+		} else
+			throw new FatalIOException(NLS.bind(Messages.ContentReference_UnableToCreateInputStream, (new String[] { this.toString() })));
+	}
+	
+	/**
+	 * Returns the size of the referenced input, if it can be determined.
+	 * 
+	 * @return input size, or @see #UNKNOWN_SIZE if size cannot be determined.
+	 * @since 2.0
+	 */
+	public long getInputSize() throws IOException {
+		if (length>0)
+			return length;
+		if (file != null)
+			return file.length();
+		else if (url != null) {
+			if (response == null) {
+				URL resolvedURL = null;
+				try {
+					resolvedURL = URLEncoder.encode(url);
+					response = ConnectionFactory.get(resolvedURL);
+				} catch (IOException e) {
+					return ContentReference.UNKNOWN_SIZE;
+				}
+				UpdateManagerUtils.checkConnectionResult(response,resolvedURL);			
+			}
+			long size = response.getContentLength();
+			return size == -1 ? ContentReference.UNKNOWN_SIZE : size;
+		} else
+			return ContentReference.UNKNOWN_SIZE;
+	}
+
+	/**
+	 * Indicates whether the reference is a local file reference.
+	 * 
+	 * @return <code>true</code> if the reference is local, 
+	 * otherwise <code>false</code>
+	 * @since 2.0
+	 */
+	public boolean isLocalReference() {
+		/*if (file != null)
+			return true;
+		else if (url != null)
+			return FILE_URL_PROTOCOL.equals(url.getProtocol());
+		else
+			return false;*/
+		// only temp files are considered local
+		return tempLocal;
+	}
+
+	/**
+	 * Returns the content reference as a file. Note, that this method
+	 * <b>does not</b> cause the file to be downloaded if it
+	 * is not already local.
+	 * 
+	 * @return reference as file
+	 * @exception IOException reference cannot be returned as file
+	 * @since 2.0
+	 */
+	public File asFile() throws IOException {
+		if (file != null)
+			return file;
+
+		if (url != null && FILE_URL_PROTOCOL.equals(url.getProtocol())) {
+			File result = new File(url.getFile());
+			if (result.exists())
+				return result;
+			else 
+				throw new IOException(NLS.bind(Messages.ContentReference_FileDoesNotExist, (new String[] { this.toString() }))); 			
+		}
+
+		throw new IOException(NLS.bind(Messages.ContentReference_UnableToReturnReferenceAsFile, (new String[] { this.toString() }))); 
+	}
+
+	/**
+	 * Returns the content reference as a URL.
+	 * 
+	 * @return reference as URL
+	 * @exception IOException reference cannot be returned as URL
+	 * @since 2.0
+	 */
+	public URL asURL() throws IOException {
+		if (url != null)
+			return url;
+
+		if (file != null)
+			return file.toURL();
+
+		throw new FatalIOException(NLS.bind(Messages.ContentReference_UnableToReturnReferenceAsURL, (new String[] { this.toString() })));
+	}
+
+	/**
+	 * Return string representation of this reference.
+	 * 
+	 * @return string representation
+	 * @since 2.0
+	 */
+	public String toString() {
+		if (file != null)
+			return file.getAbsolutePath();
+		else
+			return url.toExternalForm();
+	}
+	/**
+	 * Returns the permission for this file.
+	 * 
+	 * @return the content reference permission
+	 * @see #DEFAULT_EXECUTABLE_PERMISSION
+	 * @since 2.0.1
+	 */
+	public int getPermission() {
+		return permission;
+	}
+
+	/**
+	 * Sets the permission of this content reference.
+	 * 
+	 * @param permission The permission to set
+	 */
+	public void setPermission(int permission) {
+		this.permission = permission;
+	}
+
+	/**
+	 * Sets if a content reference is considered local 
+	 * 
+	 * @param tempLocal <code>true</code> if the file is considered local
+	 */
+	protected void setTempLocal(boolean tempLocal) {
+		this.tempLocal = tempLocal;
+	}
+	
+	/**
+	 * Sets the timestamp the content was last modified.
+	 * @param timestamp
+	 * @since 3.0
+	 */
+	public void setLastModified(long timestamp) {
+		this.lastModified = timestamp;
+	}
+	
+	/**
+	 * Returns the timestamp when the content was last modified
+	 * @return the timestamp
+	 * @since 3.0
+	 */
+	public long getLastModified() {
+		if (lastModified == 0) {
+			if (file != null) 
+				lastModified = file.lastModified();
+			else if (url != null) {
+				if (response == null) {
+					try {
+						URL resolvedURL = URLEncoder.encode(url);
+						response = ConnectionFactory.get(resolvedURL);
+					} catch (MalformedURLException e) {
+						// return 0
+					} catch (IOException e) {
+						// return 0
+					}
+				}
+				lastModified = response.getLastModified();
+			} 
+		}
+		return lastModified;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/Feature.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/Feature.java
new file mode 100644
index 0000000..329ef57
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/Feature.java
@@ -0,0 +1,1163 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.core;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.model.ContentEntryModel;
+import org.eclipse.update.core.model.FeatureModel;
+import org.eclipse.update.core.model.FeatureReferenceModel;
+import org.eclipse.update.core.model.ImportModel;
+import org.eclipse.update.core.model.InstallAbortedException;
+import org.eclipse.update.core.model.NonPluginEntryModel;
+import org.eclipse.update.core.model.PluginEntryModel;
+import org.eclipse.update.core.model.URLEntryModel;
+import org.eclipse.update.internal.core.ErrorRecoveryLog;
+import org.eclipse.update.internal.core.InstallHandlerProxy;
+import org.eclipse.update.internal.core.InstallRegistry;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.TargetFeature;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.internal.core.UpdateSiteIncludedFeatureReference;
+
+/**
+ * Convenience implementation of a feature.
+ * <p>
+ * This class may be instantiated or subclassed 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> 
+ * @see org.eclipse.update.core.IFeature
+ * @see org.eclipse.update.core.model.FeatureModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class Feature extends FeatureModel implements IFeature {
+
+	/**
+	 * Simple file name of the default feature manifest file
+	 * @since 2.0
+	 */
+	public static final String FEATURE_FILE = "feature"; //$NON-NLS-1$
+
+	/**
+	 * File extension of the default feature manifest file
+	 * @since 2.0
+	 */
+	public static final String FEATURE_XML = FEATURE_FILE + ".xml"; //$NON-NLS-1$
+
+	private ISite site; // feature site
+	private IFeatureContentProvider featureContentProvider; // content provider
+	private List /*of IFeatureReference*/
+	includedFeatureReferences;
+
+	//PERF: new instance variable
+	private VersionedIdentifier versionId;
+
+	private InstallAbortedException abortedException = null;
+
+	/**
+	 * Feature default constructor
+	 * 
+	 * @since 2.0
+	 */
+	public Feature() {
+	}
+
+	/**
+	 * Compares two features for equality
+	 * 
+	 * @param object feature object to compare with
+	 * @return <code>true</code> if the two features are equal, 
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 */
+	public boolean equals(Object object) {
+		if (!(object instanceof IFeature))
+			return false;
+		IFeature f = (IFeature) object;
+		return getVersionedIdentifier().equals(f.getVersionedIdentifier());
+	}
+
+	/**
+	 * Returns the feature identifier.
+	 * 
+	 * @see IFeature#getVersionedIdentifier()
+	 * @since 2.0
+	 */
+	public VersionedIdentifier getVersionedIdentifier() {
+		if (versionId != null)
+			return versionId;
+
+		String id = getFeatureIdentifier();
+		String ver = getFeatureVersion();
+		if (id != null && ver != null) {
+			try {
+				versionId = new VersionedIdentifier(id, ver);
+				return versionId;
+			} catch (Exception e) {
+				UpdateCore.warn(
+					"Unable to create versioned identifier:" + id + ":" + ver); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
+		versionId = new VersionedIdentifier(getURL().toExternalForm(), null);
+		return versionId;
+	}
+
+	/**
+	 * Returns the site this feature is associated with.
+	 * 
+	 * @see IFeature#getSite()
+	 * @since 2.0
+	 */
+	public ISite getSite() {
+		return site;
+	}
+
+	/**
+	 * Returns the feature URL.
+	 * 
+	 * @see IFeature#getURL()
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		IFeatureContentProvider contentProvider = null;
+		try {
+			contentProvider = getFeatureContentProvider();
+		} catch (CoreException e) {
+			UpdateCore.warn("No content Provider", e); //$NON-NLS-1$
+		}
+		return (contentProvider != null) ? contentProvider.getURL() : null;
+	}
+
+	/**
+	 * Returns an information entry referencing the location of the
+	 * feature update site. 
+	 * 
+	 * @see IFeature#getUpdateSiteEntry()
+	 * @since 2.0
+	 */
+	public IURLEntry getUpdateSiteEntry() {
+		return (IURLEntry) getUpdateSiteEntryModel();
+	}
+
+	/**
+	 * Return an array of information entries referencing locations of other
+	 * update sites.
+	 * 
+	 * @see IFeature#getDiscoverySiteEntries()
+	 * @since 2.0
+	 */
+	public IURLEntry[] getDiscoverySiteEntries() {
+		URLEntryModel[] result = getDiscoverySiteEntryModels();
+		if (result.length == 0)
+			return new IURLEntry[0];
+		else
+			return (IURLEntry[]) result;
+	}
+
+	/**
+	 * Returns and optional custom install handler entry.
+	 * 
+	 * @see IFeature#getInstallHandlerEntry()
+	 * @since 2.0
+	 */
+	public IInstallHandlerEntry getInstallHandlerEntry() {
+		return (IInstallHandlerEntry) getInstallHandlerModel();
+	}
+
+	/**
+	 * Returns the feature description.
+	 * 
+	 * @see IFeature#getDescription()
+	 * @since 2.0
+	 */
+	public IURLEntry getDescription() {
+		return (IURLEntry) getDescriptionModel();
+	}
+
+	/**
+	 * Returns the copyright information for the feature.
+	 * 
+	 * @see IFeature#getCopyright()
+	 * @since 2.0
+	 */
+	public IURLEntry getCopyright() {
+		return (IURLEntry) getCopyrightModel();
+	}
+
+	/**
+	 * Returns the license information for the feature.
+	 * 
+	 * @see IFeature#getLicense()
+	 * @since 2.0
+	 */
+	public IURLEntry getLicense() {
+		return (IURLEntry) getLicenseModel();
+	}
+
+	/**
+	 * Return optional image for the feature.
+	 * 
+	 * @see IFeature#getImage()
+	 * @since 2.0
+	 */
+	public URL getImage() {
+		return getImageURL();
+	}
+
+	/**
+	 * Return a list of plug-in dependencies for this feature.
+	 * 
+	 * @see IFeature#getRawImports()
+	 * @since 2.0
+	 */
+	public IImport[] getRawImports() {
+		ImportModel[] result = getImportModels();
+		if (result.length == 0)
+			return new IImport[0];
+		else
+			return (IImport[]) result;
+	}
+
+	/**
+	 * Install the contents of this feature into the specified target feature.
+	 * This method is a reference implementation of the feature installation
+	 * protocol. Other concrete feature implementation that override this
+	 * method need to implement this protocol.
+	 * 
+	 * @see IFeature#install(IFeature, IVerificationListener, IProgressMonitor)
+	 * @since 2.0
+	 */
+	public IFeatureReference install(
+		IFeature targetFeature,
+		IVerificationListener verificationListener,
+		IProgressMonitor progress)
+		throws InstallAbortedException, CoreException {
+		// call other API with all optional features, or setup variable meaning install all
+		return install(targetFeature, null, verificationListener, progress);
+	}
+
+	/**
+	 * Install the contents of this feature into the specified target feature.
+	 * This method is a reference implementation of the feature installation
+	 * protocol. Other concrete feature implementation that override this
+	 * method need to implement this protocol.
+	 * 
+	 * @see IFeature#install(IFeature, IVerificationListener, IProgressMonitor)
+	 * @since 2.0
+	 */
+	public IFeatureReference install(
+		IFeature targetFeature,
+		IFeatureReference[] optionalfeatures,
+		IVerificationListener verificationListener,
+		IProgressMonitor progress)
+		throws InstallAbortedException, CoreException {
+
+		//DEBUG
+		debug("Installing...:" + getURL().toExternalForm()); //$NON-NLS-1$
+		ErrorRecoveryLog recoveryLog = ErrorRecoveryLog.getLog();
+
+		// make sure we have an InstallMonitor		
+		InstallMonitor monitor;
+		if (progress == null)
+			monitor = new InstallMonitor(new NullProgressMonitor());
+		else if (progress instanceof InstallMonitor)
+			monitor = (InstallMonitor) progress;
+		else
+			monitor = new InstallMonitor(progress);
+
+		// Setup optional install handler
+		InstallHandlerProxy handler =
+			new InstallHandlerProxy(
+				IInstallHandler.HANDLER_ACTION_INSTALL,
+				this,
+				this.getInstallHandlerEntry(),
+				monitor);
+		boolean success = false;
+		Throwable originalException = null;
+		abortedException = null;
+
+		// Get source feature provider and verifier.
+		// Initialize target variables.
+		IFeatureContentProvider provider = getFeatureContentProvider();
+		IVerifier verifier = provider.getVerifier();
+		IFeatureReference result = null;
+		IFeatureReference alreadyInstalledFeature = null;
+		IFeatureContentConsumer consumer = null;
+		IPluginEntry[] targetSitePluginEntries = null;
+		ArrayList justInstalledPlugins = new ArrayList();
+
+		try {
+			// determine list of plugins to install
+			// find the intersection between the plugin entries already contained
+			// on the target site, and plugin entries packaged in source feature
+			IPluginEntry[] sourceFeaturePluginEntries = getPluginEntries();
+			ISite targetSite = targetFeature.getSite();
+			if (targetSite == null) {
+				debug("The site to install in is null"); //$NON-NLS-1$
+				targetSitePluginEntries = new IPluginEntry[0];
+			} else {
+				targetSitePluginEntries = targetSite.getPluginEntries();
+			}
+			IPluginEntry[] pluginsToInstall =
+				UpdateManagerUtils.diff(
+					sourceFeaturePluginEntries,
+					targetSitePluginEntries);
+			INonPluginEntry[] nonPluginsToInstall = getNonPluginEntries();
+
+			IFeatureReference[] children = getIncludedFeatureReferences();
+			if (optionalfeatures != null) {
+				children =
+					UpdateManagerUtils.optionalChildrenToInstall(
+						children,
+						optionalfeatures);
+			}
+
+			// determine number of monitor tasks
+			//   2 tasks for the feature jar (download/verify + install)
+			// + 2*n tasks for plugin entries (download/verify + install for each)
+			// + 1*m tasks per non-plugin data entry (download for each)
+			// + 1 task for custom non-plugin entry handling (1 for all combined)
+			// + 5*x tasks for children features (5 subtasks per install)
+			int taskCount =
+				2
+					+ 2 * pluginsToInstall.length
+					+ nonPluginsToInstall.length
+					+ 1
+					+ 5 * children.length;
+			monitor.beginTask("", taskCount); //$NON-NLS-1$
+			SubProgressMonitor subMonitor = null;
+
+			// start log
+			recoveryLog.open(ErrorRecoveryLog.START_INSTALL_LOG);
+
+			// Start the installation tasks			
+			handler.installInitiated();
+
+			// Download and verify feature archive(s)
+			ContentReference[] references =
+				provider.getFeatureEntryArchiveReferences(monitor);
+			verifyReferences(
+				verifier,
+				references,
+				monitor,
+				verificationListener,
+				true);
+			monitorWork(monitor, 1);
+			
+			// Download and verify plugin archives
+			for (int i = 0; i < pluginsToInstall.length; i++) {
+				references = provider.getPluginEntryArchiveReferences(
+						pluginsToInstall[i], monitor);
+				verifyReferences(verifier, references, monitor,
+								verificationListener, false);
+				monitorWork(monitor, 1);
+			}
+			
+			handler.pluginsDownloaded(pluginsToInstall);
+
+			Vector filteredPlugins = new Vector();
+            // Download non-plugin archives. Verification handled by optional
+            // install handler
+            for (int i = 0; i < nonPluginsToInstall.length; i++) {
+                if (handler.acceptNonPluginData(nonPluginsToInstall[i])) {
+                    references = provider.getNonPluginEntryArchiveReferences(
+                            nonPluginsToInstall[i], monitor);
+                    monitorWork(monitor, 1);
+                    filteredPlugins.add(nonPluginsToInstall[i]);
+                }
+            }
+            nonPluginsToInstall = (INonPluginEntry[]) filteredPlugins
+                    .toArray(new INonPluginEntry[0]);
+            handler.nonPluginDataDownloaded(nonPluginsToInstall,
+                    verificationListener);
+
+			// All archives are downloaded and verified. Get ready to install
+			consumer = targetFeature.getFeatureContentConsumer();
+
+			// install the children feature
+			// check if they are optional, and if they should be installed [2.0.1]
+			for (int i = 0; i < children.length; i++) {
+				IFeature childFeature = null;
+				try {
+					childFeature = children[i].getFeature(null);
+				} catch (CoreException e) {
+					UpdateCore.warn(null, e);
+				}
+				if (childFeature != null) {
+					subMonitor = new SubProgressMonitor(monitor, 5);
+					((Site) targetSite).install(// need to cast
+					childFeature,
+						optionalfeatures,
+						consumer,
+						verifier,
+						verificationListener,
+						subMonitor);
+				}
+			}
+
+			// Install plugin files
+			for (int i = 0; i < pluginsToInstall.length; i++) {
+				// if another feature has already installed this plugin, skip it
+				if (InstallRegistry.getInstance().isPluginJustInstalled(pluginsToInstall[i])) {
+					monitor.worked(1);
+					continue;
+				}
+				IContentConsumer pluginConsumer =
+					consumer.open(pluginsToInstall[i]);
+				// TODO consumer.open returns either
+				// SiteFilePackedPluginContentConsumer or SiteFilePluginContentConsumer
+				// and they are fed either
+				// PluginEntryArchiveReference or PluginEntryContentReferences
+				// it would be better to have one that is given PluginEntryArchiveReference
+				// but it would break external IContentConsumers
+
+				if(pluginsToInstall[i] instanceof PluginEntryModel && !((PluginEntryModel)pluginsToInstall[i]).isUnpack()){
+					// plugin can run from a jar
+					references = provider.getPluginEntryArchiveReferences(
+							pluginsToInstall[i], monitor);
+				} else{
+					// plugin must be unpacked
+					references =
+						provider.getPluginEntryContentReferences(
+							pluginsToInstall[i],
+							monitor);
+				}
+
+				String msg = ""; //$NON-NLS-1$
+				subMonitor = new SubProgressMonitor(monitor, 1);
+				VersionedIdentifier pluginVerId =
+					pluginsToInstall[i].getVersionedIdentifier();
+				String pluginID =
+					(pluginVerId == null) ? "" : pluginVerId.getIdentifier(); //$NON-NLS-1$
+				msg = NLS.bind(Messages.Feature_TaskInstallPluginFiles, (new String[] { pluginID }));
+
+				for (int j = 0; j < references.length; j++) {
+					setMonitorTaskName(
+						subMonitor,
+						msg + references[j].getIdentifier());
+					pluginConsumer.store(references[j], subMonitor);
+				}
+
+				if (monitor.isCanceled())
+					abort();
+				else {
+					justInstalledPlugins.add(pluginsToInstall[i]);
+					InstallRegistry.registerPlugin(pluginsToInstall[i]);
+				}
+			}
+
+			// check if we need to install feature files [16718]	
+			// store will throw CoreException if another feature is already
+			// installed in the same place
+			alreadyInstalledFeature = featureAlreadyInstalled(targetSite);
+			// 18867
+			if (alreadyInstalledFeature == null) {
+				//Install feature files
+				references = provider.getFeatureEntryContentReferences(monitor);
+
+				String msg = ""; //$NON-NLS-1$
+				subMonitor = new SubProgressMonitor(monitor, 1);
+				msg = Messages.Feature_TaskInstallFeatureFiles; 
+
+				for (int i = 0; i < references.length; i++) {
+					setMonitorTaskName(
+						subMonitor,
+						msg + " " + references[i].getIdentifier()); //$NON-NLS-1$
+					consumer.store(references[i], subMonitor);
+				}
+
+				if (monitor.isCanceled())
+					abort();
+				else
+					InstallRegistry.registerFeature(this);
+			} else {
+				if (monitor.isCanceled())
+					abort();
+				else
+					monitor.worked(1);
+			}
+
+			// call handler to complete installation (eg. handle non-plugin entries)
+			handler.completeInstall(consumer);
+			monitorWork(monitor, 1);
+
+			// indicate install success
+			success = true;
+
+		} catch (InstallAbortedException e) {
+			abortedException = e;
+		} catch (CoreException e) {
+			originalException = e;
+		} finally {
+			Exception newException = null;
+			try {
+				if (consumer != null) {
+					if (success) {
+						result = consumer.close();
+						if (result == null) {
+							result = alreadyInstalledFeature; // 18867
+							if (result != null
+								&& optionalfeatures != null
+								&& optionalfeatures.length > 0) {
+								// reinitialize as new optional children may have been installed
+								reinitializeFeature(result);
+							}
+						}
+						// close the log
+						recoveryLog.close(ErrorRecoveryLog.END_INSTALL_LOG);
+					} else {
+						// unregister the just installed plugins
+						for (int i=0; i<justInstalledPlugins.size(); i++)
+							InstallRegistry.unregisterPlugin(((IPluginEntry)justInstalledPlugins.get(i)));
+						consumer.abort();
+					}
+				}
+				handler.installCompleted(success);
+				// if abort is done, no need for the log to stay
+				recoveryLog.delete();
+			} catch (CoreException e) {
+				newException = e;
+			}
+
+			// original exception wins unless it is InstallAbortedException
+			// and an error occured during abort
+			if (originalException != null) {
+				throw Utilities.newCoreException(
+					NLS.bind(Messages.InstallHandler_error, (new String[] { this.getLabel() })),
+					originalException);
+			}
+
+			if (newException != null)
+				throw Utilities.newCoreException(
+					NLS.bind(Messages.InstallHandler_error, (new String[] { this.getLabel() })),
+					newException);
+
+			if (abortedException != null) {
+				throw abortedException;
+			}
+
+		}
+		return result;
+	}
+
+	/**
+	 * Returns an array of plug-in entries referenced by this feature
+	 * 
+	 * @see IFeature#getPluginEntries()
+	 * @since 2.0
+	 */
+	public IPluginEntry[] getRawPluginEntries() {
+		PluginEntryModel[] result = getPluginEntryModels();
+		if (result.length == 0)
+			return new IPluginEntry[0];
+		else
+			return (IPluginEntry[]) result;
+	}
+
+	/*
+	 * Method filter.
+	 * @param result
+	 * @return IPluginEntry[]
+	 */
+	private IPluginEntry[] filterPluginEntry(IPluginEntry[] all) {
+		List list = new ArrayList();
+		if (all != null) {
+			for (int i = 0; i < all.length; i++) {
+				if (UpdateManagerUtils.isValidEnvironment(all[i]))
+					list.add(all[i]);
+			}
+		}
+
+		IPluginEntry[] result = new IPluginEntry[list.size()];
+		if (!list.isEmpty()) {
+			list.toArray(result);
+		}
+
+		return result;
+	}
+
+	/**
+	 * Returns the count of referenced plug-in entries.
+	 * 
+	 * @see IFeature#getPluginEntryCount()
+	 * @since 2.0
+	 */
+	public int getPluginEntryCount() {
+		return getPluginEntries().length;
+	}
+
+	/**
+	 * Returns an array of non-plug-in entries referenced by this feature
+	 * 
+	 * @see IFeature#getNonPluginEntries()
+	 * @since 2.0
+	 */
+	public INonPluginEntry[] getRawNonPluginEntries() {
+		NonPluginEntryModel[] result = getNonPluginEntryModels();
+		if (result.length == 0)
+			return new INonPluginEntry[0];
+		else
+			return (INonPluginEntry[]) result;
+	}
+
+	/**
+	 * Returns the count of referenced non-plug-in entries.
+	 * 
+	 * @see IFeature#getNonPluginEntryCount()
+	 * @since 2.0
+	 */
+	public int getNonPluginEntryCount() {
+		return getNonPluginEntryModels().length;
+	}
+
+	/**
+	 * Returns an array of feature references included by this feature
+	 * 
+	 * @return an erray of feature references, or an empty array.
+	 * @since 2.0
+	 */
+	public IIncludedFeatureReference[] getRawIncludedFeatureReferences()
+		throws CoreException {
+		if (includedFeatureReferences == null)
+			initializeIncludedReferences();
+
+		if (includedFeatureReferences.size() == 0)
+			return new IncludedFeatureReference[0];
+
+		return (IIncludedFeatureReference[]) includedFeatureReferences.toArray(
+			new IIncludedFeatureReference[includedFeatureReferences.size()]);
+	}
+	/**
+	 * Returns the download size of the feature, if it can be determined.
+	 * 
+	 * @see IFeature#getDownloadSize()
+	 * @since 2.0
+	 */
+	public long getDownloadSize() {
+		try {
+			Set allPluginEntries = new HashSet();
+			Set allNonPluginEntries = new HashSet();
+
+			IPluginEntry[] plugins = getPluginEntries();
+			allPluginEntries.addAll(Arrays.asList(plugins));
+			INonPluginEntry[] nonPlugins = getNonPluginEntries();
+			allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
+
+			IFeatureReference[] children = getIncludedFeatureReferences();
+			for (int i = 0; i < children.length; i++) {
+				plugins = children[i].getFeature(null).getPluginEntries();
+				allPluginEntries.addAll(Arrays.asList(plugins));
+				nonPlugins = children[i].getFeature(null).getNonPluginEntries();
+				allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
+			}
+
+			IPluginEntry[] totalPlugins =
+				new IPluginEntry[allPluginEntries.size()];
+			INonPluginEntry[] totalNonPlugins =
+				new INonPluginEntry[allNonPluginEntries.size()];
+			if (allPluginEntries.size() != 0) {
+				allPluginEntries.toArray(totalPlugins);
+			}
+			if (allNonPluginEntries.size() != 0) {
+				allNonPluginEntries.toArray(totalNonPlugins);
+			}
+
+			return getFeatureContentProvider().getDownloadSizeFor(
+				totalPlugins,
+				totalNonPlugins);
+
+		} catch (CoreException e) {
+			UpdateCore.warn(null, e);
+			return ContentEntryModel.UNKNOWN_SIZE;
+		}
+	}
+
+	/**
+	 * Returns the install size of the feature, if it can be determined.
+	 * 
+	 * @see IFeature#getInstallSize()
+	 * @since 2.0
+	 */
+	public long getInstallSize() {
+		try {
+			Set allPluginEntries = new HashSet();
+			Set allNonPluginEntries = new HashSet();
+
+			IPluginEntry[] plugins = getPluginEntries();
+			allPluginEntries.addAll(Arrays.asList(plugins));
+			INonPluginEntry[] nonPlugins = getNonPluginEntries();
+			allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
+
+			IFeatureReference[] children = getIncludedFeatureReferences();
+			for (int i = 0; i < children.length; i++) {
+				plugins = children[i].getFeature(null).getPluginEntries();
+				allPluginEntries.addAll(Arrays.asList(plugins));
+				nonPlugins = children[i].getFeature(null).getNonPluginEntries();
+				allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
+			}
+
+			IPluginEntry[] totalPlugins =
+				new IPluginEntry[allPluginEntries.size()];
+			INonPluginEntry[] totalNonPlugins =
+				new INonPluginEntry[allNonPluginEntries.size()];
+			if (allPluginEntries.size() != 0) {
+				allPluginEntries.toArray(totalPlugins);
+			}
+			if (allNonPluginEntries.size() != 0) {
+				allNonPluginEntries.toArray(totalNonPlugins);
+			}
+
+			return getFeatureContentProvider().getInstallSizeFor(
+				totalPlugins,
+				totalNonPlugins);
+
+		} catch (CoreException e) {
+			UpdateCore.warn(null, e);
+			return ContentEntryModel.UNKNOWN_SIZE;
+		}
+	}
+
+	/**
+	 * Returns the content provider for this feature.
+	 * 
+	 * @see IFeature#getFeatureContentProvider()
+	 * @since 2.0
+	 */
+	public IFeatureContentProvider getFeatureContentProvider()
+		throws CoreException {
+		if (featureContentProvider == null) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.Feature_NoContentProvider, (new String[] { getVersionedIdentifier().toString() })),
+				null);	
+		}
+		return this.featureContentProvider;
+	}
+
+	/**
+	 * Returns the content consumer for this feature.
+	 * 
+	 * @see IFeature#getFeatureContentConsumer()
+	 * @since 2.0
+	 */
+	public IFeatureContentConsumer getFeatureContentConsumer()
+		throws CoreException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Sets the site for this feature.
+	 * 
+	 * @see IFeature#setSite(ISite)
+	 * @since 2.0
+	 */
+	public void setSite(ISite site) throws CoreException {
+		if (this.site != null) {
+			String featureURLString =
+				(getURL() != null) ? getURL().toExternalForm() : ""; //$NON-NLS-1$
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.Feature_SiteAlreadySet, (new String[] { featureURLString })),
+				null);
+		}
+		this.site = site;
+	}
+
+	/**
+	 * Sets the content provider for this feature.
+	 * 
+	 * @see IFeature#setFeatureContentProvider(IFeatureContentProvider)
+	 * @since 2.0
+	 */
+	public void setFeatureContentProvider(IFeatureContentProvider featureContentProvider) {
+		this.featureContentProvider = featureContentProvider;
+		featureContentProvider.setFeature(this);
+	}
+
+	/**
+	 * Return the string representation of this fetaure
+	 * 
+	 * @return feature as string
+	 * @since 2.0
+	 */
+	public String toString() {
+		String URLString =
+			(getURL() == null)
+				? Messages.Feature_NoURL
+				: getURL().toExternalForm();
+
+		String verString =
+			NLS.bind(Messages.Feature_FeatureVersionToString, (new String[] { URLString, getVersionedIdentifier().toString() }));
+		String label = getLabel() == null ? "" : getLabel(); //$NON-NLS-1$
+		return verString + " [" + label + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/*
+	 * Installation has been cancelled, abort and revert
+	 */
+	private void abort() throws CoreException {
+		String msg = Messages.Feature_InstallationCancelled; 
+		throw new InstallAbortedException(msg, null);
+	}
+
+	/*
+	 * Initializes includes feature references
+	 * If the included feature reference is found on the site, add it to the List
+	 * Otherwise attempt to instanciate it using the same type as this feature and
+	 * using the default location on the site.
+	 */
+	private void initializeIncludedReferences() throws CoreException {
+		includedFeatureReferences = new ArrayList();
+
+		IIncludedFeatureReference[] nestedFeatures = getFeatureIncluded();
+		if (nestedFeatures.length == 0)
+			return;
+
+		ISite site = getSite();
+		if (site == null)
+			return;
+
+		for (int i = 0; i < nestedFeatures.length; i++) {
+			IIncludedFeatureReference include = nestedFeatures[i];
+			IIncludedFeatureReference newRef =
+				getPerfectIncludeFeature(site, include);
+			includedFeatureReferences.add(newRef);
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private IIncludedFeatureReference getPerfectIncludeFeature(
+		ISite site,
+		IIncludedFeatureReference include)
+		throws CoreException {
+
+		// [20367] no site, cannot initialize nested references
+		ISiteFeatureReference[] refs = site.getFeatureReferences();
+		VersionedIdentifier identifier = include.getVersionedIdentifier();
+
+		// too long to compute if not a file system
+		// other solution would be to parse feature.xml
+		// when parsing file system to create archive features/FeatureId_Ver.jar
+		if ("file".equals(site.getURL().getProtocol())) { //$NON-NLS-1$
+			// check if declared on the Site
+			if (refs != null) {
+				for (int ref = 0; ref < refs.length; ref++) {
+					if (refs[ref] != null) {
+						VersionedIdentifier id =
+							refs[ref].getVersionedIdentifier();
+						if (identifier.equals(id)) {
+							// found a ISiteFeatureReference that matches our IIncludedFeatureReference
+							IncludedFeatureReference newRef =
+								new IncludedFeatureReference(refs[ref]);
+							newRef.isOptional(include.isOptional());
+							if (include instanceof FeatureReferenceModel)
+								newRef.setLabel(
+									((FeatureReferenceModel) include)
+										.getLabel());
+							newRef.setSearchLocation(
+								include.getSearchLocation());
+							return newRef;
+						}
+					}
+				}
+			}
+		}
+
+        // instantiate by mapping it based on the site.xml
+        // in future we may ask for a factory to create the feature ref
+        IncludedFeatureReference newRef = new UpdateSiteIncludedFeatureReference(include);
+        newRef.setSite(getSite());
+        IFeatureReference parentRef = getSite().getFeatureReference(this);
+        if (parentRef instanceof FeatureReference) {
+         newRef.setType(((FeatureReference) parentRef).getType());
+        }
+        String featureID = Site.DEFAULT_FEATURE_PATH + identifier.toString();
+        if(this instanceof TargetFeature)
+         featureID = featureID + "/"; //$NON-NLS-1$
+        else
+         featureID = featureID + ".jar"; //$NON-NLS-1$
+		URL featureURL =
+			getSite().getSiteContentProvider().getArchiveReference(featureID);
+		newRef.setURL(featureURL);
+		newRef.setFeatureIdentifier(identifier.getIdentifier());
+		newRef.setFeatureVersion(identifier.getVersion().toString());
+		try {
+			newRef.resolve(getSite().getURL(), null);
+			// no need to get the bundle
+			return newRef;
+		} catch (Exception e) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.Feature_UnableToInitializeFeatureReference, (new String[] { identifier.toString() })),
+				e);
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private void debug(String trace) {
+		//DEBUG
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL) {
+			UpdateCore.debug(trace);
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private void setMonitorTaskName(
+		IProgressMonitor monitor,
+		String taskName) {
+		if (monitor != null)
+			monitor.setTaskName(taskName);
+	}
+
+	/*
+	 *
+	 */
+	private void monitorWork(IProgressMonitor monitor, int tick)
+		throws CoreException {
+		if (monitor != null) {
+			monitor.worked(tick);
+			if (monitor.isCanceled()) {
+				abort();
+			}
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private void verifyReferences(
+		IVerifier verifier,
+		ContentReference[] references,
+		InstallMonitor monitor,
+		IVerificationListener verificationListener,
+		boolean isFeature)
+		throws CoreException {
+		IVerificationResult vr = null;
+		if (verifier != null) {
+			for (int j = 0; j < references.length; j++) {
+				vr = verifier.verify(this, references[j], isFeature, monitor);
+				if (vr != null) {
+					if (verificationListener == null)
+						return;
+
+					int result = verificationListener.prompt(vr);
+
+					if (result == IVerificationListener.CHOICE_ABORT) {
+						String msg = Messages.JarVerificationService_CancelInstall; 
+						Exception e = vr.getVerificationException();
+						throw new InstallAbortedException(msg, e);
+					}
+					if (result == IVerificationListener.CHOICE_ERROR) {
+						throw Utilities
+							.newCoreException(
+								Messages.JarVerificationService_UnsucessfulVerification,	
+						vr.getVerificationException());
+					}
+				}
+			}
+		}
+	}
+
+	/*
+	 * returns reference if the same feature is installed on the site
+	 * [18867]
+	 */
+	private IFeatureReference featureAlreadyInstalled(ISite targetSite) {
+
+		ISiteFeatureReference[] references = targetSite.getFeatureReferences();
+		IFeatureReference currentReference = null;
+		for (int i = 0; i < references.length; i++) {
+			currentReference = references[i];
+			// do not compare URL
+			try {
+				if (this.equals(currentReference.getFeature(null)))
+					return currentReference; // 18867
+			} catch (CoreException e) {
+				UpdateCore.warn(null, e);
+			}
+		}
+
+		UpdateCore.warn(
+			"ValidateAlreadyInstalled:Feature " //$NON-NLS-1$
+				+ this
+				+ " not found on site:" //$NON-NLS-1$
+				+ this.getURL());
+		return null;
+	}
+
+	/*
+	 * re initialize children of the feature, invalidate the cache
+	 * @param result FeatureReference to reinitialize.
+	 */
+	private void reinitializeFeature(IFeatureReference referenceToReinitialize) {
+
+		if (referenceToReinitialize == null)
+			return;
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+			UpdateCore.debug(
+				"Re initialize feature reference:" + referenceToReinitialize); //$NON-NLS-1$
+
+		IFeature feature = null;
+		try {
+			feature = referenceToReinitialize.getFeature(null);
+			if (feature != null && feature instanceof Feature) {
+				((Feature) feature).initializeIncludedReferences();
+			}
+			// bug 24981 - recursively go into hierarchy
+			// only if site if file 
+			ISite site = referenceToReinitialize.getSite();
+			if (site == null)
+				return;
+			URL url = site.getURL();
+			if (url == null)
+				return;
+			if ("file".equals(url.getProtocol())) { //$NON-NLS-1$
+				IFeatureReference[] included =
+					feature.getIncludedFeatureReferences();
+				for (int i = 0; i < included.length; i++) {
+					reinitializeFeature(included[i]);
+				}
+			}
+		} catch (CoreException e) {
+			UpdateCore.warn("", e); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IFeature#getRawIncludedFeatureReferences()
+	 */
+	public IIncludedFeatureReference[] getIncludedFeatureReferences()
+		throws CoreException {
+		return filterFeatures(getRawIncludedFeatureReferences());
+	}
+
+	/*
+	 * Method filterFeatures.
+	 * Also implemented in Site
+	 * 
+	 * @param list
+	 * @return List
+	 */
+	private IIncludedFeatureReference[] filterFeatures(IIncludedFeatureReference[] allIncluded) {
+		List list = new ArrayList();
+		if (allIncluded != null) {
+			for (int i = 0; i < allIncluded.length; i++) {
+				IIncludedFeatureReference included = allIncluded[i];
+				if (UpdateManagerUtils.isValidEnvironment(included))
+					list.add(included);
+				else {
+					if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS) {
+						UpdateCore.warn(
+							"Filtered out feature reference:" + included); //$NON-NLS-1$
+					}
+				}
+			}
+		}
+
+		IIncludedFeatureReference[] result =
+			new IIncludedFeatureReference[list.size()];
+		if (!list.isEmpty()) {
+			list.toArray(result);
+		}
+
+		return result;
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IFeature#getRawNonPluginEntries()
+	 */
+	public INonPluginEntry[] getNonPluginEntries() {
+		return filterNonPluginEntry(getRawNonPluginEntries());
+	}
+
+	/**
+	 * Method filterPluginEntry.
+	 * @param all
+	 * @return INonPluginEntry[]
+	 */
+	private INonPluginEntry[] filterNonPluginEntry(INonPluginEntry[] all) {
+		List list = new ArrayList();
+		if (all != null) {
+			for (int i = 0; i < all.length; i++) {
+				if (UpdateManagerUtils.isValidEnvironment(all[i]))
+					list.add(all[i]);
+			}
+		}
+
+		INonPluginEntry[] result = new INonPluginEntry[list.size()];
+		if (!list.isEmpty()) {
+			list.toArray(result);
+		}
+
+		return result;
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IFeature#getRawPluginEntries()
+	 */
+	public IPluginEntry[] getPluginEntries() {
+		return filterPluginEntry(getRawPluginEntries());
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IFeature#getImports()
+	 */
+	public IImport[] getImports() {
+		return filterImports(getRawImports());
+	}
+
+	/**
+	 * Method filterImports.
+	 * @param all
+	 * @return IImport[]
+	 */
+	private IImport[] filterImports(IImport[] all) {
+		List list = new ArrayList();
+		if (all != null) {
+			for (int i = 0; i < all.length; i++) {
+				if (UpdateManagerUtils.isValidEnvironment(all[i]))
+					list.add(all[i]);
+			}
+		}
+
+		IImport[] result = new IImport[list.size()];
+		if (!list.isEmpty()) {
+			list.toArray(result);
+		}
+
+		return result;
+	}
+
+}
\ No newline at end of file
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/FeatureContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/FeatureContentProvider.java
new file mode 100644
index 0000000..0ceea5a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/FeatureContentProvider.java
@@ -0,0 +1,589 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.model.ContentEntryModel;
+import org.eclipse.update.core.model.InstallAbortedException;
+import org.eclipse.update.core.model.NonPluginEntryModel;
+import org.eclipse.update.core.model.PluginEntryModel;
+import org.eclipse.update.internal.core.FatalIOException;
+import org.eclipse.update.internal.core.FeatureDownloadException;
+import org.eclipse.update.internal.core.FileFragment;
+import org.eclipse.update.internal.core.InternalSiteManager;
+import org.eclipse.update.internal.core.LockManager;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+
+/**
+ * Base implementation of a feature content provider. This class provides a set
+ * of helper methods useful for implementing feature content providers. In
+ * particular, methods dealing with downloading and caching of feature files.
+ * <p>
+ * This class must be subclassed 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>
+ * @see org.eclipse.update.core.IFeatureContentProvider
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class FeatureContentProvider implements IFeatureContentProvider {
+
+	/**
+	 *  
+	 */
+	public class FileFilter {
+
+		private IPath filterPath = null;
+
+		/**
+		 * Constructor for FileFilter.
+		 */
+		public FileFilter(String filter) {
+			super();
+			this.filterPath = new Path(filter);
+		}
+
+		/**
+		 * returns true if the name matches the rule
+		 */
+		public boolean accept(String name) {
+
+			if (name == null)
+				return false;
+
+			// no '*' pattern matching
+			// must be equals
+			IPath namePath = new Path(name);
+			if (filterPath.lastSegment().indexOf('*') == -1) {
+				return filterPath.equals(namePath);
+			}
+
+			// check same file extension if extension exists (a.txt/*.txt)
+			// or same file name (a.txt,a.*)
+			String extension = filterPath.getFileExtension();
+			if (extension != null && !extension.equals("*")) { //$NON-NLS-1$
+				if (!extension.equalsIgnoreCase(namePath.getFileExtension()))
+					return false;
+			} else {
+				IPath noExtension = filterPath.removeFileExtension();
+				String fileName = noExtension.lastSegment();
+				if (!fileName.equals("*")) { //$NON-NLS-1$
+					if (!namePath.lastSegment().startsWith(fileName))
+						return false;
+				}
+			}
+
+			// check same path
+			IPath p1 = namePath.removeLastSegments(1);
+			IPath p2 = filterPath.removeLastSegments(1);
+			return p1.equals(p2);
+		}
+
+	}
+
+	private URL base;
+	private IFeature feature;
+	private File tmpDir; // local work area for each provider
+	public static final String JAR_EXTENSION = ".jar"; //$NON-NLS-1$	
+
+	private static final String DOT_PERMISSIONS = "permissions.properties"; //$NON-NLS-1$
+	private static final String EXECUTABLES = "permissions.executable"; //$NON-NLS-1$
+
+	/**
+	 * Feature content provider constructor
+	 * 
+	 * @param base
+	 *            feature URL. The interpretation of this URL is specific to
+	 *            each content provider.
+	 * @since 2.0
+	 */
+	public FeatureContentProvider(URL base) {
+		this.base = base;
+		this.feature = null;
+	}
+
+	/**
+	 * Returns the feature url.
+	 * 
+	 * @see IFeatureContentProvider#getURL()
+	 */
+	public URL getURL() {
+		return base;
+	}
+
+	/**
+	 * Returns the feature associated with this content provider.
+	 * 
+	 * @see IFeatureContentProvider#getFeature()
+	 */
+	public IFeature getFeature() {
+		return feature;
+	}
+
+	/**
+	 * Sets the feature associated with this content provider.
+	 * 
+	 * @see IFeatureContentProvider#setFeature(IFeature)
+	 */
+	public void setFeature(IFeature feature) {
+		this.feature = feature;
+	}
+
+	/**
+	 * Returns the specified reference as a local file system reference. If
+	 * required, the file represented by the specified content reference is
+	 * first downloaded to the local system
+	 * 
+	 * @param ref
+	 *            content reference
+	 * @param monitor
+	 *            progress monitor, can be <code>null</code>
+	 * @exception IOException
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public ContentReference asLocalReference(ContentReference ref, InstallMonitor monitor) throws IOException, CoreException {
+
+		// check to see if this is already a local reference
+		if (ref.isLocalReference())
+			return ref;
+
+		// check to see if we already have a local file for this reference
+		String key = ref.toString();
+
+		// need to synch as another thread my have created the file but
+		// is still copying into it
+		File localFile = null;
+		FileFragment localFileFragment = null;
+		Object keyLock = LockManager.getLock(key);
+		synchronized (keyLock) {
+			localFile = Utilities.lookupLocalFile(key);
+			if (localFile != null) {
+				// check if the cached file is still valid (no newer version on
+				// server)
+				try {
+				if (UpdateManagerUtils.isSameTimestamp(ref.asURL(), localFile.lastModified()))
+					return ref.createContentReference(ref.getIdentifier(), localFile);
+				} catch(FatalIOException e) {
+					throw e;
+				} catch(IOException e) {
+					throw new FeatureDownloadException(NLS.bind(Messages.FeatureContentProvider_ExceptionDownloading, (new Object[] {getURL().toExternalForm()})), e);
+				}
+			}
+
+			if (localFile == null) {
+				localFileFragment = UpdateManagerUtils.lookupLocalFileFragment(key);
+			}
+			// 
+			// download the referenced file into local temporary area
+			InputStream is = null;
+			OutputStream os = null;
+			long bytesCopied = 0;
+			long inputLength = 0;
+			boolean success = false;
+			if (monitor != null) {
+				monitor.saveState();
+				monitor.setTaskName(Messages.FeatureContentProvider_Downloading);
+				monitor.subTask(ref.getIdentifier() + " "); //$NON-NLS-1$
+				try {
+					monitor.setTotalCount(ref.getInputSize());
+				} catch (FatalIOException e) {
+					throw e;
+				} catch (IOException e) {
+					throw new FeatureDownloadException(NLS.bind(Messages.FeatureContentProvider_ExceptionDownloading, (new Object[] {getURL().toExternalForm()})), e);
+				}
+				monitor.showCopyDetails(true);
+			}
+
+			try {
+				//long startTime = System.nanoTime();
+				if (localFileFragment != null && "http".equals(ref.asURL().getProtocol())) { //$NON-NLS-1$
+					localFile = localFileFragment.getFile();
+					try {
+						// get partial input stream
+						is = ref.getPartialInputStream(localFileFragment.getSize());
+						inputLength = ref.getInputSize() - localFileFragment.getSize();
+						// get output stream to append to file fragment
+						os = new BufferedOutputStream(
+						// PAL foundation
+								//new FileOutputStream(localFile, true));
+								new FileOutputStream(localFile.getPath(), true));
+					} catch (FatalIOException e) {
+						throw e;
+					} catch (IOException e) {
+						try {
+							if (is != null)
+								is.close();
+						} catch (IOException ioe) {
+						}
+						is = null;
+						os = null;
+						localFileFragment = null;
+						throw new FeatureDownloadException(NLS.bind(Messages.FeatureContentProvider_ExceptionDownloading, (new Object[] {getURL().toExternalForm()})), e);
+					}
+				}
+				if (is == null) {
+					// must download from scratch
+					localFile = Utilities.createLocalFile(getWorkingDirectory(), null);
+					try {
+						is = ref.getInputStream();
+						inputLength = ref.getInputSize();
+					} catch (FatalIOException e) {
+						throw Utilities.newCoreException(NLS.bind(Messages.FeatureContentProvider_UnableToRetrieve, (new Object[] {ref})), e);
+					} catch (IOException e) {
+						throw new FeatureDownloadException(NLS.bind(Messages.FeatureContentProvider_ExceptionDownloading, (new Object[] {getURL().toExternalForm()})), e);
+					}
+
+					try {
+						os = new BufferedOutputStream(new FileOutputStream(localFile));
+					} catch (FileNotFoundException e) {
+						throw Utilities.newCoreException(NLS.bind(Messages.FeatureContentProvider_UnableToCreate, (new Object[] {localFile})), e);
+					}
+				}
+
+				Date start = new Date();
+				if (localFileFragment != null) {
+					bytesCopied = localFileFragment.getSize();
+					if (monitor != null) {
+						monitor.setCopyCount(bytesCopied);
+					}
+				}
+
+				// Transfer as many bytes as possible from input to output stream
+				long offset = UpdateManagerUtils.copy(is, os, monitor, inputLength);
+				if (offset != -1) {
+					bytesCopied += offset;
+					if (bytesCopied > 0) {
+						// preserve partially downloaded file
+						UpdateManagerUtils.mapLocalFileFragment(key, new FileFragment(localFile, bytesCopied));
+					}
+					if (monitor != null && monitor.isCanceled()) {
+						String msg = Messages.Feature_InstallationCancelled;
+						throw new InstallAbortedException(msg, null);
+					} else {
+						throw new FeatureDownloadException(NLS.bind(Messages.FeatureContentProvider_ExceptionDownloading, (new Object[] {getURL().toExternalForm()})), new IOException());
+					}
+				} else {
+					UpdateManagerUtils.unMapLocalFileFragment(key);
+				}
+
+				Date stop = new Date();
+				long timeInseconds = (stop.getTime() - start.getTime()) / 1000;
+				// time in milliseconds /1000 = time in seconds
+				InternalSiteManager.downloaded(
+					ref.getInputSize(),
+					(timeInseconds),
+					ref.asURL());
+
+				success = true;
+				//long endTime = System.nanoTime();
+				// file is downloaded succesfully, map it
+				Utilities.mapLocalFile(key, localFile);
+				
+				/*if (ref.asURL().toExternalForm().endsWith("jar")) {
+					synchronized(this.getClass()) {
+						timer += (endTime - startTime);
+						if (first == 0) {
+							first = endTime - startTime;
+						}
+					}
+				}*/
+			} catch (ClassCastException e) {
+				throw Utilities.newCoreException(
+					NLS.bind(Messages.FeatureContentProvider_UnableToCreate, (new Object[] { localFile })),
+					e);
+			} finally {
+				//Do not close IS if user cancel,
+				//closing IS will read the entire Stream until the end
+				if (success && is != null)
+					try {
+						is.close();
+					} catch (IOException e) {
+					}
+				if (os != null)
+					try {
+						os.close(); // should flush buffer stream
+					} catch (IOException e) {
+					}
+
+				if (success || bytesCopied > 0) {
+					// set the timestamp on the temp file to match the remote
+					// timestamp
+					localFile.setLastModified(ref.getLastModified());
+				}
+				if (monitor != null)
+					monitor.restoreState();
+			}
+			LockManager.returnLock(key);
+		} // end lock
+		ContentReference reference =
+			ref.createContentReference(ref.getIdentifier(), localFile);
+		
+		UpdateCore.getPlugin().getUpdateSession().markVisited(ref.asURL());
+		
+		return reference;
+	}
+
+	/**
+	 * Returns the specified reference as a local file. If required, the file
+	 * represented by the specified content reference is first downloaded to
+	 * the local system
+	 * 
+	 * @param ref
+	 *            content reference
+	 * @param monitor
+	 *            progress monitor, can be <code>null</code>
+	 * @exception IOException
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public File asLocalFile(ContentReference ref, InstallMonitor monitor) throws IOException, CoreException {
+		File file = ref.asFile();
+		ContentReference localRef = asLocalReference(ref, monitor);
+		file = localRef.asFile();
+		return file;
+	}
+
+	/**
+	 * Returns working directory for this content provider
+	 * 
+	 * @return working directory
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	protected File getWorkingDirectory() throws IOException {
+		if (tmpDir == null)
+			tmpDir = Utilities.createWorkingDirectory();
+		return tmpDir;
+	}
+
+	/**
+	 * Returns the total size of all archives required for the specified
+	 * plug-in and non-plug-in entries (the "packaging" view).
+	 * 
+	 * @see IFeatureContentProvider#getDownloadSizeFor(IPluginEntry[],
+	 *      INonPluginEntry[])
+	 */
+	public long getDownloadSizeFor(IPluginEntry[] pluginEntries, INonPluginEntry[] nonPluginEntries) {
+		long result = 0;
+
+		// if both are null or empty, return UNKNOWN size
+		if ((pluginEntries == null || pluginEntries.length == 0) && (nonPluginEntries == null || nonPluginEntries.length == 0)) {
+			return ContentEntryModel.UNKNOWN_SIZE;
+		}
+
+		// loop on plugin entries
+		long size = 0;
+		if (pluginEntries != null)
+			for (int i = 0; i < pluginEntries.length; i++) {
+				size = ((PluginEntryModel) pluginEntries[i]).getDownloadSize();
+				if (size == ContentEntryModel.UNKNOWN_SIZE) {
+					return ContentEntryModel.UNKNOWN_SIZE;
+				}
+				result += size;
+			}
+
+		// loop on non plugin entries
+		if (nonPluginEntries != null)
+			for (int i = 0; i < nonPluginEntries.length; i++) {
+				size = ((NonPluginEntryModel) nonPluginEntries[i]).getDownloadSize();
+				if (size == ContentEntryModel.UNKNOWN_SIZE) {
+					return ContentEntryModel.UNKNOWN_SIZE;
+				}
+				result += size;
+			}
+
+		return result;
+	}
+
+	/**
+	 * Returns the total size of all files required for the specified plug-in
+	 * and non-plug-in entries (the "logical" view).
+	 * 
+	 * @see IFeatureContentProvider#getInstallSizeFor(IPluginEntry[],
+	 *      INonPluginEntry[])
+	 */
+	public long getInstallSizeFor(IPluginEntry[] pluginEntries, INonPluginEntry[] nonPluginEntries) {
+		long result = 0;
+
+		// if both are null or empty, return UNKNOWN size
+		if ((pluginEntries == null || pluginEntries.length == 0) && (nonPluginEntries == null || nonPluginEntries.length == 0)) {
+			return ContentEntryModel.UNKNOWN_SIZE;
+		}
+
+		// loop on plugin entries
+		long size = 0;
+		if (pluginEntries != null)
+			for (int i = 0; i < pluginEntries.length; i++) {
+				size = ((PluginEntryModel) pluginEntries[i]).getInstallSize();
+				if (size == ContentEntryModel.UNKNOWN_SIZE) {
+					return ContentEntryModel.UNKNOWN_SIZE;
+				}
+				result += size;
+			}
+
+		// loop on non plugin entries
+		if (nonPluginEntries != null)
+			for (int i = 0; i < nonPluginEntries.length; i++) {
+				size = ((NonPluginEntryModel) nonPluginEntries[i]).getInstallSize();
+				if (size == ContentEntryModel.UNKNOWN_SIZE) {
+					return ContentEntryModel.UNKNOWN_SIZE;
+				}
+				result += size;
+			}
+
+		return result;
+	}
+
+	/**
+	 * Returns the path identifier for a plugin entry. <code>plugins/&lt;pluginId>_&lt;pluginVersion>.jar</code>
+	 * 
+	 * @return the path identifier
+	 */
+	protected String getPathID(IPluginEntry entry) {
+		return Site.DEFAULT_PLUGIN_PATH + entry.getVersionedIdentifier().toString() + JAR_EXTENSION;
+	}
+
+	/**
+	 * Returns the path identifer for a non plugin entry. <code>features/&lt;featureId>_&lt;featureVersion>/&lt;dataId></code>
+	 * 
+	 * @return the path identifier
+	 */
+	protected String getPathID(INonPluginEntry entry) {
+		String nonPluginBaseID = Site.DEFAULT_FEATURE_PATH + feature.getVersionedIdentifier().toString() + "/"; //$NON-NLS-1$
+		return nonPluginBaseID + entry.getIdentifier();
+	}
+
+	/**
+	 * Sets the permission of all the ContentReferences Check for the
+	 * .permissions contentReference and use it to set the permissions of other
+	 * ContentReference
+	 */
+	protected void validatePermissions(ContentReference[] references) {
+
+		if (references == null || references.length == 0)
+			return;
+
+		Map permissions = getPermissions(references);
+		if (permissions.isEmpty())
+			return;
+
+		for (int i = 0; i < references.length; i++) {
+			ContentReference contentReference = references[i];
+			String id = contentReference.getIdentifier();
+			Object value = null;
+			if ((value = matchesOneRule(id, permissions)) != null) {
+				Integer permission = (Integer) value;
+				contentReference.setPermission(permission.intValue());
+			}
+		}
+	}
+
+	/**
+	 * Returns the value of the matching rule or <code>null</code> if none
+	 * found. A rule is matched if the id is equals to a key, or if the id is
+	 * resolved by a key. if the id is <code>/path/file.txt</code> it is
+	 * resolved by <code>/path/*</code> or <code>/path/*.txt</code>
+	 * 
+	 * @param id
+	 *            the identifier
+	 * @param permissions
+	 *            list of rules
+	 * @return Object the value of the matching rule or <code>null</code>
+	 */
+	private Object matchesOneRule(String id, Map permissions) {
+
+		Set keySet = permissions.keySet();
+		Iterator iter = keySet.iterator();
+		while (iter.hasNext()) {
+			FileFilter rule = (FileFilter) iter.next();
+			if (rule.accept(id)) {
+				return permissions.get(rule);
+			}
+		}
+
+		return null;
+	}
+
+	/*
+	 * returns the permission MAP
+	 */
+	private Map getPermissions(ContentReference[] references) {
+
+		Map result = new HashMap();
+		// search for .permissions
+		boolean notfound = true;
+		ContentReference permissionReference = null;
+		for (int i = 0; i < references.length && notfound; i++) {
+			ContentReference contentReference = references[i];
+			if (DOT_PERMISSIONS.equals(contentReference.getIdentifier())) {
+				notfound = false;
+				permissionReference = contentReference;
+			}
+		}
+		if (notfound)
+			return result;
+
+		Properties prop = new Properties();
+		InputStream propertyStream = null;
+		try {
+			try {
+				propertyStream = permissionReference.getInputStream();
+				prop.load(propertyStream);
+			} finally {
+				if (propertyStream != null)
+					propertyStream.close();
+			}
+		} catch (IOException e) {
+			UpdateCore.warn("", e); //$NON-NLS-1$
+		}
+
+		String executables = prop.getProperty(EXECUTABLES);
+		if (executables == null)
+			return result;
+
+		StringTokenizer tokenizer = new StringTokenizer(executables, ","); //$NON-NLS-1$
+		Integer defaultExecutablePermission = new Integer(ContentReference.DEFAULT_EXECUTABLE_PERMISSION);
+		while (tokenizer.hasMoreTokens()) {
+			FileFilter filter = new FileFilter(tokenizer.nextToken());
+			result.put(filter, defaultExecutablePermission);
+		}
+
+		return result;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/FeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/FeatureReference.java
new file mode 100644
index 0000000..520081c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/FeatureReference.java
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.model.FeatureReferenceModel;
+import org.eclipse.update.core.model.SiteModel;
+import org.eclipse.update.internal.core.FeatureTypeFactory;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * Convenience implementation of a feature reference.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.IFeatureReference
+ * @see org.eclipse.update.core.model.FeatureReferenceModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class FeatureReference extends FeatureReferenceModel implements IFeatureReference, IPlatformEnvironment {
+
+	private VersionedIdentifier versionId;
+
+	//PERF: new instance variable
+	private IFeature exactFeature;
+
+	/**
+	 * Feature reference default constructor
+	 */
+	public FeatureReference() {
+		super();
+	}
+
+	/**
+	 * Constructor FeatureReference.
+	 * @param ref the reference to copy
+	 */
+	public FeatureReference(IFeatureReference ref) {
+		super((FeatureReferenceModel) ref);
+		try {
+			setURL(ref.getURL());
+		} catch (CoreException e) {
+			UpdateCore.warn("", e); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Constructor FeatureReference.
+	 * @param ref the reference to copy
+	 */
+	public FeatureReference(FeatureReferenceModel ref) {
+		super(ref);
+		try {
+			setURL(ref.getURL());
+		} catch (CoreException e) {
+			UpdateCore.warn("", e); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Returns the feature this reference points to 
+	 * @return the feature on the Site
+	 * @deprecated use getFeaure(IProgressMonitor)
+	 */
+	public IFeature getFeature() throws CoreException {
+		return getFeature(null);
+	}
+
+	/**
+	 * Returns the feature this reference points to 
+	 *  @return the feature on the Site
+	 */
+	public IFeature getFeature(IProgressMonitor monitor) throws CoreException {
+
+		if (exactFeature != null)
+			return exactFeature;
+		exactFeature = getFeature(this,monitor);
+		return exactFeature;
+	}
+
+	/**
+	 * Returns the feature the reference points to 
+	 * @param ref the feature reference
+	 * @return the feature on the Site
+	 */
+	protected IFeature getFeature(IFeatureReference ref,IProgressMonitor monitor) throws CoreException {
+
+		IFeature feature = null;
+		URL refURL = ref.getURL();
+		feature = createFeature(refURL,monitor);
+		return feature;
+	}
+
+	/*
+	 * create an instance of a concrete feature corresponding to this reference
+	 */
+	private IFeature createFeature(URL url,IProgressMonitor monitor) throws CoreException {
+		String type = getType();
+		ISite site = getSite();
+		// if the site exists, use the site factory
+		if (site != null) {
+			return site.createFeature(type, url, monitor);
+		}
+		
+		IFeatureFactory factory = FeatureTypeFactory.getInstance().getFactory(type);
+		return factory.createFeature(url, site, monitor);
+	}
+
+	/**
+	 * Returns the update site for the referenced feature
+	 * 
+	 * @see IFeatureReference#getSite()
+	 * @since 2.0 
+	 */
+	public ISite getSite() {
+		return (ISite) getSiteModel();
+	}
+
+	/** 
+	 * Sets the feature reference URL.
+	 * This is typically performed as part of the feature reference creation
+	 * operation. Once set, the url should not be reset.
+	 * 
+	 * @see IFeatureReference#setURL(URL)
+	 * @since 2.0 
+	 */
+	public void setURL(URL url) throws CoreException {
+		if (url != null) {
+			setURLString(url.toExternalForm());
+			try {
+				resolve(url, null);
+			} catch (MalformedURLException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.FeatureReference_UnableToResolveURL, (new String[] { url.toExternalForm() })), e);
+			}
+		}
+	}
+
+	/**
+	 * Associates a site with the feature reference.
+	 * This is typically performed as part of the feature reference creation
+	 * operation. Once set, the site should not be reset.
+	 * 
+	 * @see IFeatureReference#setSite(ISite)
+	 * @since 2.0 
+	 */
+	public void setSite(ISite site) {
+		setSiteModel((SiteModel) site);
+	}
+
+	/**
+	* Returns the feature identifier.
+	* 
+	* @see IFeatureReference#getVersionedIdentifier()
+	* @since 2.0
+	*/
+	public VersionedIdentifier getVersionedIdentifier() {
+
+		if (versionId != null)
+			return versionId;
+
+		String id = getFeatureIdentifier();
+		String ver = getFeatureVersion();
+		if (id != null && ver != null) {
+			try {
+				versionId = new VersionedIdentifier(id, ver);
+				return versionId;
+			} catch (Exception e) {
+				UpdateCore.warn("Unable to create versioned identifier:" + id + ":" + ver); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
+		// we need the exact match or we may have an infinite loop
+		versionId = new VersionedIdentifier(getURL().toExternalForm(), null);
+		try {
+			versionId = getFeature(null).getVersionedIdentifier();
+		} catch (CoreException e) {
+			UpdateCore.warn("", e); //$NON-NLS-1$
+		}
+		return versionId;
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IFeatureReference#getName()
+	 */
+	public String getName() {
+		if (super.getLabel() != null)
+			return super.getLabel();
+		try {
+			return getFeature(null).getLabel();
+		} catch (CoreException e) {
+			return getVersionedIdentifier().toString();
+		}
+	}
+
+	/**
+	 * Get optional operating system specification as a comma-separated string.
+	 *
+	 * @return the operating system specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getOS() {
+		if (super.getOS() == null && getURL()!=null)
+			try {
+				return getFeature(null).getOS();
+			} catch (CoreException e) {
+				return null;
+			}
+		return super.getOS();
+	}
+
+	/**
+	 * Get optional windowing system specification as a comma-separated string.
+	 *
+	 * @return the windowing system specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getWS() {
+		if (super.getWS() == null && getURL()!=null)
+			try {
+				return getFeature(null).getWS();
+			} catch (CoreException e) {
+				return null;
+			}
+		return super.getWS();
+	}
+
+	/**
+	 * Get optional system architecture specification as a comma-separated string.
+	 *
+	 * @return the system architecture specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getOSArch() {
+		if (super.getOSArch() == null && getURL()!=null)
+			try {
+				return getFeature(null).getOSArch();
+			} catch (CoreException e) {
+				return null;
+			}
+		return super.getOSArch();
+	}
+
+	/**
+	 * Get optional locale specification as a comma-separated string.
+	 *
+	 * @return the locale specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getNL() {
+		if (super.getNL() == null && getURL()!=null)
+			try {
+				return getFeature(null).getNL();
+			} catch (CoreException e) {
+				return null;
+			}
+		return super.getNL();
+	}
+
+	/**
+	 * Returns <code>true</code> if this feature is patching another feature,
+	 * <code>false</code> otherwise
+	 * @return boolean
+	 */
+	public boolean isPatch() {
+		if (super.getPatch() == null)
+			try {
+				return getFeature(null).isPatch();
+			} catch (CoreException e) {
+				return false;
+			}
+		return "true".equalsIgnoreCase(super.getPatch()); //$NON-NLS-1$
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IArchiveReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IArchiveReference.java
new file mode 100644
index 0000000..776226c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IArchiveReference.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Site archive interface.
+ * Site archive is a representation of a packaged archive (file) located
+ * on an update site. It allows a "symbolic" path used to identify
+ * a plug-in or non-plug-in feature entry to be explicitly mapped
+ * to a specific URL. 
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.ArchiveReference
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IArchiveReference extends IAdaptable {
+
+	/** 
+	 * 
+	 * @return the archive "symbolic" path, or <code>null</code>
+	 * @since 2.0 
+	 */
+	public String getPath();
+
+	/**
+	 * Retrieve the site archive URL 
+	 * 
+	 * @return the archive URL, or <code>null</code>
+	 * @since 2.0 
+	 */
+	public URL getURL();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ICategory.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ICategory.java
new file mode 100644
index 0000000..c3ac959
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ICategory.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Feature category definition.
+ * A site can organize its features into categories. Categories
+ * can be further organized into hierarchies. Each category name
+ * is a composed of the name of its parent and a simple identifier
+ * separated by a slash ("/"). For example <code>tools/utilities/print</code>
+ * defines a category that is a child of <code>tools/utilities</code> and
+ * grandchild of <code>tools</code>.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.Category
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ICategory  extends IAdaptable{
+
+	/** 
+	 * Retrieve the name of the category. The name can be a simple
+	 * token (root category) or a number of slash-separated ("/") 
+	 * tokens.
+	 * 
+	 * @return the category name
+	 * @since 2.0 
+	 */
+	public String getName();
+
+	/**
+	 * Retrieve the displayable label for the category
+	 * 
+	 * @return displayable category label, or <code>null</code>
+	 * @since 2.0 
+	 */
+	public String getLabel();
+
+	/** 
+	 * Retrieve the detailed category description
+	 * 
+	 * @return category description, or <code>null</code>
+	 * @since 2.0 
+	 */
+	public IURLEntry getDescription();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IContentConsumer.java
new file mode 100644
index 0000000..74fc952
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IContentConsumer.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Generic content consumer.
+ * A generic content consumer is used to store plug-in and non-plug-in files
+ * for a feature.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients 
+ * will only use the content consumer provided by the feature type(s)
+ * implemented by the platform.
+ * </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>
+ * @see org.eclipse.update.core.IFeatureContentConsumer
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IContentConsumer {
+
+	/**
+	 * Stores a file.
+	 * 
+	 * @see IFeatureContentConsumer#open(IPluginEntry)
+	 * @see IFeatureContentConsumer#open(INonPluginEntry)
+	 * @param contentReference reference to the file to store
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Indicates successful completion of the store operations for this
+	 * consumer
+	 * 
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public void close() throws CoreException;
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeature.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeature.java
new file mode 100644
index 0000000..8af1af9
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeature.java
@@ -0,0 +1,483 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.core.model.InstallAbortedException;
+
+/**
+ * Feature defines the packaging "container" for a group of related plug-ins,
+ * plug-in fragments, and optionally non-plug-in files. 
+ * <p>
+ * Features are treated purely as an installation and packaging construct. 
+ * They do not play a role during Eclipse plug-in execution.
+ * They are simply an inclusive "manifest" of the plug-ins, fragments 
+ * and other files that make up that feature. If features are logically made 
+ * up of plug-ins from "sub-features", the top-level feature "manifest"
+ * must be fully resolved at packaging time.
+ * </p>
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.Feature
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IFeature extends IAdaptable, IPlatformEnvironment {
+
+	/**
+	 * Indicates a 'happy' feature
+	 * A feature is considered to be 'happy' in the context of a local site
+	 * if all the plug-ins referenced by the feature are installed on the site and no other
+	 * version of any of the plug-ins are installed on any other site of the local site.
+	 * 
+	 * @see org.eclipse.update.configuration.IConfiguredSite#getBrokenStatus(IFeature)
+	 * @since 2.0
+	 */
+	public static final int STATUS_HAPPY = 0;
+
+	/**
+	 * Indicates a 'happy' feature
+	 * A feature is considered to be 'ambiguous' in the context of a local site
+	 * if all the plug-ins referenced by the feature are installed on the site and other
+	 * version of any of the plug-ins are installed on any other site of the local site.
+	 * 
+	 * @see org.eclipse.update.configuration.IConfiguredSite#getBrokenStatus(IFeature)
+	 */	
+	public static final int STATUS_AMBIGUOUS = 1;
+	
+	/**
+	 * Indicates an 'unhappy' feature
+	 * A feature is considered to be 'unhappy' in the context of this site,
+	 * if some of the plug-ins referenced by the feature are not installed on this site.
+	 * 
+	 * @see org.eclipse.update.configuration.IConfiguredSite#getBrokenStatus(IFeature)
+	 * @since 2.0
+	 */	
+	public static final int STATUS_UNHAPPY = 2;
+	
+	
+	/**
+	 * Indicates a disable feature
+	 * 
+	 * @see org.eclipse.update.configuration.IConfiguredSite#getBrokenStatus(IFeature)
+	 * @since 2.0.2
+	 */	
+	public static final int STATUS_DISABLED = -1;
+		
+	/**
+	 * Indicates the one-click update will search the 
+	 * location of the nesting root feature.
+	 * 
+	 * @since 2.0.1
+	 */	
+	public static final int SEARCH_LOCATION_DEFAULT = 0;	
+
+	/**
+	 * Indicates the one-click update will search the 
+	 * location defined by the feature.
+	 * 
+	 * @since 2.0.1
+	 */	
+	public static final int SEARCH_LOCATION_FEATURE = 1;	
+
+	/**
+	 * Indicates the one-click update will search both the 
+	 * location of the nesting root feature and the 
+	 * location defined by the feature.
+	 * 
+	 * @since 2.0.1
+	 */	
+	public static final int SEARCH_LOCATION_BOTH = 2;	
+
+	
+	/**
+	 * Returns the feature identifier.
+	 * 
+	 * @return the feature identifier.
+	 * @since 2.0 
+	 */
+	public VersionedIdentifier getVersionedIdentifier();
+
+	/**
+	 * Returns the site this feature is associated with.
+	 * 
+	 * @return the site for this feature
+	 * @since 2.0 
+	 */
+	public ISite getSite();
+
+	/**
+	 * Returns the displayable label of the feature.
+	 * 
+	 * @return feature label, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getLabel();
+
+	/**
+	 * Returns the feature URL.
+	 * This is the URL that was used to create the feature. The interpretation
+	 * of the URL is dependent on the concrete feature implementation.  * 
+	 * @return feature URL
+	 * @since 2.0 
+	 */
+	public URL getURL();
+
+	/**
+	 * Returns an information entry referencing the location of the
+	 * feature update site. The update site can be accessed to obtain
+	 * feature updates for this feature.
+	 * 
+	 * @return update site entry, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public IURLEntry getUpdateSiteEntry();
+
+	/**
+	 * Return an array of information entries referencing locations of other
+	 * update sites. This mechanism can be used by features to distribute
+	 * location information about general update sites to clients.
+	 * 
+	 * @return an array of site entries, or an empty array.
+	 * @since 2.0 
+	 */
+	public IURLEntry[] getDiscoverySiteEntries();
+
+	/**
+	 * Returns a displayable label identifying the provider of this feature
+	 * 
+	 * @return provider label, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getProvider();
+
+	/**
+	 * Returns and optional custom install handler entry.
+	 * 
+	 * @return install handler entry, or <code>null</code> if
+	 * none was specified
+	 * @since 2.0
+	 */
+	public IInstallHandlerEntry getInstallHandlerEntry();
+
+	/**
+	 * Returns the feature description.
+	 * 
+	 * @return feature description, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public IURLEntry getDescription();
+
+	/**
+	 * Returns the copyright information for the feature.
+	 * 
+	 * @return copyright information, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public IURLEntry getCopyright();
+
+	/**
+	 * Returns the license information for the feature.
+	 * 
+	 * @return feature license, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public IURLEntry getLicense();
+
+	/**
+	 * Return optional image for the feature.
+	 * 
+	 * @return the URL pointing to the image, , or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public URL getImage();
+
+	/**
+	 * Return a list of plug-in dependencies for this feature. A plug-in
+	 * dependency is a reference to a plug-in required for feature execution
+	 * that is not packaged as part of the feature.
+	 * filtered by the operating system, windowing system and architecture system
+	 * set in <code>SiteManager</code>
+	 * 
+	 * @return the list of required plug-in dependencies, or an empty array.
+	 * @since 2.0 
+	 */
+	public IImport[] getImports();
+
+	/**
+	 * Return a list of plug-in dependencies for this feature. A plug-in
+	 * dependency is a reference to a plug-in required for feature execution
+	 * that is not packaged as part of the feature.
+ 	 * No filtering occurs
+	 * 
+	 * @return the list of required plug-in dependencies, or an empty array.
+	 * @since 2.1
+	 */
+	public IImport[] getRawImports();
+	
+	/**
+	 * Return the identifier of the primary plugin associated to this feature
+	 * or <code>null</code> if the feature is not a primary feature.
+	 * If the primary plugin id is not specified and the feature is a primary
+	 * feature, returns the feature identifier.
+	 * 
+	 * @return the identifier of the associated primary plugin or <code>null</code>
+	 * @since 2.1 
+	 */
+	public String getPrimaryPluginID();
+
+
+	/**
+	 * Install the contents of this feature into the specified target feature.
+	 * All optional features will be installed
+	 * 
+	 * @param targetFeature
+	 * @param verificationListener
+	 * @param monitor
+	 * @exception InstallAbortedException when the user cancels the install
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public IFeatureReference install(
+		IFeature targetFeature,
+		IVerificationListener verificationListener,
+		IProgressMonitor monitor)
+		throws InstallAbortedException,CoreException;
+
+	/**
+	 * Install the contents of this feature into the specified target feature.
+	 * Only the listed optional features will be installed.
+	 * 
+	 * @param targetFeature
+	 * @param optionalFeatures the optional features to be installed
+	 * @param verificationListener
+	 * @param monitor
+	 * @exception InstallAbortedException when the user cancels the install
+	 * @exception CoreException
+	 * @since 2.0.1
+	 */
+	public IFeatureReference install(
+		IFeature targetFeature,
+		IFeatureReference[] optionalFeatures,
+		IVerificationListener verificationListener,
+		IProgressMonitor monitor)
+		throws InstallAbortedException,CoreException;
+
+	/**
+	 * Returns an array of feature references included by this feature
+	 * filtered by the operating system, windowing system and architecture system
+	 * set in <code>SiteManager</code>
+	 * 
+	 * @return an array of feature references, or an empty array.
+	 * @since 2.0
+	 */
+	public IIncludedFeatureReference[] getIncludedFeatureReferences() throws CoreException;
+
+	/**
+	 * Returns an array of feature references included by this feature
+	 * No filtering occurs
+	 * 
+	 * @return an array of feature references, or an empty array.
+	 * @since 2.0
+	 */
+	public IIncludedFeatureReference[] getRawIncludedFeatureReferences() throws CoreException;
+
+	/**
+	 * Returns an array of plug-in entries referenced by this feature
+	 * filtered by the operating system, windowing system and architecture system
+	 * set in <code>SiteManager</code>
+	 * 
+	 * @return an array of plug-in entries, or an empty array.
+	 * @since 2.0
+	 */
+	public IPluginEntry[] getPluginEntries();
+
+	/**
+	 * Returns an array of plug-in entries referenced by this feature
+	 * No filtering occurs
+	 * 
+	 * @return an array of plug-in entries, or an empty array.
+	 * @since 2.1
+	 */
+	public IPluginEntry[] getRawPluginEntries();
+
+	/**
+	 * Returns the count of referenced plug-in entries.
+	 * 
+	 * @return plug-in entry count
+	 * @since 2.0
+	 */
+	public int getPluginEntryCount();
+
+	/**
+	 * Returns an array of non-plug-in entries referenced by this feature
+	 * filtered by the operating system, windowing system and architecture system
+	 * set in <code>SiteManager</code>
+	 * 
+	 * @return an array of non-plug-in entries, or an empty array.
+	 * @since 2.0
+	 */
+	public INonPluginEntry[] getNonPluginEntries();
+
+	/**
+	 * Returns an array of non-plug-in entries referenced by this feature
+	 * No filtering occurs
+	 * 
+	 * @return an array of non-plug-in entries, or an empty array.
+	 * @since 2.1
+	 */
+	public INonPluginEntry[] getRawNonPluginEntries();
+
+	/**
+	 * Returns the count of referenced non-plug-in entries.
+	 * 
+	 * @return non-plug-in entry count
+	 * @since 2.0
+	 */
+	public int getNonPluginEntryCount();
+
+	/**
+	 * Returns the download size of the feature, if it can be determined.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @return download size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getDownloadSize();
+
+	/**
+	 * Returns the install size of the feature, if it can be determined.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @return install size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getInstallSize();
+
+	/**
+	 * Indicates whether the feature can be used as a primary feature.
+	 * 
+	 * @return <code>true</code> if this is a primary feature, 
+	 * otherwise <code>false</code>
+	 * @since 2.0 
+	 */
+	public boolean isPrimary();
+	
+	/**
+	 * Indicates whether the feature must be processed alone during installation
+	 * and configuration. Features that are not exclusive can be installed in a
+	 * batch.
+	 * 
+	 * @return <code>true</code> if feature requires exclusive processing,
+	 * <code>false</code> otherwise.
+	 * @since 2.1
+	 */
+	public boolean isExclusive();
+
+	/**
+	 * Returns an optional identifier of an application to be used when
+	 * starting up the platform with this feature as the primary feature.
+	 * The application identifier must represent a valid application registered
+	 * in the <code>org.eclipse.core.runtime.applications</code> extension point.
+	 * 
+	 * @return application identifier, or <code>null</code>
+	 * @since 2.0 
+	 */
+	public String getApplication();
+	
+	/**
+	 * Returns an optional identifier of a co-location affinity feature.
+	 * 
+	 * @return feature identifier, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getAffinityFeature();
+
+	/**
+	 * Returns the content provider for this feature. A content provider
+	 * is an abstraction of each feature internal packaging structure.
+	 * It allows the feature content to be accessed in a standard way
+	 * regardless of the internal packaging. All concrete features
+	 * need to be able to return a content provider.
+	 * 
+	 * @return feature content provider
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public IFeatureContentProvider getFeatureContentProvider()
+		throws CoreException;
+
+	/**
+	 * Returns the content consumer for this feature. A content consumer
+	 * is an abstraction of each feature internal packaging mechanism.
+	 * It allows content to be stored into a feature in a standard way
+	 * regardless of the packaging mechanism used. Only concrete features
+	 * that support storing need to implement a content consumer. The platform
+	 * implements at least one feature type supporting content consumer.
+	 * This is the feature type representing a locally-installed
+	 * feature.
+	 * 
+	 * @return feature content consumer
+	 * @exception CoreException
+	 * @exception UnsupportedOperationException
+	 * @since 2.0
+	 */
+	public IFeatureContentConsumer getFeatureContentConsumer()
+		throws CoreException;
+
+	/**
+	 * Sets the site for this feature. This is typically performed as part
+	 * of the feature creation operation. Once set, the site
+	 * should not be reset.
+	 * 
+	 * @param site the site
+	 * @throws CoreException site for this feature is already set
+	 * @since 2.0 
+	 */
+	public void setSite(ISite site) throws CoreException;
+
+	/**
+	 * Sets the content provider for this feature. This is typically
+	 * performed as part of the feature creation operation. Once set, the 
+	 * provider should not be reset.
+	 * 
+	 * @param featureContentProvider content provider
+	 * @since 2.0
+	 */
+	public void setFeatureContentProvider(IFeatureContentProvider featureContentProvider);
+
+	/**
+	 * Returns <code>true</code> if this feature is patching another feature,
+	 * <code>false</code> otherwise
+	 * @return boolean
+	 * @since 2.1
+	 */
+	public boolean isPatch();
+
+
+}
\ No newline at end of file
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureContentConsumer.java
new file mode 100644
index 0000000..116d3dd
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureContentConsumer.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Feature content consumer.
+ * A feature content consumer is an abstraction of each feature internal
+ * packaging mechanism. It allows content to be stored into a feature in
+ * a standard way regardless of the packaging mechanism used. Only concrete
+ * features that support storing need to implement a content consumer. 
+ * The platform implements at least one feature type supporting content
+ * consumer. This is the feature type representing a locally-installed
+ * feature.
+ * <p>
+ * A feature content consumer delegates the storage of plug-in and 
+ * non-plug-in files to a generic content consumer.
+ * </p>
+ * <p>
+ * Clients may implement this interface. However, in most cases clients 
+ * will only use the feature content consumer provided by the feature type(s)
+ * implemented by the platform.
+ * </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>
+ * @see org.eclipse.update.core.IContentConsumer
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IFeatureContentConsumer {
+
+	
+	/**
+	 * Store a feature file.
+	 * Note that only the feature definition files should be stored using
+	 * this method. Plug-in files and non-plug-in data files should be
+	 * stored using the content consumers corresponding to their respective
+	 * entries.
+	 * 
+	 * @see #open(IPluginEntry)
+	 * @see #open(INonPluginEntry)	 
+	 * @param contentReference content reference to feature file
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Opens a generic content consumer for the specified plug-in entry.
+	 * Plug-in files corresponding to this entry should be stored
+	 * using this content consumer.
+	 * 
+	 * @param pluginEntry plug-in entry
+	 * @return generic content consumer for the entry
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public IContentConsumer open(IPluginEntry pluginEntry) throws CoreException;
+
+	/**
+	 * Opens a generic content consumer for the specified non-plug-in entry.
+	 * Non-plug-in files corresponding to this entry should be stored
+	 * using this content consumer.
+	 * 
+	 * @param nonPluginEntry non-plug-in entry
+	 * @return generic content consumer for the entry
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public IContentConsumer open(INonPluginEntry nonPluginEntry)
+		throws CoreException;
+
+	/**
+	 * Closes this content consumer. This indicates a successful completion
+	 * of the store operations. The content consumer commits any changes
+	 * made by this consumer.
+	 * 
+	 * @return reference to the newly populated feature
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public IFeatureReference close() throws CoreException;
+
+	/**
+	 * Closes this content consumer, indicating the store operations should
+	 * be aborted. The content consumer attempts to back out any changes
+	 * made by this content consumer.
+	 * 
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public void abort() throws CoreException;
+
+	/**
+	 * Sets the feature for this content consumer.
+	 * In general, this method should only be called as part of
+	 * feature creation. Once set, the feature should not be reset.
+	 * 
+	 * @param feature feature for this content consumer
+	 * @since 2.0
+	 */
+	public void setFeature(IFeature feature);
+
+	/**
+	 * Returns the feature for this content consumer.
+	 *
+	 * @return the feature for this content consumer
+	 * @since 2.0
+	 */
+	public IFeature getFeature();
+
+	/**
+	 * Sets the parent for this content consumer.
+	 * In general, this method should only be called as part of
+	 * feature creation. Once set, the feature should not be reset.
+	 * 
+	 * @param parent parent feature content consumer.
+	 * @since 2.0 
+	 */
+	public void setParent(IFeatureContentConsumer parent);
+
+	/**
+	 * Returns the feature content consumer that opened
+	 * this feature content consumer, or <code>null</code>
+	 * if this feature content consumer is a root feature
+	 * content consumer.
+	 *
+	 * @return the parent feature content consumer, or null.
+	 * @since 2.0
+	 */
+	public IFeatureContentConsumer getParent();
+
+	/**
+	 * Link the content consumer of the feature as a child
+	 * content consumer
+	 * 
+	 * @param feature the child feature.
+	 * @throws CoreException 
+	 * @since 2.0 
+	 */
+	public void addChild(IFeature feature) throws CoreException;
+
+	/**
+	 * Returns the feature content consumers that
+	 * this feature content consumer opened
+	 *
+	 * @return an array of feature content consumer, or en empty array.
+	 * @since 2.0
+	 */
+	public IFeatureContentConsumer[] getChildren();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureContentProvider.java
new file mode 100644
index 0000000..d09f6f1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureContentProvider.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Feature content provider.
+ * A feature content provider is an abstraction of each feature internal 
+ * packaging structure. It allows the feature content to be accessed in
+ * a standard way regardless of the internal packaging. All concrete feature
+ * implementations need to implement a feature content provider.
+ * <p>
+ * There are two ways of looking at a feature content:
+ * <ol>
+ * <li>the "logical" view, which is a representation of the actual files that
+ * make up the feature. These include any files that describe the feature
+ * itself, files that are the actual implementation of referenced plug-ins,
+ * and files that are the non-plug-in data files associated with the feature
+ * <li>the "packaged" view, which is a set of related archive files that
+ * contain the "logical" files.
+ * </ol>
+ * It is the responsibility of a feature content provider to manage the
+ * mapping between the "packaged" and "logical" views.
+ * </p>
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.FeatureContentProvider
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IFeatureContentProvider {
+
+	/**
+	 * Returns the feature url. 
+	 * The exact interpretation of this URL is specific to each content
+	 * provider. Typically, the URL is a reference to a file that can be 
+	 * used directly, or indirectly, to determine the content of the feature.
+	 * 
+	 * @return feature url
+	 * @since 2.0
+	 */
+	public URL getURL();
+
+	/**
+	 * Returns a content reference to the feature manifest. The feature manifest
+	 * is an xml file, whose format is specified by the platform. Typically
+	 * a feature will contain the manifest as one of the packaged files.
+	 * For features that do not contain the manifest, or contain a manifest
+	 * that does not follow the specified format, this method returns
+	 * a reference to a computed manifest in the appropriate platform
+	 * format.
+	 * 
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return feature manifest reference, or <code>null</code> if the manifest cannot be found.
+	 * @since 2.0
+	 */
+	public ContentReference getFeatureManifestReference(InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Returns an array of content references of all the "packaged"
+	 * archives that make up this feature. 
+	 * <p>
+	 * The number of returned references is dependent on each feature 
+	 * content provider (i.e is dependent on the packaging mechanism used
+	 * by the particular feature type).
+	 * </p>
+	 * 
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return an array of references, or an empty array if no references
+	 * are found
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public ContentReference[] getArchiveReferences(InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Returns an array of content references of the "packaged"
+	 * archives that contain the feature descriptive information.
+	 * <p>
+	 * In general, the feature descriptive information should be packaged
+	 * separately from the "bulk" of the actual feature content.
+	 * The feature entry archive(s) must be downloaded from an update
+	 * site in order to present information about the feature to the
+	 * client. Consequently, keeping the number and size of the feature
+	 * entry archive(s) to a minimum will speed up the responsiveness of the
+	 * user interface.
+	 * </p>
+	 * <p>
+	 * The number of returned references is dependent on each feature
+	 * content provider (i.e is dependent on the packaging mechanism used
+	 * by the particular feature type).
+	 * </p>
+	 * 
+	 * @see IFeatureContentProvider#getFeatureEntryContentReferences(InstallMonitor)
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return an array of references, or an empty array if no references
+	 * are found
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public ContentReference[] getFeatureEntryArchiveReferences(InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Returns an array of content references of the "packaged"
+	 * archives that contain the files for the specified plug-in entry.
+	 * <p>
+	 * The number of returned references is dependent on each feature
+	 * content provider (i.e is dependent on the packaging mechanism used
+	 * by the particular feature type).
+	 * </p>
+	 * 
+	 * @see IFeatureContentProvider#getPluginEntryContentReferences(IPluginEntry, InstallMonitor)
+	 * @param pluginEntry plug-in entry
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return an array of references, or an empty array if no references
+	 * are found
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public ContentReference[] getPluginEntryArchiveReferences(
+		IPluginEntry pluginEntry,
+		InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Returns an array of content references of the "packaged"
+	 * archives that contain the files for the specified non-plug-in entry.
+	 * <p>
+	 * The number of returned references is dependent on each feature
+	 * content provider (i.e is dependent on the packaging mechanism used
+	 * by the particular feature type).
+	 * </p>
+	 * <p>
+	 * Note, that the platform does not interpret non-plug-in entries in any 
+	 * way, other that performing any required downloads. Non-plug-in entries
+	 * are handled by custom install handlers that must be specified for
+	 * the feature. Consequently, this interface does not make a distinction
+	 * between the "logical" and "packaged" views for non-plug-in entries.
+	 * The "packaged" view (returning references to the non-plug-in archives)
+	 * is the only one supported. It is the responsibility of the custom install
+	 * handler to understand the "logical" view of non-plug-in archives.
+	 * </p>
+	 * 
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return an array of references, or an empty array if no references
+	 * are found
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public ContentReference[] getNonPluginEntryArchiveReferences(
+		INonPluginEntry nonPluginEntry,
+		InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Returns an array of content references to the feature definition files
+	 * (i.e the "logical" view of the files defining the feature). These
+	 * are the files required to present information about the feature to the
+	 * client, and in general, should not contain references to plug-in and 
+	 * non-plug-in files.
+	 * 
+	 * @see IFeatureContentProvider#getFeatureEntryArchiveReferences(InstallMonitor)
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return an array of ContentReference or an empty array if no references are found
+	 * @exception CoreException when an error occurs
+	 * @since 2.0 
+	 */
+	public ContentReference[] getFeatureEntryContentReferences(InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Returns an array of content references to the files implementing
+	 * the specified plug-in. (i.e the "logical" view of the plug-in).
+	 * 
+	 * @see IFeatureContentProvider#getPluginEntryArchiveReferences(IPluginEntry, InstallMonitor)
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return an array of ContentReference or an empty array if no references are found
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public ContentReference[] getPluginEntryContentReferences(
+		IPluginEntry pluginEntry,
+		InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Returns the total size of all archives required for the
+	 * specified plug-in and non-plug-in entries (the "packaging" view).
+	 * 
+	 * @param pluginEntries an array of plug-in entries
+	 * @param nonPluginEntries an array of non-plug-in entries
+	 * @return total download size, or an indication that size could not be
+	 * determined
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @since 2.0
+	 */
+	public long getDownloadSizeFor(
+		IPluginEntry[] pluginEntries,
+		INonPluginEntry[] nonPluginEntries);
+
+	/**
+	 * Returns the total size of all files required for the
+	 * specified plug-in and non-plug-in entries (the "logical" view).
+	 * 
+	 * @param pluginEntries an array of plug-in entries
+	 * @param nonPluginEntries an array of non-plug-in entries
+	 * @return total download size, or an indication that size could not be
+	 * determined
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @since 2.0
+	 */
+	public long getInstallSizeFor(
+		IPluginEntry[] pluginEntries,
+		INonPluginEntry[] nonPluginEntries);
+
+	/**
+	 * Returns the verifier for this feature.
+	 * If provided, the verifier is called at various point during
+	 * installation processing to verify downloaded archives. The
+	 * type of verification provided is dependent on the content
+	 * provider implementation.
+	 * 
+	 * @return verifier
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public IVerifier getVerifier() throws CoreException;
+
+	/**
+	 * Returns the feature associated with this content provider.
+	 * 
+	 * @return feature for this content provider
+	 * @since 2.0
+	 */
+	public IFeature getFeature();
+
+	/**
+	 * Sets the feature associated with this content provider.
+	 * In general, this method should only be called as part of
+	 * feature creation. Once set, the feature should not be reset.
+	 * 
+	 * @param feature feature for this content provider
+	 * @since 2.0
+	 */
+	public void setFeature(IFeature feature);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureFactory.java
new file mode 100644
index 0000000..7e0736d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureFactory.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Feature factory interface.
+ * A feature factory is used to construct new instances of concrete
+ * features. 
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.BaseFeatureFactory
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IFeatureFactory {
+
+	/**
+	 * Returns a feature defined by the supplied URL. The feature
+	 * is associated with the specified site.
+	 * <p>
+	 * The actual interpretation of the URL is feature-type specific.
+	 * In most cases the URL will point to some feature-specific
+	 * file that can be used (directly or indirectly) to construct
+	 * the feature object.
+	 * </p>
+	 * @param url URL interpreted by the feature
+	 * @param site site to be associated with the feature
+	 * @return concrete feature object
+	 * @exception CoreException
+	 * @deprecated use createFeature(URL, ISite, IProgressMonitor) instead
+	 * @since 2.0 
+	 */
+	public IFeature createFeature(URL url, ISite site) throws CoreException;
+	
+	/**
+	 * Returns a feature defined by the supplied URL. The feature
+	 * is associated with the specified site.
+	 * <p>
+	 * The actual interpretation of the URL is feature-type specific.
+	 * In most cases the URL will point to some feature-specific
+	 * file that can be used (directly or indirectly) to construct
+	 * the feature object.
+	 * </p>
+	 * @param url URL interpreted by the feature
+	 * @param site site to be associated with the feature
+	 * @return concrete feature object
+	 * @exception CoreException
+	 * @since 2.1 
+	 */
+	public IFeature createFeature(URL url, ISite site, IProgressMonitor monitor) throws CoreException;	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureReference.java
new file mode 100644
index 0000000..e8bd3a3
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IFeatureReference.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Feature reference.
+ * A reference to a feature.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.FeatureReference
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IFeatureReference extends IAdaptable,IPlatformEnvironment {
+
+	/**
+	 * Returns the referenced feature URL.
+	 * 
+	 * @return feature URL 
+	 * @since 2.0 
+	 */
+	public URL getURL();
+
+	/**
+	 * Returns the update site for the referenced feature
+	 * 
+	 * @return feature site
+	 * @since 2.0 
+	 */
+	public ISite getSite();
+
+	/**
+	 * Returns the label for the referenced feature
+	 *
+	 * @return the label
+	 * @since 2.1
+	 */
+	public String getName();
+
+
+	/**
+	 * Returns the referenced feature.
+	 * This is a factory method that creates the full feature object.
+	 * 
+	 * @return the referenced feature
+	 * @deprecated use getFeature(IProgressMonitor) instead
+	 * @since 2.0 
+	 */
+	public IFeature getFeature() throws CoreException;
+
+	/**
+	 * Returns the referenced feature.
+	 * This is a factory method that creates the full feature object.
+	 * 
+	 * @param monitor the progress monitor
+	 * @return the referenced feature
+	 * @since 2.1 
+	 */
+	public IFeature getFeature(IProgressMonitor monitor) throws CoreException;
+
+
+	/**
+	 * Returns the feature identifier.
+	 * 
+	 * @return the feature identifier.
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public VersionedIdentifier getVersionedIdentifier() throws CoreException;
+
+	/**
+	 * Sets the feature reference URL.
+	 * This is typically performed as part of the feature reference creation
+	 * operation. Once set, the url should not be reset.
+	 * 
+	 * @param url reference URL
+	 * @since 2.0 
+	 */
+	public void setURL(URL url) throws CoreException;
+
+	/**
+	 * Associates a site with the feature reference.
+	 * This is typically performed as part of the feature reference creation
+	 * operation. Once set, the site should not be reset.
+	 * 
+	 * @param site site for the feature reference
+	 * @since 2.0 
+	 */
+	public void setSite(ISite site);
+	
+	/**
+	 * Returns <code>true</code> if this feature is patching another feature,
+	 * <code>false</code> otherwise
+	 * @return boolean
+	 * @since 2.1
+	 */
+	public boolean isPatch();
+	
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IImport.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IImport.java
new file mode 100644
index 0000000..4dc6c70
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IImport.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Plug-in dependency entry.
+ * Describes a feature dependency on a particular plug-in. The dependency 
+ * can specify a specific plug-in version and a matching rule for 
+ * satisfying the dependency.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.Import
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IImport extends IAdaptable, IUpdateConstants, IPlatformEnvironment {
+
+	/**
+	 * The import relates to a plugin
+	 * @since 2.0.2
+	 */
+	public static final int KIND_PLUGIN = 0;
+	
+	/**
+	 * The import relates to a feature
+	 * @since 2.0.2
+	 */
+	public static final int KIND_FEATURE = 1;
+
+	/** 
+	 * Returns an identifier of the dependent plug-in.
+	 * 
+	 * @return plug-in identifier
+	 * @since 2.0 
+	 */
+	public VersionedIdentifier getVersionedIdentifier();
+
+	/**
+	 * Returns the matching rule for the dependency.
+	 * 
+	 * @return matching rule
+	 * @since 2.0 
+	 */
+	public int getRule();
+	
+	/**
+	 * Returns the matching rule for the dependency identifier.
+	 * 
+	 * @return matching id rule
+	 * @since 2.1 
+	 */
+	public int getIdRule();
+	
+	/**
+	 * Returns the dependency kind
+	 * 
+	 * @see #KIND_PLUGIN
+	 * @see #KIND_FEATURE
+	 * @return KIND_PLUGIN if the dependency relates to a plugin, 
+	 * KIND_FEATURE if the dependency relates to a feature.
+	 */
+	public int getKind();
+	
+	/**
+	 * Returns the patch mode. If the import is in patch mode,
+	 * the referenced feature is considered a patch target,
+	 * and the feature that owns the import is patch carrier.
+	 * Patch carrier and patched feature are linked in a
+	 * distinct way: if a patched feature is disabled,
+	 * all the patches are disabled with it.
+	 * @return true if the element represents a patch
+	 * reference, false otherwise.
+	 */
+	public boolean isPatch();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IIncludedFeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IIncludedFeatureReference.java
new file mode 100644
index 0000000..7511a62
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IIncludedFeatureReference.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.configuration.IConfiguredSite;
+
+
+
+/**
+ * Included Feature reference.
+ * A reference to a included feature.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.FeatureReference
+ * @since 2.0.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IIncludedFeatureReference extends IFeatureReference, IAdaptable {
+
+	/**
+	 * Returns the referenced feature.
+	 * This is a factory method that creates the full feature object.
+	 * equivalent to getFeature(false,null);
+	 * 
+	 * @return the referenced feature
+	 * @deprecated use getFeature(IProgressMonitor)
+	 * @since 2.0 
+	 */
+	public IFeature getFeature() throws CoreException;
+
+	/**
+	 * Returns the referenced feature.
+	 * This is a factory method that creates the full feature object.
+	 * 
+	 * @param perfectMatch <code>true</code> if the perfect match feature feature should be returned
+	 * <code>false</code> if the best match feature should be returned.
+	 * @param configuredSite the configured site to search for the Feature. If 
+	 * the configured site is <code>null</code> the search will be done in the current configured site.
+	 * @return the referenced feature
+	 * instead
+	 * @since 2.0.2
+	 * <b>Note:</b> This method 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.
+	 * @deprecated use getFeature(IProgressMonitor)
+	 */
+	public IFeature getFeature(boolean perfectMatch,IConfiguredSite configuredSite) throws CoreException;
+
+	/**
+	 * Returns the referenced feature.
+	 * This is a factory method that creates the full feature object.
+	 * 
+	 * @param perfectMatch <code>true</code> if the perfect match feature feature should be returned
+	 * <code>false</code> if the best match feature should be returned.
+	 * @param configuredSite the configured site to search for the Feature. If 
+	 * the configured site is <code>null</code> the search will be done in the current configured site.
+	 * @param monitor the progress monitor
+	 * @return the referenced feature
+	 * @since 2.1
+	 * <b>Note:</b> This method 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.
+	 * @deprecated use getFeature(IProgressMonitor)
+	 */
+	public IFeature getFeature(boolean perfectMatch,IConfiguredSite configuredSite, IProgressMonitor monitor) throws CoreException;
+
+
+	/**
+	 * Returns <code>true</code> if the feature is optional, <code>false</code> otherwise.
+	 * 
+	 * @return boolean
+	 * @since 2.0.1
+	 */
+	public boolean isOptional();
+
+	/**
+	 * Returns the matching rule for this included feature.
+	 * The rule will determine the ability of the included feature to move version 
+	 * without causing the overall feature to appear broken.
+	 * 
+	 * The default is <code>RULE_PERFECT</code>
+	 * 
+	 * @see IUpdateConstants#RULE_PERFECT
+	 * @see IUpdateConstants#RULE_EQUIVALENT
+	 * @see IUpdateConstants#RULE_COMPATIBLE
+	 * @see IUpdateConstants#RULE_GREATER_OR_EQUAL
+	 * @return int representation of feature matching rule.
+	 * @since 2.0.2
+	 * @deprecated since 3.0 included feature version is exactly specified
+	 */
+	public int getMatch();
+	
+	/**
+	 * Returns the search location for this included feature.
+	 * The location will be used to search updates for this feature.
+	 * 
+	 * The default is <code>SEARCH_ROOT</code>
+	 * 
+	 * @see IUpdateConstants#SEARCH_ROOT
+	 * @see IUpdateConstants#SEARCH_SELF
+	 * @return int representation of feature searching rule.
+	 * @since 2.0.2
+	 */
+
+	public int getSearchLocation();
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandler.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandler.java
new file mode 100644
index 0000000..a18790a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandler.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Custom install handler.
+ * Custom install handlers can optionally be associated with a feature.
+ * The actual install handler implementation can be physically delivered
+ * as part of the feature package, or can already be installed on the client
+ * machine and registered via the <code>org.eclipse.update.core.installHandlers</code>
+ * extension point. The install handler methods are called at predetermined
+ * point during update actions.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly subclass the provided implementation of this interface.
+ * </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>
+ * @see org.eclipse.update.core.BaseInstallHandler
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IInstallHandler {
+
+	/**
+	 * Indicates the handler is being initialized for feature install.
+	 * @since 2.0
+	 */
+	public static final int HANDLER_ACTION_INSTALL = 1;
+
+	/**
+	 * Indicates the handler is being initialized for feature configure.
+	 * @since 2.0
+	 */
+	public static final int HANDLER_ACTION_CONFIGURE = 2;
+
+	/**
+	 * Indicates the handler is being initialized for feature unconfigure.
+	 * @since 2.0
+	 */
+	public static final int HANDLER_ACTION_UNCONFIGURE = 3;
+
+	/**
+	 * Indicates the handler is being initialized for feature uninstall.
+	 * @since 2.0
+	 */
+	public static final int HANDLER_ACTION_UNINSTALL = 4;
+
+	/**
+	 * Initialize the install handler.
+	 * Install handlers are always constructed using the default constructor.
+	 * The are initialized immediately following construction.
+	 * 
+	 * @param type update action type
+	 * @param feature the target of the action
+	 * @param entry model entry that defines this handler
+	 * @param monitor optional progress monitor, can be <code>null</code>
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public void initialize(
+		int type,
+		IFeature feature,
+		IInstallHandlerEntry entry,
+		InstallMonitor monitor)
+		throws CoreException;
+
+	/**
+	 * Called at the start of the install action. At this point, no install
+	 * processing has taken place.
+	 * 
+	 * @see #HANDLER_ACTION_INSTALL
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void installInitiated() throws CoreException;
+
+	/**
+	 * Called after files corresponding to plug-in entries have been downloaded,
+	 * but before they are actully unpacked and installed.
+	 * 
+	 * @see #HANDLER_ACTION_INSTALL
+	 * @param plugins downloaded plug-in entries. Note this may be a subset
+	 * of the plug-ins actually references by the feature.
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void pluginsDownloaded(IPluginEntry[] plugins) throws CoreException;
+
+	/**
+	 * Called after files corresponding to non-plug-in entries have been 
+	 * downloaded. The custom install handler can perform any custom
+	 * verification of the non-plug-in entries (these are not interpreted
+	 * in any way by the platform (beyond downloading)).
+	 * 
+	 * @see #HANDLER_ACTION_INSTALL
+	 * @param nonPluginData downloaded non-plug-in entries.
+	 * @param listener verification listener, may be <code>null</code>.
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void nonPluginDataDownloaded(
+		INonPluginEntry[] nonPluginData,
+		IVerificationListener listener)
+		throws CoreException;
+
+	/**
+	 * Called after the feature files and any downloaded plug-ins have
+	 * been installed. Typically this is the point where the custom
+	 * install handler can install any non-plug-in entries (these are not 
+	 * interpreted in any way by the platform (beyond downloading)).
+	 * 
+	 * @see #HANDLER_ACTION_INSTALL
+	 * @param consumer content consumer for the feature. The install handler
+	 * can choose to use this consumer to install the non-plug-in data,
+	 * or can handle the data in any other way. If using the consumer,
+	 * the install handler should only call 
+	 * @see IFeatureContentConsumer#store(ContentReference, IProgressMonitor)
+	 * and @see IFeatureContentConsumer#open(INonPluginEntry)
+	 * methods of the consumer. 
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void completeInstall(IFeatureContentConsumer consumer)
+		throws CoreException;
+
+	/**
+	 * Called at the end of the install action.
+	 * 
+	 * @see #HANDLER_ACTION_INSTALL
+	 * @param success indicates action success. 
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void installCompleted(boolean success) throws CoreException;
+
+	/**
+	 * Called at the start of the configure action
+	 * 
+	 * @see #HANDLER_ACTION_CONFIGURE
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void configureInitiated() throws CoreException;
+
+	/**
+	 * Called after the feature has been configured. The install handler
+	 * should perform any completion tasks. No arguments are passed
+	 * to the method. If needed, the install handler can use arguments
+	 * passed on the initialization call.
+	 * 
+	 * @see #HANDLER_ACTION_CONFIGURE
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void completeConfigure() throws CoreException;
+
+	/**
+	 * Called at the end of the configure action.
+	 * 
+	 * @see #HANDLER_ACTION_CONFIGURE
+	 * @param success indicates action success. 
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void configureCompleted(boolean success) throws CoreException;
+
+	/**
+	 * Called at the start of the unconfigure action
+	 * 
+	 * @see #HANDLER_ACTION_UNCONFIGURE
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void unconfigureInitiated() throws CoreException;
+
+	/**
+	 * Called after the feature has been unconfigured. The install handler
+	 * should perform any completion tasks. No arguments are passed
+	 * to the method. If needed, the install handler can use arguments
+	 * passed on the initialization call.
+	 * 
+	 * @see #HANDLER_ACTION_UNCONFIGURE
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void completeUnconfigure() throws CoreException;
+
+	/**
+	 * Called at the end of the unconfigure action.
+	 * 
+	 * @see #HANDLER_ACTION_UNCONFIGURE
+	 * @param success indicates action success. 
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void unconfigureCompleted(boolean success) throws CoreException;
+
+	/**
+	 * Called at the start of the uninstall action
+	 * 
+	 * @see #HANDLER_ACTION_UNINSTALL
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void uninstallInitiated() throws CoreException;
+
+	/**
+	 * Called after the feature has been uninstalled. The install handler
+	 * should perform any completion tasks. No arguments are passed
+	 * to the method. If needed, the install handler can use arguments
+	 * passed on the initialization call. Note, that at this point
+	 * the feature files and any unreferenced plug-ins have been
+	 * removed.
+	 * 
+	 * @see #HANDLER_ACTION_UNINSTALL
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void completeUninstall() throws CoreException;
+
+	/**
+	 * Called at the end of the uninstall action.
+	 * 
+	 * @see #HANDLER_ACTION_UNINSTALL
+	 * @param success indicates action success. 
+	 * @exception CoreException terminates the action
+	 * @since 2.0
+	 */
+	public void uninstallCompleted(boolean success) throws CoreException;
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandlerEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandlerEntry.java
new file mode 100644
index 0000000..206cece
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandlerEntry.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Install handler entry.
+ * Associates an optional custom install handler with the feature.
+ * Install handlers must implement the IInstallHandler interface.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.InstallHandlerEntry
+ * @see org.eclipse.update.core.IInstallHandler
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IInstallHandlerEntry extends IAdaptable {
+
+	/**
+	 * Returns optional URL used for browser-triggered installation handling.
+	 * 
+	 * @return url
+	 * @since 2.0 
+	 */
+	public URL getURL();
+
+	/**
+	 * Returns optional name of a library containing the install
+	 * handler classes. If specified, the referenced library
+	 * must be contained in the feature archive.
+	 * 
+	 * @return install handler library name
+	 * @since 2.0 
+	 */
+	public String getLibrary();
+
+	/**
+	 * Returns install handler name.
+	 * It is interpreted depending on the value of the library
+	 * specification. If library is not specified, the name
+	 * is intepreted as an identifier of a "global" install
+	 * handler registered in the <code>org.eclipse.update.core.installHandlers</code> 
+	 * extension point. If library is specified, the name is interpreted
+	 * as a fully qualified name of a class contained in the
+	 * library. In both cases, the resulting class must
+	 * implement IInstallHandler. The class is dynamically loaded and
+	 * called at specific points during feature processing.
+	 * The handler has visibility to the API classes from the update plug-in,
+	 * and plug-ins required by the update plugin. 
+	 * 
+	 * @see IInstallHandler
+	 * @return handler name
+	 * @since 2.0 
+	 */
+	public String getHandlerName();
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandlerWithFilter.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandlerWithFilter.java
new file mode 100644
index 0000000..30ae8ea
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IInstallHandlerWithFilter.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+/**
+ * Custom install handler.
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IInstallHandlerWithFilter extends IInstallHandler{
+
+		public boolean acceptNonPluginData(INonPluginEntry data);
+		
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/INonPluginEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/INonPluginEntry.java
new file mode 100644
index 0000000..7bd158d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/INonPluginEntry.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * Non-plug-in entry defines an arbitrary non-plug-in data file packaged
+ * as part of a feature. Non-plug-in entries are not interpreted by the
+ * platform (other than being downloaded as part of an install action).
+ * They require a custom install handler to be specified as part of the
+ * feature. Note, that defining a non-plug-in entry does not necessarily
+ * indicate the non-plug-in file is packaged together with any other
+ * feature files. The actual packaging details are determined by the
+ * feature content provider for the feature.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.NonPluginEntry
+ * @see org.eclipse.update.core.FeatureContentProvider
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface INonPluginEntry extends IPlatformEnvironment, IAdaptable {
+
+	/** 
+	 * Returns the identifier of this data entry. 
+	 * 
+	 * @return data entry identifier
+	 * @since 2.0 
+	 */
+	public String getIdentifier();
+
+	/**
+	 * Returns the download size of the entry, if it can be determined.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @return download size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getDownloadSize();
+
+	/**
+	 * Returns the install size of the feature, if it can be determined.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @return install size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getInstallSize();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IPlatformEnvironment.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IPlatformEnvironment.java
new file mode 100644
index 0000000..bad0bfb
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IPlatformEnvironment.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Plug-in entry defines a packaging reference from a feature to a plug-in.
+ * It indicates that the referenced plug-in is to be considered as
+ * part of the feature. Note, that this does not necessarily indicate
+ * that the plug-in files are packaged together with any other
+ * feature files. The actual packaging details are determined by the
+ * feature content provider for the feature.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.PluginEntry
+ * @see org.eclipse.update.core.FeatureContentProvider
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IPlatformEnvironment extends IAdaptable {
+
+
+	/**
+	 * Returns optional operating system specification.
+	 * A comma-separated list of os designators defined by the platform.
+	 * Indicates this entry should only be installed on one of the specified
+	 * os systems. If this attribute is not specified, or is <code>*</code>, the
+	 * entry can be installed on all systems (portable implementation). If the
+
+	 * This information is used as a hint by the installation and update
+	 * support.
+	 *
+	 * @return the operating system specification, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getOS();
+
+	/**
+	 * Returns optional system architecture specification. 
+	 * A comma-separated list of arch designators defined by the platform.
+	 * Indicates this entry should only be installed on one of the specified
+	 * systems. If this attribute is not specified, or is <code>*</code>, the
+	 * entry can be installed on all systems (portable implementation).
+	 * 
+	 * This information is used as a hint by the installation and update
+	 * support.
+	 * 
+	 * @return system architecture specification, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getWS();
+
+	/**
+	 * Returns optional system architecture specification. 
+	 * A comma-separated list of arch designators defined by the platform.
+	 * Indicates this entry should only be installed on one of the specified
+	 * systems. If this attribute is not specified, or is <code>*</code>, the
+	 * entry can be installed on all systems (portable implementation).
+	 * 
+	 * This information is used as a hint by the installation and update
+	 * support.
+	 * 
+	 * @return system architecture specification, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getOSArch();
+
+	/**
+	 * Returns optional locale specification. 
+	 * A comma-separated list of locale designators defined by Java.
+	 * Indicates this entry should only be installed on a system running
+	 * with a compatible locale (using Java locale-matching rules).
+	 * If this attribute is not specified, or is <code>*</code>, the entry can
+	 * be installed on all systems (language-neutral implementation).
+	 * 
+	 * This information is used as a hint by the installation and update
+	 *  support.
+	 * 
+	 * @return the locale specification, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getNL();
+
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IPluginEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IPluginEntry.java
new file mode 100644
index 0000000..d3c2eb0
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IPluginEntry.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Plug-in entry defines a packaging reference from a feature to a plug-in.
+ * It indicates that the referenced plug-in is to be considered as
+ * part of the feature. Note, that this does not necessarily indicate
+ * that the plug-in files are packaged together with any other
+ * feature files. The actual packaging details are determined by the
+ * feature content provider for the feature.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.PluginEntry
+ * @see org.eclipse.update.core.FeatureContentProvider
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IPluginEntry extends IPlatformEnvironment,IAdaptable {
+
+	/** 
+	 * Returns the identifier of this plugin entry
+	 * 
+	 * @return plugin entry identifier
+	 * @since 2.0 
+	 */
+	public VersionedIdentifier getVersionedIdentifier();
+
+	/**
+	 * Returns an indication whethyer this entry represents a fragment.
+	 * 
+	 * @return <code>true</code> if the entry represents a plug-in fragment, 
+	 * <code>false</code> if the entry represents a plug-in
+	 * @since 2.0 
+	 */
+	public boolean isFragment();
+
+	/**
+	 * Returns the download size of the entry, if it can be determined.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @return download size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getDownloadSize();
+
+	/**
+	 * Returns the install size of the feature, if it can be determined.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @return install size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getInstallSize();
+
+	/** 
+	 * Sets the identifier of this plugin entry. 
+	 * This is typically performed as part of the plug-in entry creation
+	 * operation. Once set, the identifier should not be reset.
+	 * 
+	 * @param identifier plugin entry identifier
+	 * @since 2.0 
+	 */
+	public void setVersionedIdentifier(VersionedIdentifier identifier);
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ISite.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISite.java
new file mode 100644
index 0000000..663bf80
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISite.java
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.model.*;
+
+/**
+ * Site represents a location containing some number of features (packaged
+ * or installed). Sites are treated purely as an installation and packaging
+ * construct. They do not play a role during Eclipse plug-in execution. 
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.Site
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ISite extends IAdaptable {
+
+	/**
+	 * Default type for an installed feature. Different concrete feature
+	 * implementations can be registered together with their corresponding type
+	 * using the <code>org.eclipse.update.core.featureTypes</code> 
+	 * extension point.
+	 * 
+	 * @since 2.0
+	 */
+	public static final String DEFAULT_INSTALLED_FEATURE_TYPE = "org.eclipse.update.core.installed"; //$NON-NLS-1$		
+
+	/**
+	 * Default type for a packaged feature. Different concrete feature
+	 * implementations can be registered together with their corresponding type
+	 * using the <code>org.eclipse.update.core.featureTypes</code> 
+	 * extension point.
+	 * 
+	 * @since 2.0
+	 */
+	public static final String DEFAULT_PACKAGED_FEATURE_TYPE = "org.eclipse.update.core.packaged"; //$NON-NLS-1$		
+
+	/**
+	 * If we are unable to access a site, the returned CoreException will contain
+	 * this return code.
+	 * 
+	 * @since 2.0.1
+	 */
+	public static final int SITE_ACCESS_EXCEPTION = 42;
+
+	/**
+	 * Returns the site URL
+	 * 
+	 * @return site URL
+	 * @since 2.0 
+	 */
+	public URL getURL();
+
+	/**
+	 * Return the site type. Different concrete site implementations can be
+	 * registered together with their corresponding type using the
+	 * <code>org.eclipse.update.core.siteTypes</code> extension point.
+	 * 
+	 * @return site type, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getType();
+
+	/**
+	 * Returns the site description.
+	 * 
+	 * @return site description, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public IURLEntry getDescription();
+
+	/**
+	 * Returns an array of categories defined by the site.
+	 * 
+	 * @return array of site categories, or an empty array.
+	 * @since 2.0 
+	 */
+	public ICategory[] getCategories();
+
+	/**
+	 * Returns the named site category.
+	 * 
+	 * @param name category name
+	 * @return named category, or <code>null</code> ifit does not exist
+	 * @since 2.0
+	 */
+	public ICategory getCategory(String name);
+
+	/**
+	 * Returns an array of references to features on this site.
+	 * 
+	 * @return an array of feature references, or an empty array.
+	 * @since 2.0 
+	 */
+	public ISiteFeatureReference[] getFeatureReferences();
+
+	/**
+	 * Returns an array of references to features on this site.
+	 * No filtering occurs.
+	 * 
+	 * @return an array of feature references, or an empty array..
+	 * @since 2.1
+	 */
+	public ISiteFeatureReference[] getRawFeatureReferences();
+	
+	/**
+	 * Returns a reference to the specified feature if 
+	 * it is installed on this site.
+	 * filtered by the operating system, windowing system and architecture
+	 * system set in <code>Sitemanager</code>
+	 * 
+	 * @param feature feature
+	 * @return feature reference, or <code>null</code> if this feature
+	 * cannot be located on this site.
+	 * @since 2.0
+	 */
+	public ISiteFeatureReference getFeatureReference(IFeature feature);
+
+	/**
+	 * Returns an array of plug-in and non-plug-in archives located
+	 * on this site
+	 * 
+	 * @return an array of archive references, or an empty array if there are
+	 * no archives known to this site. Note, that an empty array does not
+	 * necessarily indicate there are no archives accessible on this site.
+	 * It simply indicates the site has no prior knowledge of such archives.
+	 * @since 2.0 
+	 */
+	public IArchiveReference[] getArchives();
+
+	/**
+	 * Returns the content provider for this site. A content provider
+	 * is an abstraction of each site organization. It allows the 
+	 * content of the site to be accessed in a standard way
+	 * regardless of the organization. All concrete sites
+	 * need to be able to return a content provider.
+	 * 
+	 * @return site content provider
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public ISiteContentProvider getSiteContentProvider() throws CoreException;
+
+	/**
+	 * Returns the default type for a packaged feature supported by this site
+	 * 
+	 * @return feature type, as registered in the
+	 * <code>org.eclipse.update.core.featureTypes</code> extension point.
+	 * @since 2.0
+	 */
+	public String getDefaultPackagedFeatureType();
+
+	/**
+	 * Returns an array of entries corresponding to plug-ins installed
+	 * on this site.
+	 * 
+	 * @return array of plug-in entries,or an empty array.
+	 * @since 2.0
+	 */
+	public IPluginEntry[] getPluginEntries();
+
+	/**
+	 * Returns the number of plug-ins installed on this site
+	 * 
+	 * @return number of installed plug-ins
+	 * @since 2.0
+	 */
+	public int getPluginEntryCount();
+
+	/**
+	 * Adds a new plug-in entry to this site.
+	 * 
+	 * @param pluginEntry plug-in entry
+	 * @since 2.0
+	 */
+	public void addPluginEntry(IPluginEntry pluginEntry);
+
+	/**
+	 * Returns an array of entries corresponding to plug-ins that are
+	 * installed on this site and are referenced only by the specified
+	 * feature. These are plug-ins that are not shared with any other
+	 * feature.
+	 * 
+	 * @param feature feature
+	 * @return an array of plug-in entries, or an empty array.
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public IPluginEntry[] getPluginEntriesOnlyReferencedBy(IFeature feature) throws CoreException;
+
+	/**
+	 * Returns the size of the files that need to be downloaded in order
+	 * to install the specified feature on this site, if it can be determined.
+	 * This method takes into account any plug-ins that are already
+	 * available on this site.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @param feature candidate feature
+	 * @return download size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getDownloadSizeFor(IFeature feature);
+
+	/**
+	 * Returns the size of the files that need to be installed
+	 * for the specified feature on this site, if it can be determined.
+	 * This method takes into account any plug-ins that are already
+	 * installed on this site.
+	 * 
+	 * @see org.eclipse.update.core.model.ContentEntryModel#UNKNOWN_SIZE
+	 * @param feature candidate feature
+	 * @return install size of the feature in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+	public long getInstallSizeFor(IFeature feature);
+
+	/**
+	 * Installs the specified feature on this site.
+	 * 
+	 * @param feature feature to install
+	 * @param verificationListener install verification listener
+	 * @param monitor install monitor, can be <code>null</code>
+	 * @exception InstallAbortedException when the user cancels the install
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public IFeatureReference install(IFeature feature, IVerificationListener verificationListener, IProgressMonitor monitor) throws InstallAbortedException, CoreException;
+
+	/**
+	 * Installs the specified feature on this site.
+	 * Only optional features passed as parameter will be installed.
+	 * 
+	 * @param feature feature to install
+	 * @param optionalfeatures list of optional features to be installed
+	 * @param verificationListener install verification listener
+	 * @param monitor install monitor, can be <code>null</code>
+	 * @exception InstallAbortedException when the user cancels the install
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public IFeatureReference install(IFeature feature, IFeatureReference[] optionalfeatures, IVerificationListener verificationListener, IProgressMonitor monitor) throws InstallAbortedException, CoreException;
+
+	/**
+	 * Removes (uninstalls) the specified feature from this site. This method
+	 * takes into account plug-in entries referenced by the specified fetaure
+	 * that continue to be required by other features installed on this site.
+	 * 
+	 * @param feature feature to remove
+	 * @param monitor progress monitor
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public void remove(IFeature feature, IProgressMonitor monitor) throws CoreException;
+
+	/**
+	 * Sets the site content provider. This is typically performed
+	 * as part of the site creation operation. Once set, the 
+	 * provider should not be reset.
+	 * 
+	 * @param siteContentProvider site content provider
+	 * @since 2.0
+	 */
+	public void setSiteContentProvider(ISiteContentProvider siteContentProvider);
+
+	/** 
+	 * Returns the <code>IConfiguredSite</code> for this site in the current 
+	 * configuration or <code>null</code> if none found.
+	 * 
+	 * @since 2.0.2
+	 */
+	public IConfiguredSite getCurrentConfiguredSite();
+
+	/**
+	* Creates a new feature object. The feature must exist on this site
+	* or a core exception will be thrown. Concrete implementations 
+	* may elect to cache instances, in which case subsequent calls 
+	* to create a feature with the same URL will
+	* return the same instance.
+	* param type the feature type that will be used to select the factory. If
+	* <code>null</code> is passed, default feature type will be used.
+	* param url URL of the feature archive as listed in the site.
+	* return newly created feature object, or a cached value if
+	* caching is implemented by this site.
+	* @deprecated use createFeature(String,URL,IProgressMonitor) instead
+	* @since 2.0.2
+	*/
+	IFeature createFeature(String type, URL url) throws CoreException;
+
+	/**
+	* Creates a new feature object. The feature must exist on this site
+	* or a core exception will be thrown. Concrete implementations 
+	* may elect to cache instances, in which case subsequent calls 
+	* to create a feature with the same URL will
+	* return the same instance.
+	* param type the feature type that will be used to select the factory. If
+	* <code>null</code> is passed, default feature type will be used.
+	* param url URL of the feature archive as listed in the site.
+	* return newly created feature object, or a cached value if
+	* caching is implemented by this site.
+	* @param monitor the progress monitor
+	* @since 2.1
+	*/
+	IFeature createFeature(String type, URL url,IProgressMonitor monitor) throws CoreException;
+
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteContentProvider.java
new file mode 100644
index 0000000..be7c97b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteContentProvider.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+ 
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Site content provider.
+ * A site content provider is an abstraction of each site internal 
+ * organization. It allows the site content to be accessed in
+ * a standard way regardless of the internal organization. All concrete site
+ * implementations need to implement a site content provider.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.SiteContentProvider
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ISiteContentProvider {
+	
+	/**
+	 * Returns the URL of this site
+	 * 
+	 * @return site URL
+	 * @since 2.0
+	 */	
+	public URL getURL();
+			
+	
+	/**
+	 * Returns a URL for the identified archive 
+	 * 
+	 * @param id archive identifier
+	 * @return archive URL, or <code>null</code>.
+	 * @exception CoreException 
+	 * @since 2.0 
+	 */
+	public URL getArchiveReference(String id)  throws CoreException;
+
+	/**
+	 * Returns the site for this provider
+	 * 
+	 * @return provider site
+	 * @since 2.0
+	 */
+	public ISite getSite();	
+	
+	/**
+	 * Sets the site for this provider.
+	 * In general, this method should only be called as part of
+	 * site creation. Once set, the site should not be reset.
+	 * 
+	 * @param site provider site
+	 * @since 2.0
+	 */
+	public void setSite(ISite site);
+}
+
+
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFactory.java
new file mode 100644
index 0000000..9d908eb
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFactory.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.model.*;
+
+/**
+ * Site factory interface.
+ * A site factory is used to construct new instances of concrete
+ * sites. 
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.BaseSiteFactory
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ISiteFactory {
+
+	/**
+	 * Returns a site defined by the supplied URL. 
+	 * <p>
+	 * The actual interpretation of the URL is site-type specific.
+	 * In most cases the URL will point to some site-specific
+	 * file that can be used (directly or indirectly) to construct
+	 * the site object.
+	 * </p>
+	 * @param url URL interpreted by the site
+	 * @return site object
+	 * @exception CoreException
+	 * @exception InvalidSiteTypeException the referenced site type is
+	 * not a supported type for this factory 
+	 * @since 2.0 
+	 */
+	public ISite createSite(URL url)
+		throws CoreException, InvalidSiteTypeException;
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFactoryExtension.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFactoryExtension.java
new file mode 100644
index 0000000..b0422b6
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFactoryExtension.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.model.*;
+
+/**
+ * <p>
+ * This is an extension to the standard ISiteFactory interface.
+ * If a factory implements this interface and is handling
+ * URL connections, a progress monitor can be passed to 
+ * allow canceling of frozen connections. 
+ * </p>
+ * <p>Input stream is obtained from the connection on
+ * a separate thread. When connection is canceled, 
+ * the thread is still active. It is allowed to terminate
+ * when the connection times out on its own.
+ * </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>
+ * @see org.eclipse.update.core.BaseSiteFactory
+ * @since 2.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ISiteFactoryExtension {
+	/**
+	 * Returns a site defined by the supplied URL. 
+	 * <p>
+	 * The actual interpretation of the URL is site-type specific.
+	 * In most cases the URL will point to some site-specific
+	 * file that can be used (directly or indirectly) to construct
+	 * the site object.
+	 * </p>
+	 * @param url URL interpreted by the site
+	 * @param monitor a progress monitor that can be canceled
+	 * @return site object
+	 * @exception CoreException
+	 * @exception InvalidSiteTypeException the referenced site type is
+	 * not a supported type for this factory 
+	 * @since 2.0 
+	 */
+	public ISite createSite(URL url, IProgressMonitor monitor)
+		throws CoreException, InvalidSiteTypeException;
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFeatureReference.java
new file mode 100644
index 0000000..0ba2c01
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteFeatureReference.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Site Feature reference.
+ * A reference to a feature on a particular update site.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.SiteFeatureReference
+ * @since 2.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ISiteFeatureReference extends IFeatureReference, IAdaptable {
+
+	/**
+	 * Returns an array of categories the referenced feature belong to.
+	 * 
+	 * @return an array of categories, or an empty array
+	 * @since 2.1 
+	 */
+	public ICategory[] getCategories();
+
+	/**
+	 * Adds a category to the referenced feature.
+	 * 
+	 * @param category new category
+	 * @since 2.1 
+	 */
+	public void addCategory(ICategory category);
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteWithMirrors.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteWithMirrors.java
new file mode 100644
index 0000000..7a0ec9c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/ISiteWithMirrors.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * An extension of ISite that supports mirrors.
+ * The regular update site contains features (optionally grouped by categories), while
+ * a mirrored site can define zero or more updates sites (mirrors) with the same content.
+ * This allows users to pick their own update site, for performance purposes.
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface ISiteWithMirrors extends ISite {
+
+	/**
+	 * Returns an array of mirror sites that contain the same features/plugins.
+	 * @return array of mirror sites, or empty array
+	 * @throws CoreException
+	 * @since 3.1
+	 */
+	IURLEntry[] getMirrorSiteEntries() throws CoreException;
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IURLEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IURLEntry.java
new file mode 100644
index 0000000..93ebf0f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IURLEntry.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * URL entry is an annotated URL object. It allows descriptive text to be
+ * associated with a URL. When used as description object, the annotation
+ * typically corresponds to short descriptive text, with the URL reference
+ * pointing to full browsable description.
+ * <p>
+ * Clients may implement this interface. However, in most cases clients should 
+ * directly instantiate or subclass the provided implementation of this 
+ * interface.
+ * </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>
+ * @see org.eclipse.update.core.URLEntry
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IURLEntry extends IAdaptable {
+
+	public static final int UPDATE_SITE = 0;
+	public static final int WEB_SITE = 1;	
+
+	/** 
+	 * Returns the URL annotation or <code>null</code> if none
+	 * 
+	 * @return url annotation or <code>null</code> if none
+	 * @since 2.0 
+	 */
+	public String getAnnotation();
+
+	/**
+	 * Returns the actual URL.
+	 * 
+	 * @return url.
+	 * @since 2.0 
+	 */
+	public URL getURL();
+	
+	/**
+	 * Returns the type of the URLEntry
+	 * 
+	 * @see #UPDATE_SITE
+	 * @see #WEB_SITE
+	 * @return type
+	 * @since 2.0 
+	 */
+	public int getType();	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IUpdateConstants.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IUpdateConstants.java
new file mode 100644
index 0000000..c4a1381
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IUpdateConstants.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+ 
+/**
+ * Manages a list of static constants.
+ * 
+ * <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>
+ * @since 2.0.2
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateConstants {
+	
+		/**
+	 * No matching rule specified 
+	 * @since 2.0
+	 */
+	public static final int RULE_NONE = 0;
+
+	/**
+	 * Dependency can be satisfied only with plug-in version matching 
+	 * exactly the specified version.
+	 * @since 2.0
+	 */
+	public static final int RULE_PERFECT = 1;
+
+	/**
+	 * Dependency can be satisfied only with plug-in version that is 
+	 * equivalent to the specified version (same major and minor version
+	 * identifier, greater than or equal service identifier).
+	 * @since 2.0
+	 */
+	public static final int RULE_EQUIVALENT = 2;
+
+	/**
+	 * Dependency can be satisfied only with plug-in version that is 
+	 * compatible with the specified version (either is equivalent,
+	 * or greater minor identifier (but same major identifier)).
+	 * @since 2.0
+	 */
+	public static final int RULE_COMPATIBLE = 3;
+
+	/**
+	 * Dependency can be satisfied only with plug-in version that is 
+	 * greater or equal to the specified version.
+	 * @since 2.0
+	 */
+	public static final int RULE_GREATER_OR_EQUAL = 4;
+	
+	/**
+	 * Dependency can be satisfied only if the required identifier
+	 * is a prefix of the specified identifier.
+	 * @since 2.1
+	 */
+	public static final int RULE_PREFIX = 1;
+
+	/**
+	 * The search location for updates is defined by the root feature.
+	 * @since 2.0.2
+	 */
+	public static final int SEARCH_ROOT = 1<<1;
+	
+	/**
+	 * The search location for updates is defined by this feature.
+	 * @since 2.0.2
+	 */
+	public static final int SEARCH_SELF = 1<<2;
+	
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerificationListener.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerificationListener.java
new file mode 100644
index 0000000..16ac847
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerificationListener.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+ 
+/**
+ * Verification listener. This interface abstract the user interaction
+ * that may be required as a result of feature installation. In particular,
+ * as feature archives are downloaded and verified, the user may need to 
+ * indicate whether to accept any one of the archives, or abort the 
+ * installation.
+ * <p>
+ * Clients may implement this interface.
+ * </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>
+ * @see org.eclipse.update.core.IVerificationResult
+ * @see org.eclipse.update.core.IVerifier
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IVerificationListener {
+	
+	/**
+	 * Indicate the action that resulted in this notification should be aborted.
+	 * 
+	 * @since 2.0
+	 */
+	public static final int CHOICE_ABORT = 0;
+	
+	/**
+	 * Indicate there was an error in processing the request.
+	 * The action that resulted in this notification should be aborted.
+	 * 
+	 * @since 2.0
+	 */
+	public static final int CHOICE_ERROR = 1;
+	
+	/**
+	 * Indicate that the target of the verification should be accepted,
+	 * but the information supplied with the verification result
+	 * should be trusted only for this request.
+	 * 
+	 * @since 2.0
+	 */
+	public static final int CHOICE_INSTALL_TRUST_ONCE = 2;
+	
+	/**
+	 * Indicate that the target of the verification should be accepted,
+	 * and the information supplied with the verification result
+	 * should be trusted for this request, and subsequent requests.
+	 * 
+	 * @since 2.0
+	 */
+	public static final int CHOICE_INSTALL_TRUST_ALWAYS = 3;
+	
+	/**
+	 * Determine if we should continue with the current action
+	 * based on the indicated verification results. Typically,
+	 * the implementation of this method will prompt the user
+	 * for the appropriate answer. However, other respose 
+	 * implementations can be provided.
+	 * 
+	 * @param result verification result 
+	 * @since 2.0
+	 */
+	public int prompt(IVerificationResult result);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerificationResult.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerificationResult.java
new file mode 100644
index 0000000..8ceacc5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerificationResult.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+/**
+ * Verification result. An object implementing this interface represents
+ * a token passed between the update framework and the verifier and verification
+ * listener. The verifier uses the token to capture the result of the file
+ * verification. It is then passed to the verification listener
+ * to optionally present this information to the user (in an 
+ * implementation-specific way). The verification listener in turn
+ * uses this token to capture the desired response.
+ * <p>
+ * Clients may implement this interface.
+ * </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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IVerificationResult {
+
+	/**
+	 * Indicates the file type is recognized but the file is not signed
+	 * 
+	 * @see #TYPE_ENTRY_UNRECOGNIZED
+	 * @since 2.0
+	 */
+	public static final int TYPE_ENTRY_NOT_SIGNED = 1;
+
+	/**
+	 * Indicates the file has been modified since it was signed
+	 * 
+	 * @since 2.0
+	 */
+	public static final int TYPE_ENTRY_CORRUPTED = 2;
+
+	/**
+	 * Indicates the file is signed by a known signer
+	 * 
+	 * @since 2.0
+	 */
+	public static final int TYPE_ENTRY_SIGNED_RECOGNIZED = 3;
+
+	/**
+	 * Indicates the file is signed but the signer is not known
+	 * 
+	 * @since 2.0
+	 */
+	public static final int TYPE_ENTRY_SIGNED_UNRECOGNIZED = 5;
+
+	/**
+	 * Error occurred during verification
+	 * 
+	 * @since 2.0
+	 */
+	public static final int UNKNOWN_ERROR = 6;
+
+	/**
+	 * Verification was cancelled
+	 * 
+	 * @since 2.0
+	 */
+	public static final int VERIFICATION_CANCELLED = 7;
+
+	/**
+	 * Could not perform verification due to unrecognized file
+	 * 
+	 * @see #TYPE_ENTRY_NOT_SIGNED
+	 * @since 2.0
+	 */
+	public static final int TYPE_ENTRY_UNRECOGNIZED = 8;
+
+	/**
+	 * Returns the content reference that is the target of the verification.
+	 * 
+	 * @return content reference
+	 * @since 2.0
+	 */
+	public ContentReference getContentReference();
+
+	/**
+	 * Returns the feature the referenced file is part of.
+	 * 
+	 * @return feature
+	 * @since 2.0
+	 */
+	public IFeature getFeature();
+
+	/**
+	 * Returns the verification code.
+	 * 
+	 * @return verification code, as defined in this interface.
+	 * @since 2.0
+	 */
+	public int getVerificationCode();
+
+	/**
+	 * Returns any exception caught during verification
+	 * 
+	 * @return exception, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public Exception getVerificationException();
+
+	/**
+	 * Returns display text describing the result of the verification.
+	 * 
+	 * @return result text, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getText();
+
+	/**
+	 * Returns text describing the signer
+	 * 
+	 * @return signer information, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getSignerInfo();
+
+	/**
+	 * Returns text describing the authority that verified/ certified 
+	 * the signer
+	 * 
+	 * @return verifier information, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getVerifierInfo();
+
+	/**
+	 * Indicates whether the referenced file is part of the overall feature
+	 * definition, or one of its component plug-in or non-plug-in entries.
+	 * 
+	 * @return <code>true</code> if reference is a feature file, 
+	 * <code>false</code> if reference is a plug-in or non-plug-in file
+	 * @since 2.0
+	 */
+	public boolean isFeatureVerification();
+	
+	/**
+	 * Indicates whether the signer and verifier info have already been accepted by the user
+	 * during a previous verification of one of the file of the feature.
+	 * 
+	 * @return <code>true</code> if the result has already been accepted, <code>false</code>
+	 * if the result has not yet been accepted by the user
+	 * @since 2.0
+	 */
+	public boolean alreadySeen();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerifier.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerifier.java
new file mode 100644
index 0000000..61d835a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IVerifier.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Verifier. This interface abstracts the archive verification step
+ * performed by specific feature implementations. The actual details
+ * of the verification are the responsibility of the concrete implementation.
+ * <p>
+ * Clients may implement this interface.
+ * </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>
+ * @see org.eclipse.update.core.IVerificationResult
+ * @see org.eclipse.update.core.IFeatureContentProvider#getVerifier()
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IVerifier {
+
+	/**
+	 * Perform verification of the specified archive.
+	 * 
+	 * @param feature feature containing this archive
+	 * @param reference actual archive reference
+	 * @param isFeatureVerification <code>true</code> indicates the specified
+	 * reference should be considered as part of the feature description
+	 * information (ie. verifying the overall feature), 
+	 * <code>false</code> indicates the specified reference is a plug-in
+	 * or a non-plug-in archive file (ie. verifying a component of the
+	 * feature)
+	 * @param monitor progress monitor, can be <code>null</code>
+	 * @return verification result
+	 * @exception CoreException
+	 * @since 2.0
+	 */
+	public IVerificationResult verify(
+		IFeature feature,
+		ContentReference reference,
+		boolean isFeatureVerification,
+		InstallMonitor monitor)
+		throws CoreException;
+		
+	/**
+	 * Sets the parent verifier.
+	 * 
+	 * The parent verifier can only be set once by the parent feature.
+	 * It may used for different verification strategies. 
+	 * (for instance, you may decide that both the parent and current verifier
+	 * must sucessfully verify the content reference, or that only one of them must verify)
+	 * @param parentVerifier the parent verifier.
+	 */
+	public void setParent(IVerifier parentVerifier);
+	
+	/**
+	 * Returns the parent verifier
+	 * 
+	 * @return the parent verifier
+	 * @since 2.0
+	 */
+	public IVerifier getParent();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/Import.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/Import.java
new file mode 100644
index 0000000..156b006
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/Import.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.update.core.model.ImportModel;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+
+/**
+ * Convenience implementation of a plug-in dependency.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.IImport
+ * @see org.eclipse.update.core.model.ImportModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class Import extends ImportModel implements IImport {
+
+	//PERF: new instance variable
+	private VersionedIdentifier versionId;
+
+	/**
+	 * Returns an identifier of the dependent plug-in.
+	 * @see IImport#getVersionedIdentifier()
+	 */
+	public VersionedIdentifier getVersionedIdentifier() {
+		if (versionId != null)
+			return versionId;
+
+		String id = getIdentifier();
+		String ver = getVersion();
+		if (id != null && ver != null) {
+			try {
+				versionId = new VersionedIdentifier(id, ver);
+				return versionId;
+			} catch (Exception e) {
+				UpdateCore.warn("Unable to create versioned identifier:" + id + ":" + ver); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
+		
+		versionId = new VersionedIdentifier("",null); //$NON-NLS-1$
+		return versionId;		
+	}
+
+	/**
+	 * Returns the matching rule for the dependency.
+	 * @see IImport#getRule()
+	 */
+	public int getRule() {
+		return UpdateManagerUtils.getMatchingRule(getMatchingRuleName());
+	}
+	
+	/**
+	 * Returns the matching rule for the dependency identifier.
+	 * @see IImport#getIdRule()
+	 */
+	public int getIdRule() {
+		return UpdateManagerUtils.getMatchingIdRule(getMatchingIdRuleName());
+	}
+	
+	/**
+	 * 
+	 * @see org.eclipse.update.core.IImport#getKind()
+	 */
+
+	/**
+	 * Returns the dependency kind
+	 * @see org.eclipse.update.core.IImport#getKind()
+	 */
+	public int getKind() {
+		return isFeatureImport()?KIND_FEATURE:KIND_PLUGIN;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/IncludedFeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/IncludedFeatureReference.java
new file mode 100644
index 0000000..7b8ee28
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/IncludedFeatureReference.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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
+ *     James D Miles (IBM Corp.) - bug 182625, Missing constructor
+ *******************************************************************************/
+package org.eclipse.update.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.model.IncludedFeatureReferenceModel;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * This is a utility class representing the options of a nested feature.
+ * Feature will include other features. This class will represent the options of the inclusion.
+ * <p>
+ * Clients may instantiate; not intended to be subclassed 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>
+ * @see org.eclipse.update.core.VersionedIdentifier
+ * @since 2.0.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class IncludedFeatureReference
+	extends IncludedFeatureReferenceModel
+	implements IIncludedFeatureReference {
+
+	/**
+	 * Construct a included feature reference
+	 * 
+	 * @since 2.1
+	 */
+	public IncludedFeatureReference() {
+		super();
+	}
+
+	/**
+	 * Construct a feature options 
+	 * 
+	 * @param includedFeatureRef reference to clone
+	 * @since 2.0.2
+	 */
+	public IncludedFeatureReference(IIncludedFeatureReference includedFeatureRef) {
+		super((IncludedFeatureReferenceModel) includedFeatureRef);
+	}
+
+	/**
+	 * Constructor IncludedFeatureReference.
+	 * @param featureReference
+	 */
+	public IncludedFeatureReference(IFeatureReference featureReference) {
+		super(featureReference);
+	}
+	
+	public IncludedFeatureReference(IncludedFeatureReferenceModel includedFeatureRefModel){
+		super(includedFeatureRefModel);
+	}
+
+	/*
+	 * Method isDisabled.
+	 * @return boolean
+	 */
+	private boolean isDisabled() {
+		IConfiguredSite cSite = getSite().getCurrentConfiguredSite();
+		if (cSite == null)
+			return false;
+		IFeatureReference[] configured = cSite.getConfiguredFeatures();
+		for (int i = 0; i < configured.length; i++) {
+			if (this.equals(configured[i]))
+				return false;
+		}
+		return true;
+		//		// FIXME: the above code was commented out and returned false. 
+		//		// Should this be commented out again?
+		//		return false;
+	}
+
+	/*
+	 * Method isInstalled.
+	 * @return boolean
+	 */
+	private boolean isUninstalled() {
+		if (!isDisabled())
+			return false;
+		IFeatureReference[] installed = getSite().getFeatureReferences();
+		for (int i = 0; i < installed.length; i++) {
+			if (this.equals(installed[i]))
+				return false;
+		}
+		// if we reached this point, the configured site exists and it does not
+		// contain this feature reference, so clearly the feature is uninstalled
+		return true;
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IIncludedFeatureReference#getFeature(boolean,
+	 * IConfiguredSite)
+	 * @deprecated use getFeature(IProgressMonitor)
+	 */
+	public IFeature getFeature(
+		boolean perfectMatch,
+		IConfiguredSite configuredSite)
+		throws CoreException {
+		return getFeature(null);
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IIncludedFeatureReference#getFeature(boolean,
+	 * IConfiguredSite,IProgressMonitor)
+	 * @deprecated use getFeature(IProgressMonitor)
+	 */
+	public IFeature getFeature(
+		boolean perfectMatch,
+		IConfiguredSite configuredSite,
+		IProgressMonitor monitor)
+		throws CoreException {
+			return getFeature(monitor);
+	}
+
+	/**
+	 * @see org.eclipse.update.core.IFeatureReference#getFeature()
+	 * @deprecated use getFeature(IProgressMonitor)
+	 */
+	public IFeature getFeature() throws CoreException {
+		return getFeature(null);
+	}
+	/**
+	 * @see org.eclipse.update.core.IFeatureReference#getFeature
+	 * (IProgressMonitor)
+	 */
+	public IFeature getFeature(IProgressMonitor monitor) throws CoreException {
+		if (isUninstalled())
+			throw new CoreException(new Status(IStatus.ERROR, UpdateCore.getPlugin().getBundle().getSymbolicName(), IStatus.OK, NLS.bind(Messages.IncludedFeatureReference_featureUninstalled, (new String[] { getFeatureIdentifier() })), null));
+		else
+			return super.getFeature(monitor);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/InstallHandlerEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/InstallHandlerEntry.java
new file mode 100644
index 0000000..900254b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/InstallHandlerEntry.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.update.core.model.InstallHandlerEntryModel;
+
+/**
+ * Convenience implementation of an install handler entry.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.IInstallHandlerEntry
+ * @see org.eclipse.update.core.model.InstallHandlerEntryModel
+ * @see org.eclipse.update.core.IInstallHandler
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class InstallHandlerEntry
+	extends InstallHandlerEntryModel
+	implements IInstallHandlerEntry {
+
+	/**
+	 * Constructor for InstallHandlerEntry.
+	 * @since 2.0
+	 */
+	public InstallHandlerEntry() {
+		super();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/InstallMonitor.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/InstallMonitor.java
new file mode 100644
index 0000000..b722575
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/InstallMonitor.java
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.util.Stack;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.internal.core.Messages;
+
+/**
+ * Install progress monitor
+ * Delegating wrapper for IProgressMonitor used for installation handling.
+ * 
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class InstallMonitor implements IProgressMonitor {
+
+	protected IProgressMonitor monitor;
+	protected Stack tasks;
+
+	protected String taskString;
+	protected String subTaskString;
+	protected boolean showDetails;
+	protected long totalCopyCount;
+	protected	long currentCount = 0;
+
+	protected class MonitorState {
+
+		private String taskString;
+		private String subTaskString;
+		private boolean showDetails;
+		private long totalCopyCount;
+        private long currentCount;
+
+		private MonitorState(
+			String taskString,
+			String subTaskString,
+			boolean showDetails,
+            long currentCount,
+			long totalCopyCount) {
+			this.taskString = taskString;
+			this.subTaskString = subTaskString;
+			this.showDetails = showDetails;
+            this.currentCount = currentCount;
+			this.totalCopyCount = totalCopyCount;
+		}
+
+		private String getTaskString() {
+			return this.taskString;
+		}
+
+		private String getSubTaskString() {
+			return this.subTaskString;
+		}
+
+		private boolean getShowDetails() {
+			return this.showDetails;
+		}
+
+        private long getCurrentCount() {
+            return this.currentCount;
+        }
+        
+		private long getTotalCopyCount() {
+			return this.totalCopyCount;
+		}
+	}
+	
+	protected InstallMonitor() {
+	}
+
+	/**
+	 * Install monitor constructor
+	 * 
+	 * @param monitor base install monitor
+	 * @since 2.0
+	 */
+	public InstallMonitor(IProgressMonitor monitor) {
+		this.monitor = monitor;
+		this.tasks = new Stack();
+		this.taskString = ""; //$NON-NLS-1$
+		this.subTaskString = ""; //$NON-NLS-1$
+		this.showDetails = false;
+		this.totalCopyCount = 0;
+	}
+
+	/**
+	 * Begin new monitor task.
+	 * 
+	 * @see IProgressMonitor#beginTask(String, int)
+	 * @since 2.0
+	 */
+	public void beginTask(String name, int totalWork) {
+		taskString = name;
+		monitor.beginTask(name, totalWork);
+	}
+
+	/**
+	 * Indicate completion of monitor activity.
+	 * 
+	 * @see IProgressMonitor#done()
+	 * @since 2.0
+	 */
+	public void done() {
+		monitor.done();
+	}
+
+	/**
+	 * Indicate monitor progress.
+	 * 
+	 * @see IProgressMonitor#internalWorked(double)
+	 * @since 2.0
+	 */
+	public void internalWorked(double work) {
+		monitor.internalWorked(work);
+	}
+
+	/**
+	 * Check is use indicated that the operation be cancelled.
+	 * 
+	 * @see IProgressMonitor#isCanceled()
+	 * @since 2.0
+	 */
+	public boolean isCanceled() {
+		return monitor.isCanceled();
+	}
+
+	/**
+	 * Set the cancellation state.
+	 * 
+	 * @see IProgressMonitor#setCanceled(boolean)
+	 * @since 2.0
+	 */
+	public void setCanceled(boolean value) {
+		monitor.setCanceled(value);
+	}
+
+	/**
+	 * Set task name.
+	 * 
+	 * @see IProgressMonitor#setTaskName(String)
+	 * @since 2.0
+	 */
+	public void setTaskName(String name) {
+		this.taskString = name;
+		this.subTaskString = ""; //$NON-NLS-1$
+		this.showDetails = false;
+		this.totalCopyCount = 0;
+		monitor.subTask(""); //$NON-NLS-1$
+		monitor.setTaskName(name);
+	}
+
+	/**
+	 * Set subtask name.
+	 * 
+	 * @see IProgressMonitor#subTask(String)
+	 * @since 2.0
+	 */
+	public void subTask(String name) {
+		this.subTaskString = name;
+		this.showDetails = false;
+		this.totalCopyCount = 0;
+		monitor.subTask(name);
+	}
+
+	/**
+	 * Indicate monitor progress.
+	 * 
+	 * @see IProgressMonitor#worked(int)
+	 * @since 2.0
+	 */
+	public void worked(int work) {
+		monitor.worked(work);
+	}
+
+	/**
+	 * Save the current monitor state.
+	 * The states are saved on a push-down stack. Prior states
+	 * can be restored by calling restorState()
+	 * 
+	 * @see #restoreState()
+	 * @since 2.0
+	 */
+	public void saveState() {
+		tasks.push(
+			new MonitorState(taskString, subTaskString, showDetails, currentCount, totalCopyCount));
+	}
+
+	/**
+	 * Restore the monitor state.
+	 * 
+	 * @see #saveState()
+	 * @since 2.0
+	 */
+	public void restoreState() {
+		if (tasks.size() > 0) {
+			MonitorState state = (MonitorState) tasks.pop();
+			setTaskName(state.getTaskString());
+			subTask(state.getSubTaskString());
+			this.showDetails = state.getShowDetails();
+            this.currentCount = state.getCurrentCount();
+			this.totalCopyCount = state.getTotalCopyCount();
+		}
+	}
+
+	/**
+	 * Indicate whether the monitor subtask message should include
+	 * copy progress counts.
+	 * 
+	 * @see #setCopyCount(long)
+	 * @see #setTotalCount(long)
+	 * @param setting <code>true</code> to show the copy count,
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 */
+	public void showCopyDetails(boolean setting) {
+		this.showDetails = setting;
+	}
+
+	/**
+	 * Sets the total number of bytes to copy.
+	 * 
+	 * @see #showCopyDetails(boolean)
+	 * @see #setCopyCount(long)
+	 * @param count total number of bytes to copy.
+	 * @since 2.0
+	 */
+	public void setTotalCount(long count) {
+		this.totalCopyCount = count;
+	}
+
+	/**
+	 * Sets the number of bytes already copied.
+	 * 
+	 * @see #showCopyDetails(boolean)
+	 * @see #setTotalCount(long)
+	 * @param count number of bytes already copied.
+	 * @since 2.0
+	 */
+	public void setCopyCount(long count) {
+		if (showDetails && count > 0) {
+			currentCount = count;
+			long countK = count / 1024;
+			long totalK = totalCopyCount / 1024;
+			String msg =
+				(totalK <= 0)
+					? NLS.bind(Messages.InstallMonitor_DownloadSize, (new String[] { Long.toString(countK) }))
+					: NLS.bind(Messages.InstallMonitor_DownloadSizeLong, (new String[] { Long.toString(countK), Long.toString(totalK) }));
+			monitor.subTask(subTaskString + msg);
+		}
+	}
+	
+	/**
+	 * Increments the number of bytes copied.
+	 * 
+	 * @param increment number of new bytes  copied.
+	 * @since 3.0
+	 */
+	public void incrementCount(long increment) {
+		setCopyCount(currentCount + increment);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/JarContentReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/JarContentReference.java
new file mode 100644
index 0000000..1ea7f3a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/JarContentReference.java
@@ -0,0 +1,370 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.*;
+
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * Local .jar file content reference.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.ContentReference
+ * @see org.eclipse.update.core.JarEntryContentReference
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class JarContentReference extends ContentReference {
+
+	//private static ArrayList referenceList = new ArrayList();
+	private JarFile jarFile;
+
+	/**
+	 * Content selector used in .jar operations.
+	 * Default implementation causes all file entries to be selected with
+	 * generated identifiers being the same as the original .jar entry name.
+	 * 
+	 * @since 2.0
+	 */
+	public static class ContentSelector {
+
+		/**
+		 * Indicates whether the .jar entry should be selected.
+		 * Default behavior is to select all non-directory entries.
+		 * 
+		 * @param entry .jar entry
+		 * @return <code>true</code> if entry is to be selected, 
+		 * <code>false</code> otherwise
+		 * @since 2.0
+		 */
+		public boolean include(JarEntry entry) {
+			return entry == null ? false : !entry.isDirectory();
+		}
+
+		/**
+		 * Defines the "symbolic" path identifier for the 
+		 * entry. Default identifier is the same as the jar entry name.
+		 * 
+		 * @param entry .jar entry
+		 * @return "symbolic" path identifier
+		 * @since 2.0
+		 */
+		public String defineIdentifier(JarEntry entry) {
+			return entry == null ? null : entry.getName();
+		}
+	}
+
+	/**
+	 * Create jar content reference from URL.
+	 * 
+	 * @param id "symbolic" path identifier
+	 * @param url actual referenced URL
+	 * @since 2.0
+	 */
+	public JarContentReference(String id, URL url) {
+		super(id, url);
+		this.jarFile = null;
+		//referenceList.add(this); // keep track of archives
+	}
+
+	/**
+	 * Create jar content reference from file.
+	 * 
+	 * @param id "symbolic" path identifier
+	 * @param file actual referenced file
+	 * @since 2.0
+	 */
+	public JarContentReference(String id, File file) {
+		super(id, file);
+		this.jarFile = null;
+		//referenceList.add(this); // keep track of archives
+	}
+
+	/**
+	 * A factory method to create a jar content reference.
+	 * 
+	 * @param id "symbolic" path identifier
+	 * @param file actual referenced file
+	 * @return jar content reference
+	 * @since 2.0
+	 */
+	public ContentReference createContentReference(String id, File file) {
+		return new JarContentReference(id, file,true);
+	}
+	/**
+	 * Constructor JarContentReference.
+	 * @param id
+	 * @param file
+	 * @param b
+	 */
+	public JarContentReference(String id, File file, boolean b) {
+		this(id,file);
+		setTempLocal(b);
+	}
+
+	/**
+	 * Returns the content reference as a jar file. Note, that this method
+	 * <b>does not</b> cause the file to be downloaded if it
+	 * is not already local.
+	 * 
+	 * @return reference as jar file
+	 * @exception IOException reference cannot be returned as jar file
+	 * @since 2.0
+	 */
+	protected JarFile asJarFile() throws IOException {
+		if (this.jarFile == null) {
+			File file = asFile();
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
+				UpdateCore.debug("asJarFile :" + file); //$NON-NLS-1$
+			if (file != null && !file.exists()) {
+				UpdateCore.warn("JarFile does not exits:" + file); //$NON-NLS-1$
+				throw new FileNotFoundException(file.getAbsolutePath());
+			}
+			this.jarFile = new JarFile(file);
+		}
+		return jarFile;
+	}
+
+	/**
+	 * Unpacks the referenced jar archive into the specified location.
+	 * Returns content references to the unpacked files.
+	 * 
+	 * @param dir location to unpack the jar into
+	 * @param selector selector, used to select entries to unpack, and to define
+	 * "symbolic" path identifiers for the entries.
+	 * @param monitor progress monitor 
+	 * @exception IOException
+	 * @exception InstallAbortedException
+	 * @since 2.0
+	 */
+	public ContentReference[] unpack(File dir, ContentSelector selector, InstallMonitor monitor) throws IOException, InstallAbortedException {
+
+		// make sure we have a selector
+		if (selector == null)
+			selector = new ContentSelector();
+
+		// get archive content
+		JarFile jarArchive = this.asJarFile();
+		List content = new ArrayList();
+		Enumeration entries = jarArchive.entries();
+
+		// run through the entries and unjar
+		String entryId;
+		JarEntry entry;
+		InputStream is;
+		OutputStream os;
+		File localFile;
+		try {
+			if (monitor != null) {
+				monitor.saveState();
+				monitor.setTaskName(Messages.JarContentReference_Unpacking);	
+				monitor.subTask(this.getIdentifier());
+				monitor.showCopyDetails(false);
+			}
+			while (entries.hasMoreElements()) {
+				entry = (JarEntry) entries.nextElement();
+				if (entry != null && selector.include(entry)) {
+					is = null;
+					os = null;
+					entryId = selector.defineIdentifier(entry);
+					localFile = Utilities.createLocalFile(dir, entryId); // create temp file 
+					if (!entry.isDirectory()) {
+						try {
+							is = jarArchive.getInputStream(entry);
+							os = new FileOutputStream(localFile);
+							Utilities.copy(is, os, monitor);
+						} finally {
+							if (is != null)
+								try {
+									is.close();
+								} catch (IOException e) {
+								}
+							if (os != null)
+								try {
+									os.close();
+								} catch (IOException e) {
+								}
+						}
+						content.add(new ContentReference(entryId, localFile));
+					}
+				}
+			}
+		} finally {
+			if (monitor != null)
+				monitor.restoreState();
+		}
+		return (ContentReference[]) content.toArray(new ContentReference[0]);
+	}
+
+	/**
+	 * Unpacks the named jar entry into the specified location.
+	 * Returns content reference to the unpacked file.
+	 * 
+	 * @param dir location to unpack the jar into
+	 * @param entryName name of the jar entry
+	 * @param selector selector, used to define "symbolic" path identifier
+	 * for the entry
+	 * @param monitor progress monitor 
+	 * @exception IOException
+	 * @exception InstallAbortedException
+	 * @since 2.0
+	 */
+	public ContentReference unpack(File dir, String entryName, ContentSelector selector, InstallMonitor monitor) throws IOException, InstallAbortedException {
+
+		// make sure we have a selector
+		if (selector == null)
+			selector = new ContentSelector();
+
+		// unjar the entry
+		JarFile jarArchive = this.asJarFile();
+		entryName = entryName.replace(File.separatorChar, '/');
+		JarEntry entry = jarArchive.getJarEntry(entryName);
+		String entryId;
+		if (entry != null) {
+			InputStream is = null;
+			OutputStream os = null;
+			entryId = selector.defineIdentifier(entry);
+			File localFile = Utilities.createLocalFile(dir, entryId); // create temp file
+			if (!entry.isDirectory()) {
+				try {
+					is = jarArchive.getInputStream(entry);
+					os = new FileOutputStream(localFile);
+					Utilities.copy(is, os, monitor);
+				} finally {
+					if (is != null)
+						try {
+							is.close();
+						} catch (IOException e) {
+						}
+					if (os != null)
+						try {
+							os.close();
+						} catch (IOException e) {
+						}
+				}
+				return new ContentReference(entryId, localFile);
+			} else
+				return null; // entry was a directory
+		} else
+			throw new FileNotFoundException(this.asFile().getAbsolutePath() + " " + entryName);	//$NON-NLS-1$
+	}
+
+	/**
+	 * Peeks into the referenced jar archive.
+	 * Returns content references to the jar entries within the jar file.
+	 * 
+	 * @param selector selector, used to select entries to return, and to define
+	 * "symbolic" path identifiers for the entries.
+	 * @param monitor progress monitor 
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public ContentReference[] peek(ContentSelector selector, InstallMonitor monitor) throws IOException {
+
+		// make sure we have a selector
+		if (selector == null)
+			selector = new ContentSelector();
+
+		// get archive content
+		JarFile jarArchive = this.asJarFile();
+		List content = new ArrayList();
+		Enumeration entries = jarArchive.entries();
+
+		// run through the entries and create content references
+		JarEntry entry;
+		String entryId;
+		while (entries.hasMoreElements()) {
+			entry = (JarEntry) entries.nextElement();
+			if (selector.include(entry)) {
+				entryId = selector.defineIdentifier(entry);
+				content.add(new JarEntryContentReference(entryId, this, entry));
+			}
+		}
+		return (ContentReference[]) content.toArray(new ContentReference[0]);
+	}
+
+	/**
+	 * Peeks into the referenced jar archive looking for the named entry.
+	 * Returns content reference to the jar entry within the jar file.
+	 * 
+	 * @param entryName name of the jar entry
+	 * @param selector selector, used to define "symbolic" path identifier
+	 * for the entry
+	 * @param monitor progress monitor 
+	 * @return the content reference ofr <code>null</null> if the entry doesn't exist
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public ContentReference peek(String entryName, ContentSelector selector, InstallMonitor monitor) throws IOException {
+
+		// make sure we have a selector
+		if (selector == null)
+			selector = new ContentSelector();
+
+		// assume we have a reference that represents a jar archive.
+		JarFile jarArchive = this.asJarFile();
+		entryName = entryName.replace(File.separatorChar, '/');
+		JarEntry entry = jarArchive.getJarEntry(entryName);
+		if (entry == null)
+			return null;
+
+		String entryId = selector.defineIdentifier(entry);
+		return new JarEntryContentReference(entryId, this, entry);
+	}
+
+	/**
+	 * Closes the jar archive corresponding to this reference.
+	 * 
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public void closeArchive() throws IOException {
+		if (this.jarFile != null) {
+			this.jarFile.close();
+			this.jarFile = null;
+		}
+	}
+
+	/**
+	 * Perform shutdown processing for jar archive handling.
+	 * This method is called when platform is shutting down.
+	 * It is not intended to be called at any other time under
+	 * normal circumstances. A side-effect of calling this method
+	 * is that all jars referenced by JarContentReferences are closed.
+	 * 
+	 * @since 2.0
+	 */
+	public static void shutdown() {
+		/*for (int i = 0; i < referenceList.size(); i++) {
+			JarContentReference ref = (JarContentReference) referenceList.get(i);
+			try {
+				ref.closeArchive(); // ensure we are not leaving open jars
+			} catch (IOException e) {
+				// we tried, nothing we can do ...
+			}
+		}*/
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/JarEntryContentReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/JarEntryContentReference.java
new file mode 100644
index 0000000..336532c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/JarEntryContentReference.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.io.*;
+import java.net.*;
+import java.util.jar.*;
+
+/**
+ * .jar file entry content reference.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.ContentReference
+ * @see org.eclipse.update.core.JarContentReference
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class JarEntryContentReference extends ContentReference {
+
+	private JarContentReference jarContentReference;
+	private JarEntry entry;
+
+	/**
+	 * Create jar entry content reference.
+	 * 
+	 * @param id "symbolic" path identifier
+	 * @param jarContentReference jar file content reference
+	 * @param entry jar entry
+	 * @since 2.0
+	 */
+	public JarEntryContentReference(
+		String id,
+		JarContentReference jarContentReference,
+		JarEntry entry) {
+		super(id, (File) null);
+		this.jarContentReference = jarContentReference;
+		this.entry = entry;
+	}
+	
+	/**
+	 * Creates an input stream for the reference.
+	 * 
+	 * @return input stream
+	 * @exception IOException unable to create stream
+	 * @since 2.0
+	 */
+	public InputStream getInputStream() throws IOException {
+		return jarContentReference.asJarFile().getInputStream(entry);
+	}	
+	
+	/**
+	 * Returns the size of the referenced entry.
+	 * 
+	 * @return input size
+	 * @since 2.0
+	 */
+	public long getInputSize() {
+		return entry.getSize();
+	}
+	
+	/**
+	 * Indicates whether the reference is to a an entry within a local jar.
+	 * 
+	 * @return <code>true</code> if the reference is local, 
+	 * otherwise <code>false</code>
+	 * @since 2.0
+	 */
+	public boolean isLocalReference() {
+		return jarContentReference.isLocalReference();
+	}	
+		
+	/**
+	 * Returns the content reference as a URL.
+	 * 
+	 * @return reference as URL
+	 * @exception IOException reference cannot be returned as URL
+	 * @since 2.0
+	 */
+	public URL asURL() throws IOException {
+		String fileName =
+			jarContentReference.asFile().getAbsolutePath().replace(File.separatorChar, '/');
+		return new URL("jar:file:" + fileName + "!/" + entry.getName());	//$NON-NLS-1$ //$NON-NLS-2$
+	}
+			
+	/**
+	 * Return string representation of this reference.
+	 * 
+	 * @return string representation
+	 * @since 2.0
+	 */
+	public String toString() {
+		URL url;
+		try {
+			url = asURL();
+		} catch (IOException e) {
+			url = null;
+		}
+		if (url != null)
+			return url.toExternalForm();
+		else
+			return getClass().getName() + "@" + hashCode(); //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/NonPluginEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/NonPluginEntry.java
new file mode 100644
index 0000000..f854de1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/NonPluginEntry.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.update.core.model.*;
+
+/**
+ * Convenience implementation of non-plug-in entry.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.INonPluginEntry
+ * @see org.eclipse.update.core.model.NonPluginEntryModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class NonPluginEntry
+	extends NonPluginEntryModel
+	implements INonPluginEntry {
+
+	/**
+	 * Non-plug-in entry default constructor
+	 */
+	public NonPluginEntry() {
+		super();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/PluginEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/PluginEntry.java
new file mode 100644
index 0000000..3a92eb9
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/PluginEntry.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * Convenience implementation of plug-in entry.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.IPluginEntry
+ * @see org.eclipse.update.core.model.PluginEntryModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class PluginEntry extends PluginEntryModel implements IPluginEntry {
+	
+	// PERF: new instance variable
+	private VersionedIdentifier versionId;
+
+	/**
+	 * Plug-in entry default constructor
+	 */
+	public PluginEntry() {
+		super();
+	}
+
+	/**
+	 * Returns the identifier of this plugin entry
+	 * 
+	 * @see IPluginEntry#getVersionedIdentifier()
+	 * @since 2.0
+	 */
+	public VersionedIdentifier getVersionedIdentifier() {
+		if (versionId != null)
+			return versionId;
+
+		String id = getPluginIdentifier();
+		String ver = getPluginVersion();
+		if (id != null && ver != null) {
+			try {
+				versionId = new VersionedIdentifier(id, ver);
+				return versionId;
+			} catch (Exception e) {
+				UpdateCore.warn("Unable to create versioned identifier:" + id + ":" + ver); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
+		versionId = new VersionedIdentifier("",null); //$NON-NLS-1$
+		return versionId;
+	}
+
+	/**
+	 * Sets the identifier of this plugin entry. 
+	 * 
+	 * @see IPluginEntry#setVersionedIdentifier(VersionedIdentifier)
+	 * @since 2.0
+	 */
+	public void setVersionedIdentifier(VersionedIdentifier identifier) {
+		setPluginIdentifier(identifier.getIdentifier());
+		setPluginVersion(identifier.getVersion().toString());
+	}	
+
+	/**
+	 * Compares two plugin entries for equality
+	 * 
+	 * @param object plugin entry object to compare with
+	 * @return <code>true</code> if the two entries are equal, 
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 */
+	public boolean equals(Object object) {
+		if (!(object instanceof IPluginEntry))
+			return false;
+		IPluginEntry e = (IPluginEntry) object;
+		return getVersionedIdentifier().equals(e.getVersionedIdentifier());
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/Site.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/Site.java
new file mode 100644
index 0000000..2c6b211
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/Site.java
@@ -0,0 +1,562 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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
+ *     James D Miles (IBM Corp.) - bug 191783, NullPointerException in FeatureDownloader
+ *******************************************************************************/
+package org.eclipse.update.core;
+
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * Convenience implementation of a site.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.ISite
+ * @see org.eclipse.update.core.model.SiteModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class Site extends SiteModel implements ISiteWithMirrors {
+
+	/**
+	 * Default installation path for features
+	 * 
+	 * @since 2.0
+	 */
+	public static final String DEFAULT_INSTALLED_FEATURE_PATH = "features/"; //$NON-NLS-1$
+
+	/**
+	 * Default installation path for plug-ins and plug-in fragments
+	 * 
+	 * @since 2.0
+	 */
+	public static final String DEFAULT_PLUGIN_PATH = "plugins/"; //$NON-NLS-1$
+
+	/**
+	 * Default path on a site where packaged features are located
+	 * 
+	 * @since 2.0
+	 */
+	public static final String DEFAULT_FEATURE_PATH = "features/"; //$NON-NLS-1$
+
+	/**
+	 * Default site manifest file name
+	 * 
+	 * @since 2.0
+	 */
+	public static final String SITE_FILE = "site"; //$NON-NLS-1$
+
+	/**
+	 * Default site manifest extension
+	 * 
+	 * @since 2.0
+	 */
+	public static final String SITE_XML = SITE_FILE + ".xml"; //$NON-NLS-1$
+
+	private ISiteContentProvider siteContentProvider;
+	
+	private Map featureCache = Collections.synchronizedMap(new HashMap()); // key=URLKey value=IFeature
+	
+	/**
+	 * Constructor for Site
+	 */
+	public Site() {
+		super();
+	}
+
+	/**
+	 * Compares two sites for equality
+	 * 
+	 * @param obj site object to compare with
+	 * @return <code>true</code> if the two sites are equal, 
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 */
+	public boolean equals(Object obj) {
+		if (!(obj instanceof ISite))
+			return false;
+		if (getURL() == null)
+			return false;
+		ISite otherSite = (ISite) obj;
+
+		return UpdateManagerUtils.sameURL(getURL(), otherSite.getURL());
+	}
+
+	/**
+	 * Returns the site URL
+	 * 
+	 * @see ISite#getURL()
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		URL url = null;
+		try {
+			url = getSiteContentProvider().getURL();
+		} catch (CoreException e) {
+			UpdateCore.warn(null, e);
+		}
+		return url;
+	}
+
+	/**
+	 * Returns the site description.
+	 * 
+	 * @see ISite#getDescription()
+	 * @since 2.0
+	 */
+	public IURLEntry getDescription() {
+		return (IURLEntry) getDescriptionModel();
+	}
+
+	/**
+	 * Returns an array of categories defined by the site.
+	 * 
+	 * @see ISite#getCategories()
+	 * @since 2.0
+	 */
+	public ICategory[] getCategories() {
+		CategoryModel[] result = getCategoryModels();
+		if (result.length == 0)
+			return new ICategory[0];
+		else
+			return (ICategory[]) result;
+	}
+
+	/**
+	 * Returns the named site category.
+	 * 
+	 * @see ISite#getCategory(String)
+	 * @since 2.0
+	 */
+	public ICategory getCategory(String key) {
+		ICategory result = null;
+		boolean found = false;
+		int length = getCategoryModels().length;
+
+		for (int i = 0; i < length; i++) {
+			if (getCategoryModels()[i].getName().equals(key)) {
+				result = (ICategory) getCategoryModels()[i];
+				found = true;
+				break;
+			}
+		}
+
+		//DEBUG:
+		if (!found) {
+			String URLString = (this.getURL() != null) ? this.getURL().toExternalForm() : "<no site url>"; //$NON-NLS-1$
+			UpdateCore.warn(NLS.bind(Messages.Site_CannotFindCategory, (new String[] { key, URLString })));
+			if (getCategoryModels().length <= 0)
+				UpdateCore.warn(Messages.Site_NoCategories);	
+		}
+
+		return result;
+	}
+
+	/**
+	 * Returns an array of references to features on this site.
+	 * 
+	 * @see ISite#getFeatureReferences()
+	 * @since 2.0
+	 */
+	public ISiteFeatureReference[] getRawFeatureReferences() {
+		FeatureReferenceModel[] result = getFeatureReferenceModels();
+		if (result.length == 0)
+			return new ISiteFeatureReference[0];
+		else
+			return (ISiteFeatureReference[]) result;
+	}
+
+	/**
+	 * @see org.eclipse.update.core.ISite#getFeatureReferences()
+	 */
+	public ISiteFeatureReference[] getFeatureReferences() {
+		// only filter local site
+		if (getCurrentConfiguredSite()!=null)
+			return filterFeatures(getRawFeatureReferences());
+		else 
+			return getRawFeatureReferences();
+		
+	}
+
+	/*
+	 * Method filterFeatures.
+	 * Also implemented in Feature
+	 *  
+	 * @param list
+	 * @return List
+	 */
+	private ISiteFeatureReference[] filterFeatures(ISiteFeatureReference[] allIncluded) {
+		List list = new ArrayList();
+		if (allIncluded!=null){
+			for (int i = 0; i < allIncluded.length; i++) {
+				ISiteFeatureReference included = allIncluded[i];
+				if (UpdateManagerUtils.isValidEnvironment(included))
+					list.add(included);
+				else{
+					if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS){
+						UpdateCore.warn("Filtered out feature reference:"+included); //$NON-NLS-1$
+					}
+				}
+			}
+		}
+		
+		ISiteFeatureReference[] result = new ISiteFeatureReference[list.size()];
+		if (!list.isEmpty()){
+			list.toArray(result);
+		}
+		
+		return result;	
+	}
+
+	/**
+	 * Returns a reference to the specified feature on this site.
+	 * 
+	 * @see ISite#getFeatureReference(IFeature)
+	 * @since 2.0
+	 */
+	public ISiteFeatureReference getFeatureReference(IFeature feature) {
+
+		if (feature == null) {
+			UpdateCore.warn("Site:getFeatureReference: The feature is null"); //$NON-NLS-1$
+			return null;
+		}
+
+		ISiteFeatureReference[] references = getFeatureReferences();
+		ISiteFeatureReference currentReference = null;
+		for (int i = 0; i < references.length; i++) {
+			currentReference = references[i];
+			//if (UpdateManagerUtils.sameURL(feature.getURL(), currentReference.getURL()))
+			//	return currentReference;
+			try {
+				if (feature.getVersionedIdentifier().equals(currentReference.getVersionedIdentifier()))
+					return currentReference;
+			} catch (CoreException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+
+		UpdateCore.warn("Feature " + feature + " not found on site" + this.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
+		return null;
+	}
+
+	/**
+	 * Returns an array of plug-in and non-plug-in archives located
+	 * on this site
+	 * 
+	 * @see ISite#getArchives()
+	 * @since 2.0
+	 */
+	public IArchiveReference[] getArchives() {
+		ArchiveReferenceModel[] result = getArchiveReferenceModels();
+		if (result.length == 0)
+			return new IArchiveReference[0];
+		else
+			return (IArchiveReference[]) result;
+	}
+
+	/**
+	 * Returns the content provider for this site.
+	 * 
+	 * @see ISite#getSiteContentProvider()
+	 * @since 2.0
+	 */
+	public ISiteContentProvider getSiteContentProvider() throws CoreException {
+		if (siteContentProvider == null) {
+			throw Utilities.newCoreException(Messages.Site_NoContentProvider, null);	
+		}
+		return siteContentProvider;
+	}
+
+	/**
+	 * Returns the default type for a packaged feature supported by this site
+	 * 
+	 * @see ISite#getDefaultPackagedFeatureType()
+	 * @since 2.0
+	 */
+	public String getDefaultPackagedFeatureType() {
+		return DEFAULT_PACKAGED_FEATURE_TYPE;
+	}
+
+	/**
+	 * Returns an array of entries corresponding to plug-ins installed
+	 * on this site.
+	 * 
+	 * @see ISite#getPluginEntries()
+	 * @since 2.0
+	 */
+	public IPluginEntry[] getPluginEntries() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Returns the number of plug-ins installed on this site
+	 * 
+	 * @see ISite#getPluginEntryCount()
+	 * @since 2.0
+	 */
+	public int getPluginEntryCount() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Returns an array of entries corresponding to plug-ins that are
+	 * installed on this site and are referenced only by the specified
+	 * feature. 
+	 * 
+	 * @see ISite#getPluginEntriesOnlyReferencedBy(IFeature)	 * 
+	 * @since 2.0
+	 */
+	public IPluginEntry[] getPluginEntriesOnlyReferencedBy(IFeature feature) throws CoreException {
+
+		IPluginEntry[] pluginsToRemove = new IPluginEntry[0];
+		if (feature == null)
+			return pluginsToRemove;
+
+		// get the plugins from the feature
+		IPluginEntry[] entries = feature.getPluginEntries();
+		if (entries != null) {
+			// get all the other plugins from all the other features
+			Set allPluginID = new HashSet();
+			ISiteFeatureReference[] features = getFeatureReferences();
+			if (features != null) {
+				for (int indexFeatures = 0; indexFeatures < features.length; indexFeatures++) {
+					IFeature featureToCompare = null;
+					try {
+						featureToCompare = features[indexFeatures].getFeature(null);
+					} catch (CoreException e) {
+						UpdateCore.warn(null, e);
+					}
+					if (!feature.equals(featureToCompare)) {
+						IPluginEntry[] pluginEntries = features[indexFeatures].getFeature(null).getPluginEntries();
+						if (pluginEntries != null) {
+							for (int indexEntries = 0; indexEntries < pluginEntries.length; indexEntries++) {
+								allPluginID.add(pluginEntries[indexEntries].getVersionedIdentifier());
+							}
+						}
+					}
+				}
+			}
+
+			// create the delta with the plugins that may be still used by other configured or unconfigured feature
+			List plugins = new ArrayList();
+			for (int indexPlugins = 0; indexPlugins < entries.length; indexPlugins++) {
+				if (!allPluginID.contains(entries[indexPlugins].getVersionedIdentifier())) {
+					plugins.add(entries[indexPlugins]);
+				}
+			}
+
+			// move List into Array
+			if (!plugins.isEmpty()) {
+				pluginsToRemove = new IPluginEntry[plugins.size()];
+				plugins.toArray(pluginsToRemove);
+			}
+		}
+
+		return pluginsToRemove;
+	}
+
+	/**
+	 * Adds a new plug-in entry to this site.
+	 * This implementation always throws UnsupportedOperationException
+	 * because this implementation does not support the install action.
+	 * 
+	 * @see ISite#addPluginEntry(IPluginEntry)
+	 * @exception java.lang.UnsupportedOperationException
+	 * @since 2.0
+	 */
+	public void addPluginEntry(IPluginEntry pluginEntry) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Get download size for the specified feature on this site.
+	 * This implementation always throws UnsupportedOperationException
+	 * because this implementation does not support the install action.
+	 * 
+	 * @see ISite#getDownloadSizeFor(IFeature)
+	 * @exception java.lang.UnsupportedOperationException
+	 * @since 2.0
+	 */
+	public long getDownloadSizeFor(IFeature feature) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Get install size for the specified feature on this site.
+	 * This implementation always throws UnsupportedOperationException
+	 * because this implementation does not support the install action.
+	 * 
+	 * @see ISite#getInstallSizeFor(IFeature)
+	 * @exception java.lang.UnsupportedOperationException
+	 * @since 2.0
+	 */
+	public long getInstallSizeFor(IFeature feature) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Install the specified feature and all optional features on this site.
+	 * This implementation always throws UnsupportedOperationException
+	 * because this implementation does not support the install action.
+	 * 
+	 * @see ISite#install(IFeature, IVerificationListener, IProgressMonitor)
+	 * @exception InstallAbortedException when the user cancels the install
+	 * @exception CoreException
+	 * @exception java.lang.UnsupportedOperationException
+	 * @since 2.0
+	 */
+	public IFeatureReference install(IFeature sourceFeature, IVerificationListener verificationListener, IProgressMonitor progress) throws InstallAbortedException, CoreException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Install the specified feature and listed optional features on this site.
+	 * This implementation always throws UnsupportedOperationException
+	 * because this implementation does not support the install action.
+	 * 
+	 * @see ISite#install(IFeature, IVerificationListener, IProgressMonitor)
+	 * @exception InstallAbortedException when the user cancels the install
+	 * @exception CoreException
+	 * @exception java.lang.UnsupportedOperationException
+	 * @since 2.0
+	 */
+	public IFeatureReference install(IFeature sourceFeature, IFeatureReference[] optionalFeatures, IVerificationListener verificationListener, IProgressMonitor progress) throws InstallAbortedException, CoreException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Install the specified feature on this site using the content consumer as 
+	 * a context to install the feature in.
+	 * This implementation always throws UnsupportedOperationException
+	 * because this implementation does not support the install action.
+	 * 
+	 * @param sourceFeature feature to install
+	 * @param parentContentConsumer content consumer of the parent feature
+	 * @param parentVerifier verifier of the parent feature
+	 * @param verificationListener install verification listener
+	 * @param progress install monitor, can be <code>null</code>
+	 * @exception InstallAbortedException when the user cancels the install
+	 * @exception CoreException
+	 * @exception java.lang.UnsupportedOperationException 
+	 * @since 2.0 
+	 */
+	public IFeatureReference install(IFeature sourceFeature, IFeatureReference[] optionalFeatures, IFeatureContentConsumer parentContentConsumer, IVerifier parentVerifier, IVerificationListener verificationListener, IProgressMonitor progress)
+		throws CoreException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Remove (uninstall) the specified feature from this site.
+	 * This implementation always throws UnsupportedOperationException
+	 * because this implementation does not support the remove action.
+	 * 
+	 * @see ISite#remove(IFeature, IProgressMonitor)
+	 * @exception java.lang.UnsupportedOperationException
+	 * @since 2.0
+	 */
+	public void remove(IFeature feature, IProgressMonitor progress) throws CoreException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Sets the site content provider.
+	 * 
+	 * @see ISite#setSiteContentProvider(ISiteContentProvider)
+	 * @since 2.0
+	 */
+	public void setSiteContentProvider(ISiteContentProvider siteContentProvider) {
+		this.siteContentProvider = siteContentProvider;
+	}
+	/**
+	 * @see ISite#getCurrentConfiguredSite()
+	 */
+	public IConfiguredSite getCurrentConfiguredSite() {
+		return (IConfiguredSite) getConfiguredSiteModel();
+	}
+
+	/**
+	 * @see org.eclipse.update.core.ISite#createFeature(String, URL)
+	 * @deprecated
+	 */
+	public IFeature createFeature(String type, URL url) throws CoreException {
+		return createFeature(type,url,null);
+	}
+
+	/**
+	 * @see org.eclipse.update.core.ISite#createFeature(String, URL,
+	 * IProgressMonitor)
+	 */
+	public IFeature createFeature(String type, URL url, IProgressMonitor monitor) throws CoreException {
+
+		if(url == null) {
+			UpdateCore.warn("The feature URL passed is null");
+			return null;
+		}
+		
+		// First check the cache
+		URLKey key = new URLKey(url);
+		IFeature feature = (IFeature) featureCache.get(key);
+		if (feature != null) return feature;
+
+		// Create a new one
+		if (type == null || type.equals("")) { //$NON-NLS-1$
+			// ask the Site for the default type
+			type = getDefaultPackagedFeatureType();
+		}
+
+		IFeatureFactory factory = FeatureTypeFactory.getInstance().getFactory(type);
+		feature = factory.createFeature(url, this, monitor);
+		if (feature != null) {
+			// Add the feature to the cache
+			featureCache.put(key, feature);
+		}
+		return feature;
+	}
+
+	protected void removeFeatureFromCache(URL featureURL) {
+		URLKey key = new URLKey(featureURL);
+		featureCache.remove(key);
+	}
+
+	/**
+	 * Return an array of mirror update sites.
+	 * 
+	 * @return an array of mirror update sites
+	 * @since 2.0
+	 */
+	public IURLEntry[] getMirrorSiteEntries() {
+		URLEntryModel[] result = getMirrorSiteEntryModels();
+		if (result.length == 0)
+			return new IURLEntry[0];
+		else
+			return (IURLEntry[]) result;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteContentProvider.java
new file mode 100644
index 0000000..8f38779
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteContentProvider.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+ 
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * Base site content provider
+ * <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>
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class SiteContentProvider implements ISiteContentProvider {
+
+	private URL base;
+	private ISite site;
+
+	/**
+	 * Constructor for SiteContentProvider
+	 */
+	public SiteContentProvider(URL url) {
+		super();
+		this.base = url;
+	}
+
+	/**
+	 * Returns the URL of this site
+	 * 
+	 * @see ISiteContentProvider#getURL()
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		return base;
+	}
+
+	/**
+	 * Returns a URL for the identified archive
+	 * 
+	 * @see ISiteContentProvider#getArchiveReference(String)
+	 * @since 2.0
+	 */
+	private URL getArchiveReference1(String archiveID) throws CoreException {
+		try {
+			return new URL(getURL(), archiveID);
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(
+					NLS.bind(Messages.SiteContentProvider_ErrorCreatingURLForArchiveID, (new String[] { archiveID, getURL().toExternalForm() })),
+					e);
+		}
+	}
+
+	/**
+	 * Returns the site for this provider
+	 * 
+	 * @see ISiteContentProvider#getSite()
+	 * @since 2.0
+	 */
+	public ISite getSite() {
+		return site;
+	}
+
+	/**
+	 * Sets the site for this provider
+	 * 
+	 * @param site site for this provider
+	 * @since 2.0
+	 */
+	public void setSite(ISite site) {
+		this.site = site;
+	}
+
+	public URL getArchiveReference(String archiveId) throws CoreException {
+		URL contentURL = null;
+		
+		contentURL = getArchiveURLfor(archiveId);
+		// if there is no mapping in the site.xml
+		// for this archiveId, use the default one
+		if (contentURL==null) {
+			return getArchiveReference1(archiveId);
+		}
+		
+		return contentURL;
+	}
+
+	/**
+	 * return the URL associated with the id of teh archive for this site
+	 * return null if the archiveId is null, empty or 
+	 * if teh list of archives on the site is null or empty
+	 * of if there is no URL associated with the archiveID for this site
+	 */
+	private URL getArchiveURLfor(String archiveId) {
+		URL result = null;
+		boolean found = false;
+	
+		IArchiveReference[] siteArchives = getSite().getArchives();
+		if (siteArchives.length > 0) {
+			for (int i = 0; i < siteArchives.length && !found; i++) {
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
+					UpdateCore.debug("GetArchiveURL for:"+archiveId+" compare to "+siteArchives[i].getPath()); //$NON-NLS-1$ //$NON-NLS-2$
+				if (archiveId.trim().equalsIgnoreCase(siteArchives[i].getPath())) {
+					result = siteArchives[i].getURL();
+					found = true;
+					break;
+				}
+			}
+		}
+		return result;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteFeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteFeatureReference.java
new file mode 100644
index 0000000..7876b03
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteFeatureReference.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * Convenience implementation of a feature reference.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.IFeatureReference
+ * @see org.eclipse.update.core.model.FeatureReferenceModel
+ * @see org.eclipse.update.core.ISiteFeatureReference
+ * @see org.eclipse.update.core.SiteFeatureReferenceModel 
+ * @since 2.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class SiteFeatureReference extends SiteFeatureReferenceModel implements ISiteFeatureReference {
+
+	private List categories;
+
+	/**
+	 * Feature reference default constructor
+	 */
+	public SiteFeatureReference() {
+		super();
+	}
+
+	/**
+	 * Constructor FeatureReference.
+	 * @param ref the reference to copy
+	 */
+	public SiteFeatureReference(ISiteFeatureReference ref) {
+		super(ref);
+	}
+
+	/**
+	 * Returns an array of categories the referenced feature belong to.
+	 * 
+	 * @see ISiteFeatureReference#getCategories()
+	 * @since 2.1 
+	 */
+	public ICategory[] getCategories() {
+
+		if (categories == null) {
+			categories = new ArrayList();
+			String[] categoriesAsString = getCategoryNames();
+			for (int i = 0; i < categoriesAsString.length; i++) {
+				ICategory siteCat = getSite().getCategory(categoriesAsString[i]);
+				if (siteCat != null)
+					categories.add(siteCat);
+				else {
+					String siteURL = getSite().getURL() != null ? getSite().getURL().toExternalForm() : null;
+					UpdateCore.warn("Category " + categoriesAsString[i] + " not found in Site:" + siteURL); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+			}
+		}
+
+		if (categories.size() == 0) {
+			//there was no category defined
+			//so we add the default "Other" category
+			ICategory category = new Category(Messages.SiteCategory_other_label, Messages.SiteCategory_other_description);
+			categories.add(category);
+		}
+		
+		ICategory[] result = new ICategory[0];
+
+		if (!(categories == null || categories.isEmpty())) {
+			result = new ICategory[categories.size()];
+			categories.toArray(result);
+		}
+		return result;
+	}
+
+	/**
+	 * Adds a category to the referenced feature.
+	 * 
+	 * @see ISiteFeatureReference#addCategory(ICategory)
+	 * @since 2.1 
+	 */
+	public void addCategory(ICategory category) {
+		this.addCategoryName(category.getName());
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteFeatureReferenceModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteFeatureReferenceModel.java
new file mode 100644
index 0000000..671c69e
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteFeatureReferenceModel.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Site Feature reference model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.FeatureReference
+ * @since 2.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class SiteFeatureReferenceModel extends FeatureReference {
+
+	private List /* of String*/
+	categoryNames;
+
+
+	/**
+	 * Creates an uninitialized feature reference model object.
+	 * 
+	 * @since 2.0
+	 */
+	public SiteFeatureReferenceModel() {
+		super();
+	}
+
+	/**
+	 * Constructor FeatureReferenceModel.
+	 * @param ref
+	 */
+	public SiteFeatureReferenceModel(ISiteFeatureReference ref) {
+		super(ref);
+		if (ref instanceof SiteFeatureReferenceModel) {
+			SiteFeatureReferenceModel refModel = (SiteFeatureReferenceModel) ref;
+			setCategoryNames(refModel.getCategoryNames());
+		}
+	}
+
+
+	/**
+	 * Returns the names of categories the referenced feature belongs to.
+	 * 
+	 * @return an array of names, or an empty array.
+	 * @since 2.0
+	 */
+	public String[] getCategoryNames() {
+		if (categoryNames == null)
+			return new String[0];
+
+		return (String[]) categoryNames.toArray(new String[0]);
+	}
+
+	/**
+	 * Sets the names of categories this feature belongs to.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param categoryNames an array of category names
+	 * @since 2.0
+	 */
+	public void setCategoryNames(String[] categoryNames) {
+		assertIsWriteable();
+		if (categoryNames == null)
+			this.categoryNames = null;
+		else
+			this.categoryNames = new ArrayList(Arrays.asList(categoryNames));
+	}
+
+	/**
+	 * Adds the name of a category this feature belongs to.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param categoryName category name
+	 * @since 2.0
+	 */
+	public void addCategoryName(String categoryName) {
+		assertIsWriteable();
+		if (this.categoryNames == null)
+			this.categoryNames = new ArrayList();
+		if (!this.categoryNames.contains(categoryName))
+			this.categoryNames.add(categoryName);
+	}
+	
+	/**
+	 * Removes the name of a categorys this feature belongs to.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param categoryName category name
+	 * @since 2.0
+	 */
+	public void removeCategoryName(String categoryName) {
+		assertIsWriteable();
+		if (this.categoryNames != null)
+			this.categoryNames.remove(categoryName);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteManager.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteManager.java
new file mode 100644
index 0000000..8aeebcf
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/SiteManager.java
@@ -0,0 +1,342 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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
+ *     Laurent Fourrier (laurent@fourrier.nom.fr) - HTTP Proxy code and NetAccess Plugin
+ *******************************************************************************/
+package org.eclipse.update.core;
+
+import java.net.URL;
+
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.ILocalSite;
+import org.eclipse.update.internal.core.InternalSiteManager;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * Site Manager.
+ * A helper class used for creating site instance. 
+ * Site manager is a singleton class. It cannot be instantiated; 
+ * all functionality is provided by static methods.
+ * 
+ * <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>
+ * @see org.eclipse.update.core.ISite
+ * @see org.eclipse.update.configuration.ILocalSite
+ * @see org.eclipse.update.configuration.IConfiguredSite
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class SiteManager {
+
+	private static String os;
+	private static String ws;
+	private static String arch;
+	private static String nl;
+
+	private SiteManager() {
+	}
+
+	/** 
+	 * Returns a site object for the site specified by the argument URL.
+	 * Typically, the URL references a site manifest file on an update 
+	 * site. An update site acts as a source of features for installation
+	 * actions.
+	 * 
+	 * @param siteURL site URL
+	 * @return site object for the url
+	 * @exception CoreException
+	 * @deprecated use getSite(URL,IPogressMonitor) instead
+	 * @since 2.0 
+	 */
+	public static ISite getSite(URL siteURL) throws CoreException {
+		return InternalSiteManager.getSite(siteURL, true,null);
+	}
+
+	/** 
+	 * Returns a site object for the site specified by the argument URL.
+	 * Typically, the URL references a site manifest file on an update 
+	 * site. An update site acts as a source of features for installation
+	 * actions.
+	 * 
+	 * @param siteURL site URL
+	 * @param monitor the progress monitor
+	 * @return site object for the url or <samp>null</samp> in case a 
+	 * user canceled the connection in the progress monitor.
+	 * @exception CoreException
+	 * @since 2.1 
+	 */
+	public static ISite getSite(URL siteURL, IProgressMonitor monitor) throws CoreException {
+		return InternalSiteManager.getSite(siteURL, true, monitor);
+	}
+
+	/** 
+	 * Returns a site object for the site specified by the argument URL.
+	 * Typically, the URL references a site manifest file on an update 
+	 * site. An update site acts as a source of features for installation
+	 * actions.
+	 * 
+	 * @param siteURL site URL
+	 * @param usesCache <code>false</code> if the cache should be refreshed, and the site entirely reparsed, <code>false</code> otherwise.
+	 * @return site object for the url
+	 * @exception CoreException
+	 * @deprecated use getSite(URL,boolean,IPogressMonitor) instead
+	 * @since 2.0 
+	 */
+	public static ISite getSite(URL siteURL, boolean usesCache) throws CoreException {
+		return InternalSiteManager.getSite(siteURL, usesCache,null);
+	}
+
+	/** 
+	 * Returns a site object for the site specified by the argument URL.
+	 * Typically, the URL references a site manifest file on an update 
+	 * site. An update site acts as a source of features for installation
+	 * actions.
+	 * 
+	 * @param siteURL site URL
+	 * @param usesCache <code>false</code> if the cache should be refreshed, and the site entirely reparsed, <code>false</code> otherwise.
+	 * @param monitor the progress monitor
+	 * @return site object for the url or <samp>null</samp> in case a 
+	 * user canceled the connection in the progress monitor.
+	 * @exception CoreException
+	 * @since 2.1
+	 */
+	public static ISite getSite(URL siteURL, boolean usesCache, IProgressMonitor monitor) throws CoreException {
+		return InternalSiteManager.getSite(siteURL, usesCache, monitor);
+	}
+
+
+	/**
+	 * Returns the "local site". A local site is a logical collection
+	 * of configuration information plus one or more file system 
+	 * installation directories, represented as intividual sites. 
+	 * These are potential targets for installation actions.
+	 * 
+	 * @return the local site
+	 * @exception CoreException
+	 * @since 2.0 
+	 */
+	public static ILocalSite getLocalSite() throws CoreException {
+		return InternalSiteManager.getLocalSite();
+	}
+
+	/**
+	 * Trigger handling of newly discovered features. This method
+	 * can be called by the executing application whenever it
+	 * is invoked with the -newUpdates command line argument.
+	 * 
+	 * @throws CoreException if an error occurs.
+	 * @since 2.0
+	 * @deprecated Do not use this method
+	 */
+	public static void handleNewChanges() throws CoreException {
+	}
+	/**
+	 * Returns system architecture specification. A comma-separated list of arch
+	 * designators defined by the platform. 
+	 * 
+	 * This information is used as a hint by the installation and update
+	 * support.
+	 * 
+	 * @return system architecture specification
+	 * @since 2.1
+	 */
+	public static String getOSArch() {
+		if (arch == null)
+			arch = Platform.getOSArch();
+		return arch;
+	}
+
+	/**
+	 * Returns operating system specification. A comma-separated list of os
+	 * designators defined by the platform.
+	 * 
+	 * This information is used as a hint by the installation and update
+	 * support.
+	 *
+	 * @return the operating system specification.
+	 * @since 2.1
+	 */
+	public static String getOS() {
+		if (os == null)
+			os = Platform.getOS();
+		return os;
+	}
+
+	/**
+	 * Returns system architecture specification. A comma-separated list of arch
+	 * designators defined by the platform. 
+	 * 
+	 * This information is used as a hint by the installation and update
+	 * support.
+	 * @return system architecture specification.
+	 * @since 2.1
+	 */
+	public static String getWS() {
+		if (ws == null)
+			ws = Platform.getWS();
+		return ws;
+	}
+
+	/**
+	 * Sets the arch.
+	 * @param arch The arch to set
+	 */
+	public static void setOSArch(String arch) {
+		SiteManager.arch = arch;
+	}
+
+	/**
+	 * Sets the os.
+	 * @param os The os to set
+	 */
+	public static void setOS(String os) {
+		SiteManager.os = os;
+	}
+
+	/**
+	 * Sets the ws.
+	 * @param ws The ws to set
+	 */
+	public static void setWS(String ws) {
+		SiteManager.ws = ws;
+	}
+
+	/**
+	 * Sets the nl.
+	 * @param nl The nl to set
+	 */
+	public static void setNL(String nl) {
+		SiteManager.nl = nl;
+	}
+	
+	/**
+	 * Returns an estimate of bytes per second transfer rate for this URL
+	 * @param site the URL of the site
+	 * @return long a bytes per second estimate rate
+	 * @since 2.1
+ 	 */	
+	public static long getEstimatedTransferRate(URL site) {
+		if (site == null)
+			return 0;
+		return InternalSiteManager.getEstimatedTransferRate(site.getHost());
+	}
+
+	/**
+	 * Returns current locale
+	 * 
+	 * @return the string name of the current locale or <code>null</code>
+	 * @since 2.1
+	 */
+	public static String getNL() {
+		if (nl == null)
+			nl = Platform.getNL();
+		return nl;
+	}
+
+	/**
+	 * Returns the HTTP Proxy Server or <code>null</code> if none.
+	 * @return the HTTP proxy Server 
+	 * @deprecated clients should access the {@link IProxyService} directly
+	 */
+	public static String getHttpProxyServer() {
+		IProxyService service = UpdateCore.getPlugin().getProxyService();
+		if (service != null && service.isProxiesEnabled()) {
+			IProxyData data = service.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+			if (data != null)
+				return data.getHost();
+			
+		}
+		return null;
+	}
+	/**
+	 * Returns the HTTP Proxy Port or <code>null</code> if none
+	 * @return the HTTP proxy Port
+	 * @deprecated clients should access the {@link IProxyService} directly
+	 */
+	public static String getHttpProxyPort() {
+		IProxyService service = UpdateCore.getPlugin().getProxyService();
+		if (service != null && service.isProxiesEnabled()) {
+			IProxyData data = service.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+			if (data != null) {
+				if (data.getPort() == -1)
+					return "80";
+				return String.valueOf(data.getPort());
+			}
+			
+		}
+		return null;
+	}
+	
+	/**
+	 * Returns <code>true</code> if the connection should use the 
+	 * http proxy server, <code>false</code> otherwise
+	 * @return is the http proxy server enable
+	 * @deprecated clients should access the {@link IProxyService} directly
+	 */
+	public static boolean isHttpProxyEnable() {
+		IProxyService service = UpdateCore.getPlugin().getProxyService();
+		if (service != null && service.isProxiesEnabled()) {
+			IProxyData data = service.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+			return (data != null && data.getHost() != null);
+		}
+		return false;
+	}
+	/**
+	 * Sets the HTTP Proxy information
+	 * Sets the HTTP proxy server for the HTTP proxy server 
+	 * Sets the HTTP proxy port for the HTTP proxy server 
+	 * If the proxy name is <code>null</code> or the proxy port is
+	 * <code>null</code> the connection will not use HTTP proxy server.
+	 * 
+	 * @param enable <code>true</code> if the connection should use an http
+	 * proxy server, <code>false </code> otherwise.
+	 * @param httpProxyServer the HTTP proxy server name or IP address
+	 * @param httpProxyPort the HTTP proxy port
+	 * 
+	 * @deprecated clients should use the {@link IProxyService} directly
+	 */
+	public static void setHttpProxyInfo(boolean enable, String httpProxyServer, String httpProxyPort) {
+		IProxyService service = UpdateCore.getPlugin().getProxyService();
+		if (service == null)
+			return;
+		// Make sure that the proxy service is enabled if needed but don't disable the
+		// service if the http proxy is being disabled
+		if (enable && !service.isProxiesEnabled())
+			service.setProxiesEnabled(enable);
+
+		if (service.isProxiesEnabled()) {
+			IProxyData data = service.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+			if (data != null) {
+				data.setHost(httpProxyServer);
+				if (httpProxyPort == null || httpProxyPort.equals("80")) {
+					data.setPort(-1);
+				} else {
+					try {
+						int port = Integer.parseInt(httpProxyPort);
+						data.setPort(port);
+					} catch (NumberFormatException e) {
+						UpdateCore.log(e);
+					}
+				}
+				try {
+					service.setProxyData(new IProxyData[] { data });
+				} catch (CoreException e) {
+					UpdateCore.log(e);
+				}
+			}
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/URLEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/URLEntry.java
new file mode 100644
index 0000000..21342b7
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/URLEntry.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * Convenience implementation of an annotated URL.
+ * <p>
+ * This class may be instantiated or subclassed 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>
+ * @see org.eclipse.update.core.IURLEntry
+ * @see org.eclipse.update.core.model.URLEntryModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class URLEntry extends URLEntryModel implements IURLEntry {
+
+	/**
+	 * Default constructor for annotated URL
+	 * 
+	 * @since 2.0
+	 */
+	public URLEntry() {
+		super();
+	}
+
+	/**
+	 * Returns a string representation of an annotated URL.
+	 * 
+	 * @return annotated url as String
+	 * @since 2.0
+	 */
+	public String toString() {
+		String result = "IURLEntry: "; //$NON-NLS-1$
+		String URLString =
+			(getURL() == null) ? Messages.Feature_NoURL : getURL().toExternalForm();	
+		result =
+			result
+				+ ((getAnnotation() == null) ? URLString : getAnnotation() + " : " + URLString); //$NON-NLS-1$
+		return result;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/Utilities.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/Utilities.java
new file mode 100644
index 0000000..ccde29f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/Utilities.java
@@ -0,0 +1,353 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import java.io.*;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * This class is a collection of utility functions that can be 
+ * used for install processing
+ * <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>
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class Utilities {
+
+	private static Map entryMap;
+	private static final DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault());
+	private static long tmpseed = (new Date()).getTime();
+	private static String dirRoot = null;
+
+	/**
+	 * Returns a new working directory (in temporary space). Ensures
+	 * the directory exists. Any directory levels that had to be created
+	 * are marked for deletion on exit.
+	 * 
+	 * @return working directory
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public static synchronized File createWorkingDirectory() throws IOException {
+
+		if (dirRoot == null) {
+			dirRoot = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
+			// in Linux, returns '/tmp', we must add '/'
+			if (!dirRoot.endsWith(File.separator))
+				dirRoot += File.separator;
+
+			// on Unix/Linux, the temp dir is shared by many users, so we need to ensure 
+			// that the top working directory is different for each user
+			if (!Platform.getOS().equals("win32")) { //$NON-NLS-1$
+				String home = System.getProperty("user.home"); //$NON-NLS-1$
+				home = Integer.toString(home.hashCode());
+				dirRoot += home + File.separator;
+			}
+			dirRoot += "eclipse" + File.separator + ".update" + File.separator + Long.toString(tmpseed) + File.separator; //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		String tmpName = dirRoot + Long.toString(++tmpseed) + File.separator;
+
+		File tmpDir = new File(tmpName);
+		verifyPath(tmpDir, false);
+		if (!tmpDir.exists())
+			throw new FileNotFoundException(tmpName);
+		return tmpDir;
+	}
+
+	/**
+	 * Create a new working file. The file is marked for deletion on exit.
+	 * 
+	 * @see #lookupLocalFile(String)
+	 * @param tmpDir directory location for new file. Any missing directory
+	 * levels are created (and marked for deletion on exit)
+	 * @param name optional file name, or <code>null</code>. If name is not
+	 * specified, a temporary name is generated.
+	 * @return created working file
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public static synchronized File createLocalFile(File tmpDir, String name) throws IOException {
+		// create the local file
+		File temp;
+		String filePath;
+		if (name != null) {
+			// create file with specified name
+			filePath = name.replace('/', File.separatorChar);
+			if (filePath.startsWith(File.separator))
+				filePath = filePath.substring(1);
+			temp = new File(tmpDir, filePath);
+		} else {
+			// create file with temp name
+			temp = File.createTempFile("eclipse", null, tmpDir); //$NON-NLS-1$
+		}
+		temp.deleteOnExit();
+		verifyPath(temp, true);
+
+		return temp;
+	}
+
+	/**
+	 * The file is associated with a lookup key.
+	 * @param key optional lookup key, or <code>null</code>.
+	 * @param temp the local working file
+	 * @since 2.0.2
+	 */
+	public synchronized static void mapLocalFile(String key, File temp) {
+		// create file association 
+		if (key != null) {
+			if (entryMap == null)
+				entryMap = new HashMap();
+			entryMap.put(key, temp);
+		}
+	}
+
+	/**
+	 * Returns a previously cached local file (in temporary area) matching the
+	 * specified key. 
+	 * 
+	 * @param key lookup key
+	 * @return cached file, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public static synchronized File lookupLocalFile(String key) {
+		if (entryMap == null)
+			return null;
+		return (File) entryMap.get(key);
+	}
+
+	/**
+	 * Flushes all the keys from the local file map.
+	 * Reinitialize the cache.
+     *
+	 * @since 2.1
+	 */
+	public synchronized static void flushLocalFile() {
+		entryMap = null;
+	}
+
+	/**
+	 * Removes the specified key from the local file map. The file is
+	 * not actually deleted until VM termination.
+	 * 
+	 * @param key lookup key
+	 * @since 2.0
+	 */
+	public static synchronized void removeLocalFile(String key) {
+		if (entryMap != null)
+			entryMap.remove(key);
+	}
+
+	/**
+	 * Copies specified input stream to the output stream. Neither stream
+	 * is closed as part of this operation.
+	 * 
+	 * @param is input stream
+	 * @param os output stream
+	 * @param monitor progress monitor
+	 * @exception IOException
+	 * @exception InstallAbortedException
+	 * @since 2.0
+	 */
+	public static void copy(InputStream is, OutputStream os, InstallMonitor monitor) throws IOException, InstallAbortedException {
+		long offset = UpdateManagerUtils.copy(is, os, monitor, 0);
+		if (offset != -1) {
+			if (monitor != null && monitor.isCanceled()) {
+				String msg = Messages.Feature_InstallationCancelled; 
+				throw new InstallAbortedException(msg, null);
+			} else {
+				throw new IOException();
+			}
+		}
+	}
+
+	/**
+	 * Creates a CoreException from some other exception.
+	 * The type of the CoreException is <code>IStatus.ERROR</code>
+	 * If the exception passed as a parameter is also a CoreException,
+	 * the new CoreException will contain all the status of the passed
+	 * CoreException.
+	 * 
+	 * @see IStatus#ERROR
+	 * @param s exception string
+	 * @param code the code reported
+	 * @param e actual exception being reported
+	 * @return a CoreException
+	 * @since 2.0
+	 */
+	public static CoreException newCoreException(String s, int code, Throwable e) {
+		String id = UpdateCore.getPlugin().getBundle().getSymbolicName();
+
+		// check the case of a multistatus
+		IStatus status;
+		if (e instanceof FeatureDownloadException)
+			return (FeatureDownloadException)e;
+		else if (e instanceof CoreException) {
+			if (s == null)
+				s = ""; //$NON-NLS-1$
+			status = new MultiStatus(id, code, s, e);
+			IStatus childrenStatus = ((CoreException) e).getStatus();
+			((MultiStatus) status).add(childrenStatus);
+			((MultiStatus) status).addAll(childrenStatus);
+		} else {
+			StringBuffer completeString = new StringBuffer(""); //$NON-NLS-1$
+			if (s != null)
+				completeString.append(s);
+			if (e != null) {
+				completeString.append(" ["); //$NON-NLS-1$
+				String msg = e.getLocalizedMessage();
+				completeString.append(msg!=null?msg:e.toString());
+				completeString.append("]"); //$NON-NLS-1$
+			}
+			status = new Status(IStatus.ERROR, id, code, completeString.toString(), e);
+		}
+		CoreException ce = new CoreException(status);
+		
+		if ( e instanceof FatalIOException) {
+			ce = new CoreExceptionWithRootCause(status);
+			((CoreExceptionWithRootCause)ce).setRootException(e);
+		}
+		/* for when we move to 1.5
+		 if ( e instanceof CoreException) {
+			ce.initCause(e.getCause());
+		} else {
+			ce.initCause(e);
+		}
+		if (e != null)
+			ce.setStackTrace(e.getStackTrace());*/
+		return ce; 
+	}
+
+	/**
+	 * Creates a CoreException from some other exception.
+	 * The type of the CoreException is <code>IStatus.ERROR</code>
+	 * If the exception passed as a parameter is also a CoreException,
+	 * the new CoreException will contain all the status of the passed
+	 * CoreException.
+	 * 
+	 * @see IStatus#ERROR
+	 * @param s exception string
+	 * @param e actual exception being reported
+	 * @return a CoreException
+	 * @since 2.0
+	 */
+	public static CoreException newCoreException(String s, Throwable e) {
+		return newCoreException(s, IStatus.OK, e);
+	}
+
+	/**
+	 * Creates a CoreException from two other CoreException
+	 * 
+	 * @param s overall exception string
+	 * @param s1 string for first detailed exception
+	 * @param s2 string for second detailed exception
+	 * @param e1 first detailed exception
+	 * @param e2 second detailed exception
+	 * @return a CoreException with multi-status
+	 * @since 2.0
+	 */
+	public static CoreException newCoreException(String s, String s1, String s2, CoreException e1, CoreException e2) {
+		String id = UpdateCore.getPlugin().getBundle().getSymbolicName();
+		if (s == null)
+			s = ""; //$NON-NLS-1$
+
+		IStatus childStatus1 = e1.getStatus();
+		IStatus childStatus2 = e2.getStatus();
+		int code = (childStatus1.getCode() == childStatus2.getCode()) ? childStatus1.getCode() : IStatus.OK;
+		MultiStatus multi = new MultiStatus(id, code, s, null);
+
+		multi.add(childStatus1);
+		multi.addAll(childStatus1);
+		multi.add(childStatus2);
+		multi.addAll(childStatus2);
+
+		return new CoreException(multi); 
+	}
+
+	/**
+	 * Formats a Date based on the default Locale 
+	 * If teh Date is <code>null</code> returns an empty String
+	 * 
+	 * @param date the Date to format
+	 * @return the formatted Date as a String
+	 * @since 2.0
+	 */
+	public static String format(Date date) {
+		if (date == null)
+			return ""; //$NON-NLS-1$
+		return dateFormat.format(date);
+	}
+
+	/**
+	 * Perform shutdown processing for temporary file handling.
+	 * This method is called when platform is shutting down.
+	 * It is not intended to be called at any other time under
+	 * normal circumstances. A side-effect of calling this method
+	 * is that the contents of the temporary directory managed 
+	 * by this class are deleted. 
+	 * 
+	 * @since 2.0
+	 */
+	public static void shutdown() {
+		if (dirRoot == null)
+			return;
+
+		File temp = new File(dirRoot); // temp directory root for this run
+		cleanupTemp(temp);
+		temp.delete();
+	}
+
+	private static void cleanupTemp(File root) {
+		File[] files = root.listFiles();
+		for (int i = 0; files != null && i < files.length; i++) {
+			if (files[i].isDirectory())
+				cleanupTemp(files[i]);
+			files[i].delete();
+		}
+	}
+
+	private static void verifyPath(File path, boolean isFile) {
+		// if we are expecting a file back off 1 path element
+		if (isFile) {
+			if (path.getAbsolutePath().endsWith(File.separator)) {
+				// make sure this is a file
+				path = path.getParentFile();
+				isFile = false;
+			}
+		}
+
+		// already exists ... just return
+		if (path.exists())
+			return;
+
+		// does not exist ... ensure parent exists
+		File parent = path.getParentFile();
+		verifyPath(parent, false);
+
+		// ensure directories are made. Mark files or directories for deletion
+		if (!isFile)
+			path.mkdir();
+		path.deleteOnExit();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/Verifier.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/Verifier.java
new file mode 100644
index 0000000..3692132
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/Verifier.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+ 
+import org.eclipse.core.runtime.*;
+
+/**
+ * Convenience implementation of a verifier.
+ * <p>
+ * This class may be subclassed 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>
+ * @see org.eclipse.update.core.IVerifier
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class Verifier implements IVerifier {
+	
+	private IVerifier parent;
+
+	/**
+	 * @see IVerifier#verify(IFeature, ContentReference, boolean, InstallMonitor)
+	 */
+	public abstract IVerificationResult verify(
+		IFeature feature,
+		ContentReference reference,
+		boolean isFeatureVerification,
+		InstallMonitor monitor)
+		throws CoreException ;
+
+	/**
+	 * @see IVerifier#verify(IFeature, ContentReference, boolean, InstallMonitor)
+	 */
+	public void setParent(IVerifier parentVerifier){
+		if (this.parent==null){
+			this.parent = parentVerifier;
+		}
+	}
+	
+	/**
+	 * Returns the parent verifier
+	 * 
+	 * @return the parent verifier
+	 * @since 2.0
+	 */
+	public IVerifier getParent(){
+		return parent;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/VersionedIdentifier.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/VersionedIdentifier.java
new file mode 100644
index 0000000..a578772
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/VersionedIdentifier.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * Versioned Identifier. This is a utility class combining an identification
+ * string with a version.
+ * <p>
+ * Clients may instantiate; not intended to be subclassed 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>
+ * @see org.eclipse.core.runtime.PluginVersionIdentifier
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class VersionedIdentifier {
+	private String id;
+	private PluginVersionIdentifier version;
+	private static final String SEPARATOR = "_"; //$NON-NLS-1$
+
+	/**
+	 * Construct a versioned identifier from an identifier and a string
+	 * representation of a version
+	 * 
+	 * @see org.eclipse.core.runtime.PluginVersionIdentifier#toString()
+	 * @param id identifier string
+	 * @param versionName string representation of version
+	 * @since 2.0
+	 */
+	public VersionedIdentifier(String id, String versionName) {
+		if (id == null	|| (id = id.trim()).equals("")) //$NON-NLS-1$
+			throw new IllegalArgumentException(
+				NLS.bind(Messages.VersionedIdentifier_IdOrVersionNull, (new String[] { id, versionName })));
+		this.id = id;
+		// 15707
+		if (versionName != null){
+			// if (PluginVersionIdentifier.validateVersionIdentifier(versionName).isOk())
+			try {
+				this.version = new PluginVersionIdentifier(versionName);
+			} catch (RuntimeException e){
+				UpdateCore.warn("Invalid Version:"+versionName,e); //$NON-NLS-1$
+			}
+		}
+		if (this.version==null)
+			version = new PluginVersionIdentifier(0, 0, 0);
+	}
+
+	/**
+	 * Returns the identifier
+	 * 
+	 * @return identifier
+	 * @since 2.0
+	 */
+	public String getIdentifier() {
+		return id;
+	}
+
+	/**
+	 * Returns the version
+	 * 
+	 * @return version
+	 * @since 2.0
+	 */
+	public PluginVersionIdentifier getVersion() {
+		return version;
+	}
+
+	/**
+	 * Returns a string representation of the versioned identifier.
+	 * 
+	 * @return string representation of versioned identifier. The resulting 
+	 * string is <id>_<version>, where <id> is the identifier and 
+	 * <version> is the string representation of the version
+	 * @since 2.0
+	 */
+	public String toString() {
+		return id.equals("") ? "" : id + SEPARATOR + version.toString(); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * Compares two versioned identifiers for equality
+	 * 
+	 * @param obj other versioned identifier to compare to
+	 * @return <code>true</code> if the two objects are equal, 
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 */
+	public boolean equals(Object obj) {
+		if (!(obj instanceof VersionedIdentifier))
+			return false;
+		VersionedIdentifier vid = (VersionedIdentifier) obj;
+		if (!this.id.equals(vid.id))
+			return false;
+		return this.version.equals(vid.version);
+	}
+
+	/**
+	 * Returns a computed hashcode for the versioned identifier.
+	 * 
+	 * @return hash code
+	 * @since 2.0
+	 */
+	public int hashCode() {
+		return toString().hashCode();
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ArchiveReferenceModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ArchiveReferenceModel.java
new file mode 100644
index 0000000..7359dec
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ArchiveReferenceModel.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Site archive model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.ArchiveReference
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class ArchiveReferenceModel extends ModelObject {
+
+	private String path;
+	private String urlString;
+	private URL url;
+
+	/**
+	 * Creates a uninitialized model object.
+	 *  
+	 * @since 2.0
+	 */
+	public ArchiveReferenceModel() {
+		super();
+	}
+
+	/**
+	 * Retrieve the site archive "symbolic" path
+	 *
+	 * @return path, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getPath() {
+		return path;
+	}
+
+	/**
+	 * Returns the unresolved URL string for the archive.
+	 *
+	 * @return url string, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getURLString() {
+		return urlString;
+	}
+
+	/**
+	 * Returns the resolved URL for the archive.
+	 * 
+	 * @return url, or <code>null</code>
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		return url;
+	}
+
+	/**
+	 * Sets the site archive "symbolic" path.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param path archive "symbolic" path.
+	 * @since 2.0
+	 */
+	public void setPath(String path) {
+		assertIsWriteable();
+		this.path = path;
+	}
+
+	/**
+	 * Sets the unresolved URL string for the archive.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param urlString unresolved url string.
+	 * @since 2.0
+	 */
+	public void setURLString(String urlString) {
+		assertIsWriteable();
+		this.urlString = urlString;
+		this.url = null;
+	}
+
+	/**
+	 * Resolve the model object.
+	 * Any URL strings in the model are resolved relative to the 
+	 * base URL argument. Any translatable strings in the model that are
+	 * specified as translation keys are localized using the supplied 
+	 * resource bundle.
+	 * 
+	 * @param base URL
+	 * @param bundleURL resource bundle URL
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base,URL bundleURL)
+		throws MalformedURLException {
+		// resolve local elements
+		url = resolveURL(base, bundleURL, urlString);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/CategoryModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/CategoryModel.java
new file mode 100644
index 0000000..a99d88b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/CategoryModel.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Comparator;
+
+import org.eclipse.update.core.Site;
+
+/**
+ * Feature category definition model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.Category
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class CategoryModel extends ModelObject {
+
+	private String name;
+	private String label;
+	private String localizedLabel;
+	private URLEntryModel description;
+	private static Comparator comp;
+
+	/**
+	 * Creates an uninitialized model object.
+	 * 
+	 * @since 2.0
+	 */
+	public CategoryModel() {
+		super();
+	}
+
+	/**
+	 * Retrieve the name of the category.
+	 * 
+	 * @return category name, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Retrieve the displayable label for the category. If the model
+	 * object has been resolved, the label is localized.
+	 * 
+	 * @return displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getLabel() {
+		if (localizedLabel != null)
+			return localizedLabel;
+		else
+			return label;
+	}
+
+	/**
+	 * Retrieve the non-localized displayable label for the category.
+	 * 
+	 * @return non-localized displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getLabelNonLocalized() {
+		return label;
+	}
+
+	/**
+	 * Retrieve the detailed category description
+	 * 
+	 * @return category description, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public URLEntryModel getDescriptionModel() {
+		return description;
+	}
+
+	/**
+	 * Sets the category displayable label.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param label displayable label, or resource key
+	 * @since 2.0
+	 */
+	public void setLabel(String label) {
+		assertIsWriteable();
+		this.label = label;
+		this.localizedLabel = null;
+	}
+
+	/**
+	 * Sets the category name.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param name category name
+	 * @since 2.0
+	 */
+	public void setName(String name) {
+		assertIsWriteable();
+		this.name = name;
+	}
+
+	/**
+	 * Sets the category description.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param description category description
+	 * @since 2.0
+	 */
+	public void setDescriptionModel(URLEntryModel description) {
+		assertIsWriteable();
+		this.description = description;
+	}
+
+	/**
+	 * Marks the model object as read-only.
+	 * 
+	 * @since 2.0
+	 */
+	public void markReadOnly() {
+		super.markReadOnly();
+		markReferenceReadOnly(getDescriptionModel());
+	}
+
+	/**
+	 * Resolve the model object.
+	 * Any URL strings in the model are resolved relative to the 
+	 * base URL argument. Any translatable strings in the model that are
+	 * specified as translation keys are localized using the supplied 
+	 * resource bundle.
+	 * 
+	 * @param base URL
+	 * @param bundleURL resource bundle URL
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base,URL bundleURL)
+		throws MalformedURLException {
+		// resolve local elements
+		localizedLabel = resolveNLString(bundleURL, label);
+
+		// delegate to references
+		resolveReference(getDescriptionModel(),base, bundleURL);
+	}
+
+	/**
+	 * Compare two category models for equality.
+	 * 
+	 * @see Object#equals(Object)
+	 * @since 2.0
+	 */
+	public boolean equals(Object obj) {
+		boolean result = false;
+		if (obj instanceof CategoryModel) {
+			CategoryModel otherCategory = (CategoryModel) obj;
+			result = getName().equalsIgnoreCase(otherCategory.getName());
+		}
+		return result;
+	}
+
+	/**
+	 * Compute hash code for category model.
+	 * 
+	 * @see Object#hashCode()
+	 * @since 2.0
+	 */
+	public int hashCode() {
+		return getName().hashCode();
+	}
+
+	/**
+	 * Returns a comparator for category models.
+	 * 
+	 * @return comparator
+	 * @since 2.0
+	 */
+	public static Comparator getComparator() {
+		if (comp == null) {
+			comp = new Comparator() {
+				/*
+				 * @see Comparator#compare(Object,Object)
+				 * Returns 0 if versions are equal.
+				 * Returns -1 if object1 is after than object2.
+				 * Returns +1 if object1 is before than object2.
+				 */
+				public int compare(Object o1, Object o2) {
+
+					CategoryModel cat1 = (CategoryModel) o1;
+					CategoryModel cat2 = (CategoryModel) o2;
+
+					if (cat1.equals(cat2))
+						return 0;
+					return cat1.getName().compareTo(cat2.getName());
+				}
+			};
+		}
+		return comp;
+	}
+	
+	/**
+	 * @see org.eclipse.update.core.model.ModelObject#getPropertyName()
+	 */
+	protected String getPropertyName() {
+		return Site.SITE_FILE;
+	}	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ContentEntryModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ContentEntryModel.java
new file mode 100644
index 0000000..cf71f8d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ContentEntryModel.java
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+
+/**
+ * Content entry model object.
+ * This is the base class for plug-in and non-plug-in entry models.
+ * <p>
+ * This class must be subclassed 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>
+ * @see org.eclipse.update.core.model.PluginEntryModel
+ * @see org.eclipse.update.core.model.NonPluginEntryModel
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class ContentEntryModel extends ModelObject {
+
+	/**
+	 * An indication the size could not be determined
+	 * 
+	 * @since 2.0
+	 */
+	public static final long UNKNOWN_SIZE = -1;
+
+	private long downloadSize = UNKNOWN_SIZE;
+	private long installSize = UNKNOWN_SIZE;
+	private String os;
+	private String ws;
+	private String nl;
+	private String arch;
+
+	/**
+	 * Creates a uninitialized content entry model object.
+	 * 
+	 * @since 2.0
+	 */
+	protected ContentEntryModel() {
+		super();
+	}
+
+	/**
+	 * Returns the download size of the entry, if it can be determined.
+	 * 
+	 * @return download size of the entry in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0 
+	 */
+
+	public long getDownloadSize() {
+		return downloadSize;
+	}
+
+	/**
+	 * Returns the install size of the entry, if it can be determined.
+	 * 
+	 * @return install size of the entry in KiloBytes, or an indication 
+	 * the size could not be determined
+	 * @since 2.0
+	 */
+	public long getInstallSize() {
+		return installSize;
+	}
+
+	/**
+	 * Returns optional operating system specification.
+	 *
+	 * @return the operating system specification or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getOS() {
+		return os;
+	}
+
+	/**
+	 * Returns optional windowing system specification.
+	 *
+	 * @return the windowing system specification or <code>null</code>.
+	 * @since 2.0 
+	 */
+
+	public String getWS() {
+		return ws;
+	}
+
+	/**
+	 * Returns optional system architecture specification. 
+	 * 
+	 * @return the system architecture specification or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getOSArch() {
+		return arch;
+	}
+
+	/**
+	 * Returns optional locale specification.
+	 *
+	 * @return the locale specification, or <code>null</code>.
+	 * @since 2.0 
+	 */
+	public String getNL() {
+		return nl;
+	}
+
+	/**
+	 * Sets the download size of the entry.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param downloadSize download size of the entry in KiloBytes
+	 * @since 2.0 
+	 */
+	public void setDownloadSize(long downloadSize) {
+		assertIsWriteable();
+		if (downloadSize < 0)
+			this.downloadSize = UNKNOWN_SIZE;
+		else
+			this.downloadSize = downloadSize;
+	}
+
+	/**
+	 * Sets the install size of the entry.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param installSize install size of the entry in KiloBytes
+	 * @since 2.0
+	 */
+	public void setInstallSize(long installSize) {
+		assertIsWriteable();
+		if (installSize < 0)
+			this.installSize = UNKNOWN_SIZE;
+		else
+			this.installSize = installSize;
+	}
+
+	/**
+	 * Sets the operating system specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param os comma-separated list of OS identifiers as defined by Eclipse.
+	 * @since 2.0
+	 */
+	public void setOS(String os) {
+		assertIsWriteable();
+		this.os = os;
+	}
+
+	/**
+	 * Sets the windowing system specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param ws comma-separated list of WS identifiers as defined by Eclipse.
+	 * @since 2.0
+	 */
+	public void setWS(String ws) {
+		assertIsWriteable();
+		this.ws = ws;
+	}
+
+	/**
+	 * Sets the system architecture specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param arch comma-separated list of arch identifiers as defined by Eclipse.
+	 * @since 2.0
+	 */
+	public void setArch(String arch) {
+		assertIsWriteable();
+		this.arch = arch;
+	}
+
+	/**
+	 * Sets the locale specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param nl comma-separated list of locale identifiers.
+	 * @since 2.0
+	 */
+	public void setNL(String nl) {
+		assertIsWriteable();
+		this.nl = nl;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/DefaultFeatureParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/DefaultFeatureParser.java
new file mode 100644
index 0000000..89f9738
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/DefaultFeatureParser.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.update.internal.core.InternalFeatureParser;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Default feature parser.
+ * Parses the feature manifest file as defined by the platform. Defers
+ * to a model factory to create the actual concrete model objects. The 
+ * update framework supplies two factory implementations:
+ * <ul>
+ * <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>
+ * <li>@see org.eclipse.update.core.model.FeatureModelFactory
+ * <li>@see org.eclipse.update.core.BaseFeatureFactory
+ * </ul>
+ * 
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class DefaultFeatureParser extends DefaultHandler {
+	
+	private InternalFeatureParser featureParser;
+
+
+	// Current State Information
+	//Stack stateStack = new Stack();
+
+	// Current object stack (used to hold the current object we are
+	// populating in this plugin descriptor
+	//Stack objectStack = new Stack();
+
+	/**
+	 * Constructs a feature parser.
+	 * 
+	 * @since 2.0
+	 */
+	public DefaultFeatureParser() {
+		super();
+		featureParser = new InternalFeatureParser();
+	}
+
+	public void init(FeatureModelFactory factory) {
+		init(factory, null);
+	}
+    
+    /**
+     * @param factory
+     * @param location
+     * @since 3.1
+     */
+    public void init(FeatureModelFactory factory, String location) {
+    	
+    	this.featureParser.init(factory, location);
+    }
+
+	/**
+	 * Parses the specified input steam and constructs a feature model.
+	 * The input stream is not closed as part of this operation.
+	 * 
+	 * @param in input stream
+	 * @return feature model
+	 * @exception SAXException
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public FeatureModel parse(InputStream in) throws SAXException, IOException {
+		return featureParser.parse(in);
+	}
+
+	/**
+	 * Returns all status objects accumulated by the parser.
+	 *
+	 * @return multi-status containing accumulated status, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public MultiStatus getStatus() {
+		return featureParser.getStatus();
+	}
+
+	/**
+	 * Handle start of element tags
+	 * @see DefaultHandler#startElement(String, String, String, Attributes)
+	 * @since 2.0
+	 */
+	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+		featureParser.startElement(uri, localName, qName, attributes);
+		
+
+	}
+
+	/**
+	 * Handle end of element tags
+	 * @see DefaultHandler#endElement(String, String, String)
+	 * @since 2.0
+	 */
+	public void endElement(String uri, String localName, String qName) {
+		featureParser.endElement(uri, localName, qName);
+	}
+	
+
+	/**
+	 * Handle character text
+	 * @see DefaultHandler#characters(char[], int, int)
+	 * @since 2.0
+	 */
+	public void characters(char[] ch, int start, int length) {
+		featureParser.characters(ch, start, length);
+	}
+
+	/**
+	 * Handle errors
+	 * @see DefaultHandler#error(SAXParseException)
+	 * @since 2.0
+	 */
+	public void error(SAXParseException ex) {
+		featureParser.error(ex);
+	}
+
+	/**
+	 * Handle fatal errors
+	 * @see DefaultHandler#fatalError(SAXParseException)
+	 * @exception SAXException
+	 * @since 2.0
+	 */
+	public void fatalError(SAXParseException ex) throws SAXException {
+		featureParser.fatalError(ex);
+	}
+
+	/**
+	 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+	 */
+	public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
+		featureParser.ignorableWhitespace(arg0, arg1, arg2);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/DefaultSiteParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/DefaultSiteParser.java
new file mode 100644
index 0000000..e60d979
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/DefaultSiteParser.java
@@ -0,0 +1,890 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.IURLEntry;
+import org.eclipse.update.core.SiteFeatureReferenceModel;
+import org.eclipse.update.core.URLEntry;
+import org.eclipse.update.internal.core.ExtendedSite;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Default site parser.
+ * Parses the site manifest file as defined by the platform. Defers
+ * to a model factory to create the actual concrete model objects. The 
+ * update framework supplies two factory implementations:
+ * <ul>
+ * <li>@see org.eclipse.update.core.model.SiteModelFactory
+ * <li>@see org.eclipse.update.core.BaseSiteFactory
+ * </ul>
+ * 
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class DefaultSiteParser extends DefaultHandler {
+	
+	private final static SAXParserFactory parserFactory =
+		SAXParserFactory.newInstance();
+	
+	private SAXParser parser;
+	private SiteModelFactory factory;
+
+	private MultiStatus status;
+
+	private boolean DESCRIPTION_SITE_ALREADY_SEEN = false;
+
+	private static final int STATE_IGNORED_ELEMENT = -1;
+	private static final int STATE_INITIAL = 0;
+	private static final int STATE_SITE = 1;
+	private static final int STATE_FEATURE = 2;
+	private static final int STATE_ARCHIVE = 3;
+	private static final int STATE_CATEGORY = 4;
+	private static final int STATE_CATEGORY_DEF = 5;
+	private static final int STATE_DESCRIPTION_SITE = 6;
+	private static final int STATE_DESCRIPTION_CATEGORY_DEF = 7;
+	private static final String PLUGIN_ID = UpdateCore.getPlugin().getBundle().getSymbolicName();
+
+	private static final String SITE = "site"; //$NON-NLS-1$
+	private static final String FEATURE = "feature"; //$NON-NLS-1$
+	private static final String ARCHIVE = "archive"; //$NON-NLS-1$
+	private static final String CATEGORY_DEF = "category-def"; //$NON-NLS-1$
+	private static final String CATEGORY = "category"; //$NON-NLS-1$
+	private static final String DESCRIPTION = "description"; //$NON-NLS-1$
+	private static final String MIRROR = "mirror"; //$NON-NLS-1$
+	//private static final String ASSOCIATE_SITES = "associateSites"; //$NON-NLS-1$
+	private static final String ASSOCIATE_SITE = "associateSite"; //$NON-NLS-1$
+
+	private static final String DEFAULT_INFO_URL = "index.html"; //$NON-NLS-1$
+	private static final String FEATURES = "features/"; //$NON-NLS-1$
+    
+	// Current State Information
+	Stack stateStack = new Stack();
+
+	// Current object stack (used to hold the current object we are
+	// populating in this plugin descriptor
+	Stack objectStack = new Stack();
+
+	private int currentState;
+
+	/**
+	 * Constructs a site parser.
+	 */
+	public DefaultSiteParser() {
+		super();
+		try {
+			parserFactory.setNamespaceAware(true);
+			this.parser = parserFactory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+			UpdateCore.log(e);
+		} catch (SAXException e) {
+			UpdateCore.log(e);
+		}
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("Created"); //$NON-NLS-1$
+	}
+
+	public void init(SiteModelFactory factory) {
+		// PERF: separate instance creation from parsing
+		this.factory = factory;
+		stateStack = new Stack();
+		objectStack = new Stack();
+		status = null;
+		DESCRIPTION_SITE_ALREADY_SEEN = false;
+	}
+
+	/**
+	 * Parses the specified input steam and constructs a site model.
+	 * The input stream is not closed as part of this operation.
+	 * 
+	 * @param in input stream
+	 * @return site model
+	 * @exception SAXException
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public SiteModel parse(InputStream in) throws SAXException, IOException {
+		stateStack.push(new Integer(STATE_INITIAL));
+		currentState = ((Integer) stateStack.peek()).intValue();
+		parser.parse(new InputSource(in), this);
+		if (objectStack.isEmpty())
+			throw new SAXException(Messages.DefaultSiteParser_NoSiteTag);	
+		else {
+			if (objectStack.peek() instanceof SiteModel) {
+				return (SiteModel) objectStack.pop();
+			} else {
+				String stack = ""; //$NON-NLS-1$
+				Iterator iter = objectStack.iterator();
+				while (iter.hasNext()) {
+					stack = stack + iter.next().toString() + "\r\n"; //$NON-NLS-1$
+				}
+				throw new SAXException(NLS.bind(Messages.DefaultSiteParser_WrongParsingStack, (new String[] { stack })));
+			}
+		}
+	}
+
+	/**
+	 * Returns all status objects accumulated by the parser.
+	 *
+	 * @return multi-status containing accumulated status, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public MultiStatus getStatus() {
+		return status;
+	}
+
+	/**
+	 * Handle start of element tags
+	 * @see DefaultHandler#startElement(String, String, String, Attributes)
+	 * @since 2.0
+	 */
+	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+			debug("State: " + currentState); //$NON-NLS-1$
+			debug("Start Element: uri:" + uri + " local Name:" + localName + " qName:" + qName);//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+
+		switch (currentState) {
+			case STATE_IGNORED_ELEMENT :
+				internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] { localName, getState(currentState) })));
+				break;
+			case STATE_INITIAL :
+				handleInitialState(localName, attributes);
+				break;
+
+			case STATE_SITE :
+				handleSiteState(localName, attributes);
+				break;
+
+			case STATE_FEATURE :
+				handleFeatureState(localName, attributes);
+				break;
+
+			case STATE_ARCHIVE :
+				handleSiteState(localName, attributes);
+				break;
+
+			case STATE_CATEGORY :
+				handleCategoryState(localName, attributes);
+				break;
+
+			case STATE_CATEGORY_DEF :
+				handleCategoryDefState(localName, attributes);
+				break;
+
+			case STATE_DESCRIPTION_SITE :
+				handleSiteState(localName, attributes);
+				break;
+
+			case STATE_DESCRIPTION_CATEGORY_DEF :
+				handleSiteState(localName, attributes);
+				break;
+
+			default :
+				internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownStartState, (new String[] { getState(currentState) })));
+				break;
+		}
+		int newState = ((Integer) stateStack.peek()).intValue();
+		if (newState != STATE_IGNORED_ELEMENT)
+			currentState = newState;
+
+	}
+
+	/**
+	 * Handle end of element tags
+	 * @see DefaultHandler#endElement(String, String, String)
+	 * @since 2.0
+	 */
+	public void endElement(String uri, String localName, String qName) {
+
+		String text = null;
+		URLEntryModel info = null;
+
+		int state = ((Integer) stateStack.peek()).intValue();
+		switch (state) {
+			case STATE_IGNORED_ELEMENT :
+			case STATE_ARCHIVE :
+			case STATE_CATEGORY :
+				stateStack.pop();
+				break;
+
+			case STATE_INITIAL :
+				internalError(Messages.DefaultSiteParser_ParsingStackBackToInitialState);	
+				break;
+
+			case STATE_SITE :
+				stateStack.pop();
+				if (objectStack.peek() instanceof String) {
+					text = (String) objectStack.pop();
+					SiteModel site = (SiteModel) objectStack.peek();
+					site.getDescriptionModel().setAnnotation(text);					
+				}
+				//do not pop the object
+				break;
+
+			case STATE_FEATURE :
+				stateStack.pop();
+				objectStack.pop();
+				break;
+
+			case STATE_CATEGORY_DEF :
+				stateStack.pop();
+				if (objectStack.peek() instanceof String) {
+					text = (String) objectStack.pop();
+					CategoryModel category = (CategoryModel) objectStack.peek();
+					category.getDescriptionModel().setAnnotation(text);
+				}
+				objectStack.pop();
+				break;
+
+			case STATE_DESCRIPTION_SITE :
+				stateStack.pop();
+				text = ""; //$NON-NLS-1$
+				while (objectStack.peek() instanceof String) {
+					// add text, preserving at most one space between text fragments
+					String newText = (String) objectStack.pop();
+					if (trailingSpace(newText) && !leadingSpace(text)) {
+						text = " " + text; //$NON-NLS-1$
+					}
+					text = newText.trim() + text;
+					if (leadingSpace(newText) && !leadingSpace(text)) {
+						text = " " + text; //$NON-NLS-1$
+					}
+				}
+				text = text.trim();
+
+				info = (URLEntryModel) objectStack.pop();
+				if (text != null)
+					info.setAnnotation(text);
+
+				SiteModel siteModel = (SiteModel) objectStack.peek();
+				// override description.
+				// do not raise error as previous description may be default one
+				// when parsing site tag
+				if (DESCRIPTION_SITE_ALREADY_SEEN)
+					debug(NLS.bind(Messages.DefaultSiteParser_ElementAlreadySet, (new String[] { getState(state) })));
+				siteModel.setDescriptionModel(info);
+				DESCRIPTION_SITE_ALREADY_SEEN = true;
+				break;
+
+			case STATE_DESCRIPTION_CATEGORY_DEF :
+				stateStack.pop();
+				text = ""; //$NON-NLS-1$
+				while (objectStack.peek() instanceof String) {
+					// add text, preserving at most one space between text fragments
+					String newText = (String) objectStack.pop();
+					if (trailingSpace(newText) && !leadingSpace(text)) {
+						text = " " + text; //$NON-NLS-1$
+					}
+					text = newText.trim() + text;
+					if (leadingSpace(newText) && !leadingSpace(text)) {
+						text = " " + text; //$NON-NLS-1$
+					}
+				}
+				text = text.trim();
+
+				info = (URLEntryModel) objectStack.pop();
+				if (text != null)
+					info.setAnnotation(text);
+
+				CategoryModel category = (CategoryModel) objectStack.peek();
+				if (category.getDescriptionModel() != null)
+					internalError(NLS.bind(Messages.DefaultSiteParser_ElementAlreadySet, (new String[] { getState(state), category.getLabel() })));
+				else
+					category.setDescriptionModel(info);
+				break;
+
+			default :
+				internalError(NLS.bind(Messages.DefaultSiteParser_UnknownEndState, (new String[] { getState(state) })));
+				break;
+		}
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("End Element:" + uri + ":" + localName + ":" + qName);//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+
+	/**
+	 * Handle character text
+	 * @see DefaultHandler#characters(char[], int, int)
+	 * @since 2.0
+	 */
+	public void characters(char[] ch, int start, int length) {
+		String text = new String(ch, start, length);
+		//only push if description
+		int state = ((Integer) stateStack.peek()).intValue();
+		if (state == STATE_DESCRIPTION_SITE || state == STATE_DESCRIPTION_CATEGORY_DEF)
+			objectStack.push(text);
+
+	}
+
+	/**
+	 * Handle errors
+	 * @see DefaultHandler#error(SAXParseException)
+	 * @since 2.0
+	 */
+	public void error(SAXParseException ex) {
+		logStatus(ex);
+	}
+
+	/**
+	 * Handle fatal errors
+	 * @see DefaultHandler#fatalError(SAXParseException)
+	 * @exception SAXException
+	 * @since 2.0
+	 */
+	public void fatalError(SAXParseException ex) throws SAXException {
+		logStatus(ex);
+		throw ex;
+	}
+
+	private void handleInitialState(String elementName, Attributes attributes) throws SAXException {
+		if (elementName.equals(SITE)) {
+			stateStack.push(new Integer(STATE_SITE));
+			processSite(attributes);
+		} else {
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] { elementName, getState(currentState) })));
+			// what we received was not a site.xml, no need to continue
+			throw new SAXException(Messages.DefaultSiteParser_InvalidXMLStream); 
+		}
+
+	}
+
+	private void handleSiteState(String elementName, Attributes attributes) {
+		if (elementName.equals(DESCRIPTION)) {
+			stateStack.push(new Integer(STATE_DESCRIPTION_SITE));
+			processInfo(attributes);
+		} else if (elementName.equals(FEATURE)) {
+			stateStack.push(new Integer(STATE_FEATURE));
+			processFeature(attributes);
+		} else if (elementName.equals(ARCHIVE)) {
+			stateStack.push(new Integer(STATE_ARCHIVE));
+			processArchive(attributes);
+		} else if (elementName.equals(CATEGORY_DEF)) {
+			stateStack.push(new Integer(STATE_CATEGORY_DEF));
+			processCategoryDef(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] { elementName, getState(currentState) }))); 			
+	}
+
+	private void handleFeatureState(String elementName, Attributes attributes) {
+		if (elementName.equals(DESCRIPTION)) {
+			stateStack.push(new Integer(STATE_DESCRIPTION_SITE));
+			processInfo(attributes);
+		} else if (elementName.equals(FEATURE)) {
+			stateStack.push(new Integer(STATE_FEATURE));
+			processFeature(attributes);
+		} else if (elementName.equals(ARCHIVE)) {
+			stateStack.push(new Integer(STATE_ARCHIVE));
+			processArchive(attributes);
+		} else if (elementName.equals(CATEGORY_DEF)) {
+			stateStack.push(new Integer(STATE_CATEGORY_DEF));
+			processCategoryDef(attributes);
+		} else if (elementName.equals(CATEGORY)) {
+			stateStack.push(new Integer(STATE_CATEGORY));
+			processCategory(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] { elementName, getState(currentState) }))); 			
+	}
+
+	private void handleCategoryDefState(String elementName, Attributes attributes) {
+		if (elementName.equals(FEATURE)) {
+			stateStack.push(new Integer(STATE_FEATURE));
+			processFeature(attributes);
+		} else if (elementName.equals(ARCHIVE)) {
+			stateStack.push(new Integer(STATE_ARCHIVE));
+			processArchive(attributes);
+		} else if (elementName.equals(CATEGORY_DEF)) {
+			stateStack.push(new Integer(STATE_CATEGORY_DEF));
+			processCategoryDef(attributes);
+		} else if (elementName.equals(DESCRIPTION)) {
+			stateStack.push(new Integer(STATE_DESCRIPTION_CATEGORY_DEF));
+			processInfo(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] { elementName, getState(currentState) }))); 			
+	}
+
+	private void handleCategoryState(String elementName, Attributes attributes) {
+		if (elementName.equals(DESCRIPTION)) {
+			stateStack.push(new Integer(STATE_DESCRIPTION_SITE));
+			processInfo(attributes);
+		} else if (elementName.equals(FEATURE)) {
+			stateStack.push(new Integer(STATE_FEATURE));
+			processFeature(attributes);
+		} else if (elementName.equals(ARCHIVE)) {
+			stateStack.push(new Integer(STATE_ARCHIVE));
+			processArchive(attributes);
+		} else if (elementName.equals(CATEGORY_DEF)) {
+			stateStack.push(new Integer(STATE_CATEGORY_DEF));
+			processCategoryDef(attributes);
+		} else if (elementName.equals(CATEGORY)) {
+			stateStack.push(new Integer(STATE_CATEGORY));
+			processCategory(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] { elementName, getState(currentState) }))); 			
+	}
+
+	/* 
+	 * process site info
+	 */
+	private void processSite(Attributes attributes) throws SAXException {
+		// create site map
+		SiteModel site = factory.createSiteMapModel();
+
+		// if URL is specified, it replaces the URL of the site
+		// used to calculate the location of features and archives
+		String siteURL = attributes.getValue("url"); //$NON-NLS-1$
+		if (siteURL != null && !("".equals(siteURL.trim()))) { //$NON-NLS-1$
+			if (!siteURL.endsWith("/") && !siteURL.endsWith(File.separator)) { //$NON-NLS-1$
+				siteURL += "/"; //$NON-NLS-1$
+			}
+			site.setLocationURLString(siteURL);
+		}
+
+		// provide default description URL
+		// If <description> is specified, for the site,  it takes precedence		
+		URLEntryModel description = factory.createURLEntryModel();
+		description.setURLString(DEFAULT_INFO_URL);
+		site.setDescriptionModel(description);
+
+		// verify we can parse the site ...if the site has
+		// a different type throw an exception to force reparsing
+		// with the matching parser
+		String type = attributes.getValue("type"); //$NON-NLS-1$
+		if (!factory.canParseSiteType(type)) {
+			throw new SAXException(new InvalidSiteTypeException(type));
+		}
+		site.setType(type);
+		
+		// get mirrors, if any
+		String mirrorsURL = attributes.getValue("mirrorsURL"); //$NON-NLS-1$
+		if (mirrorsURL != null && mirrorsURL.trim().length() > 0) {
+			URLEntryModel[] mirrors = getMirrors(mirrorsURL, factory);
+			if (mirrors != null)
+				site.setMirrorSiteEntryModels(mirrors);
+			else 
+				site.setMirrorsURLString(mirrorsURL);
+		}
+		
+		String pack200 = attributes.getValue("pack200"); //$NON-NLS-1$
+		if(site instanceof ExtendedSite && pack200 != null && new Boolean(pack200).booleanValue()){
+			((ExtendedSite) site).setSupportsPack200(true);
+		}
+		
+		if ( (site instanceof ExtendedSite) && (attributes.getValue("digestURL") != null)) { //$NON-NLS-1$
+			ExtendedSite extendedSite = (ExtendedSite) site;
+			extendedSite.setDigestExist(true);
+			extendedSite.setDigestURL(attributes.getValue("digestURL")); //$NON-NLS-1$
+			
+			if ( (attributes.getValue("availableLocales") != null) && (!attributes.getValue("availableLocales").trim().equals(""))) {  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+				StringTokenizer locals = new StringTokenizer(attributes.getValue("availableLocales"), ",");  //$NON-NLS-1$//$NON-NLS-2$
+				String[] availableLocals = new String[locals.countTokens()];
+				int i = 0;
+				while(locals.hasMoreTokens()) {
+					availableLocals[i++] = locals.nextToken();
+				}
+				extendedSite.setAvailableLocals(availableLocals);
+			}
+		}
+		
+		if ( (site instanceof ExtendedSite) && (attributes.getValue("associateSitesURL") != null)) { //$NON-NLS-1$
+			IURLEntry[] associateSites = getAssociateSites(attributes.getValue("associateSitesURL"), factory); //$NON-NLS-1$
+			if (associateSites != null)
+				((ExtendedSite)site).setAssociateSites(associateSites);
+			else 
+				site.setMirrorsURLString(mirrorsURL);
+		}
+		
+		objectStack.push(site);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("End process Site tag: siteURL:" + siteURL + " type:" + type);//$NON-NLS-1$ //$NON-NLS-2$
+
+	}
+
+	/* 
+	 * process feature info
+	 */
+	private void processFeature(Attributes attributes) {
+		SiteFeatureReferenceModel feature = factory.createFeatureReferenceModel();
+		
+        // feature location on the site
+        String urlInfo = attributes.getValue("url"); //$NON-NLS-1$
+        // identifier and version
+        String id = attributes.getValue("id"); //$NON-NLS-1$
+        String ver = attributes.getValue("version"); //$NON-NLS-1$
+        
+        boolean noURL = (urlInfo == null || urlInfo.trim().equals("")); //$NON-NLS-1$
+        boolean noId = (id == null || id.trim().equals("")); //$NON-NLS-1$
+        boolean noVersion = (ver == null || ver.trim().equals("")); //$NON-NLS-1$
+        
+        // We need to have id and version, or the url, or both.
+ 		if (noURL) {
+            if (noId || noVersion)
+                internalError(NLS.bind(Messages.DefaultSiteParser_Missing, (new String[] { "url", getState(currentState) })));	//$NON-NLS-1$
+            else // default url
+                urlInfo = FEATURES + id + '_' + ver; // 
+        }
+        
+		feature.setURLString(urlInfo);
+
+		String type = attributes.getValue("type"); //$NON-NLS-1$
+		feature.setType(type);
+
+		// if one is null, and not the other
+		if (noId ^ noVersion) {
+			String[] values = new String[] { id, ver, getState(currentState)};
+			UpdateCore.warn(NLS.bind(Messages.DefaultFeatureParser_IdOrVersionInvalid, values));
+		} else {
+			feature.setFeatureIdentifier(id);
+			feature.setFeatureVersion(ver);
+		}
+
+		// get label if it exists
+		String label = attributes.getValue("label"); //$NON-NLS-1$
+		if (label != null) {
+			if ("".equals(label.trim())) //$NON-NLS-1$
+				label = null;
+		}
+		feature.setLabel(label);
+
+		// OS
+		String os = attributes.getValue("os"); //$NON-NLS-1$
+		feature.setOS(os);
+
+		// WS
+		String ws = attributes.getValue("ws"); //$NON-NLS-1$
+		feature.setWS(ws);
+
+		// NL
+		String nl = attributes.getValue("nl"); //$NON-NLS-1$
+		feature.setNL(nl);
+
+		// arch
+		String arch = attributes.getValue("arch"); //$NON-NLS-1$
+		feature.setArch(arch);
+
+		//patch
+		String patch = attributes.getValue("patch"); //$NON-NLS-1$
+		feature.setPatch(patch);
+
+		SiteModel site = (SiteModel) objectStack.peek();
+		site.addFeatureReferenceModel(feature);
+		feature.setSiteModel(site);
+
+		objectStack.push(feature);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("End Processing DefaultFeature Tag: url:" + urlInfo + " type:" + type); //$NON-NLS-1$ //$NON-NLS-2$
+
+	}
+
+	/* 
+	 * process archive info
+	 */
+	private void processArchive(Attributes attributes) {
+		ArchiveReferenceModel archive = factory.createArchiveReferenceModel();
+		String id = attributes.getValue("path"); //$NON-NLS-1$
+		if (id == null || id.trim().equals("")) { //$NON-NLS-1$
+			internalError(NLS.bind(Messages.DefaultSiteParser_Missing, (new String[] { "path", getState(currentState) }))); //$NON-NLS-1$
+		}
+
+		archive.setPath(id);
+
+		String url = attributes.getValue("url"); //$NON-NLS-1$
+		if (url == null || url.trim().equals("")) { //$NON-NLS-1$
+			internalError(NLS.bind(Messages.DefaultSiteParser_Missing, (new String[] { "archive", getState(currentState) })));	//$NON-NLS-1$
+		} else {
+			archive.setURLString(url);
+
+			SiteModel site = (SiteModel) objectStack.peek();
+			site.addArchiveReferenceModel(archive);
+		}
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("End processing Archive: path:" + id + " url:" + url);//$NON-NLS-1$ //$NON-NLS-2$
+
+	}
+
+	/* 
+	 * process the Category  info
+	 */
+	private void processCategory(Attributes attributes) {
+		String category = attributes.getValue("name"); //$NON-NLS-1$
+		SiteFeatureReferenceModel feature = (SiteFeatureReferenceModel) objectStack.peek();
+		feature.addCategoryName(category);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("End processing Category: name:" + category); //$NON-NLS-1$
+	}
+
+	/* 
+	 * process category def info
+	 */
+	private void processCategoryDef(Attributes attributes) {
+		CategoryModel category = factory.createSiteCategoryModel();
+		String name = attributes.getValue("name"); //$NON-NLS-1$
+		String label = attributes.getValue("label"); //$NON-NLS-1$
+		category.setName(name);
+		category.setLabel(label);
+
+		SiteModel site = (SiteModel) objectStack.peek();
+		site.addCategoryModel(category);
+		objectStack.push(category);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("End processing CategoryDef: name:" + name + " label:" + label); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/* 
+	 * process URL info with element text
+	 */
+	private void processInfo(Attributes attributes) {
+		URLEntryModel inf = factory.createURLEntryModel();
+		String infoURL = attributes.getValue("url"); //$NON-NLS-1$
+		inf.setURLString(infoURL);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("Processed Info: url:" + infoURL); //$NON-NLS-1$
+
+		objectStack.push(inf);
+	}
+
+	/*
+	 * 
+	 */
+	private static void debug(String s) {
+		UpdateCore.debug("DefaultSiteParser" + s); //$NON-NLS-1$
+	}
+
+	/*
+	 * 
+	 */
+	private void logStatus(SAXParseException ex) {
+		String name = ex.getSystemId();
+		if (name == null)
+			name = ""; //$NON-NLS-1$
+		else
+			name = name.substring(1 + name.lastIndexOf("/")); //$NON-NLS-1$
+
+		String msg;
+		if (name.equals("")) //$NON-NLS-1$
+			msg = NLS.bind(Messages.DefaultSiteParser_ErrorParsing, (new String[] { ex.getMessage() }));
+		else {
+			String[] values = new String[] { name, Integer.toString(ex.getLineNumber()), Integer.toString(ex.getColumnNumber()), ex.getMessage()};
+			msg = NLS.bind(Messages.DefaultSiteParser_ErrorlineColumnMessage, values);
+		}
+		error(new Status(IStatus.ERROR, PLUGIN_ID, Platform.PARSE_PROBLEM, msg, ex));
+	}
+
+	/*
+	 * Handles an error state specified by the status.  The collection of all logged status
+	 * objects can be accessed using <code>getStatus()</code>.
+	 *
+	 * @param error a status detailing the error condition
+	 */
+	private void error(IStatus error) {
+
+		if (status == null) {
+			status = new MultiStatus(PLUGIN_ID, Platform.PARSE_PROBLEM, Messages.DefaultSiteParser_ErrorParsingSite, null);
+		}
+
+		status.add(error);
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			UpdateCore.log(error);
+	}
+
+	/*
+	 * 
+	 */
+	private void internalErrorUnknownTag(String msg) {
+		stateStack.push(new Integer(STATE_IGNORED_ELEMENT));
+		internalError(msg);
+	}
+
+	/*
+	 * 
+	 */
+	private void internalError(String message) {
+		error(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, message, null));
+	}
+
+	/*
+	 * return the state as String
+	 */
+	private String getState(int state) {
+
+		switch (state) {
+			case STATE_IGNORED_ELEMENT :
+				return "Ignored"; //$NON-NLS-1$
+
+			case STATE_INITIAL :
+				return "Initial"; //$NON-NLS-1$
+
+			case STATE_SITE :
+				return "Site"; //$NON-NLS-1$
+
+			case STATE_FEATURE :
+				return "Feature"; //$NON-NLS-1$
+
+			case STATE_ARCHIVE :
+				return "Archive"; //$NON-NLS-1$
+
+			case STATE_CATEGORY :
+				return "Category"; //$NON-NLS-1$
+
+			case STATE_CATEGORY_DEF :
+				return "Category Def"; //$NON-NLS-1$
+
+			case STATE_DESCRIPTION_CATEGORY_DEF :
+				return "Description / Category Def"; //$NON-NLS-1$
+
+			case STATE_DESCRIPTION_SITE :
+				return "Description / Site"; //$NON-NLS-1$
+
+			default :
+				return Messages.DefaultSiteParser_UnknownState; 
+		}
+	}
+	private boolean leadingSpace(String str) {
+		if (str.length() <= 0) {
+			return false;
+		}
+		return Character.isWhitespace(str.charAt(0));
+	}
+	private boolean trailingSpace(String str) {
+		if (str.length() <= 0) {
+			return false;
+		}
+		return Character.isWhitespace(str.charAt(str.length() - 1));
+	}
+	
+	static URLEntryModel[] getMirrors(String mirrorsURL, SiteModelFactory factory) {
+	    
+		try {
+			String countryCode = Locale.getDefault().getCountry().toLowerCase();
+			int timeZone = (new GregorianCalendar()).get(Calendar.ZONE_OFFSET)/(60*60*1000);
+
+			if (mirrorsURL.indexOf("?") != -1) { //$NON-NLS-1$
+				mirrorsURL = mirrorsURL + "&"; //$NON-NLS-1$
+			} else {
+				mirrorsURL = mirrorsURL + "?"; //$NON-NLS-1$
+			}			
+			mirrorsURL = mirrorsURL + "countryCode=" + countryCode + "&timeZone=" + timeZone + "&responseType=xml"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+		    DocumentBuilderFactory domFactory = 
+		    DocumentBuilderFactory.newInstance();
+		    DocumentBuilder builder = domFactory.newDocumentBuilder();
+		    Document document = builder.parse(mirrorsURL);
+		    if (document == null)
+		    	return null;
+		    NodeList mirrorNodes = document.getElementsByTagName(MIRROR); 
+		    URLEntryModel[] mirrors = new URLEntryModel[mirrorNodes.getLength()];
+		    for (int i=0; i<mirrorNodes.getLength(); i++) {
+		    	Element mirrorNode = (Element)mirrorNodes.item(i);
+				mirrors[i] = factory.createURLEntryModel();
+				String infoURL = mirrorNode.getAttribute("url"); //$NON-NLS-1$
+				String label = mirrorNode.getAttribute("label"); //$NON-NLS-1$
+				mirrors[i].setURLString(infoURL);
+				mirrors[i].setAnnotation(label);
+
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+					debug("Processed mirror: url:" + infoURL + " label:" + label); //$NON-NLS-1$ //$NON-NLS-2$
+		    }
+		    return mirrors;
+		}
+		catch (Exception e) {
+		    // log if absolute url
+		    if (mirrorsURL != null &&
+		    		(mirrorsURL.startsWith("http://") //$NON-NLS-1$
+					|| mirrorsURL.startsWith("https://") //$NON-NLS-1$
+					|| mirrorsURL.startsWith("file://") //$NON-NLS-1$
+					|| mirrorsURL.startsWith("ftp://") //$NON-NLS-1$
+					|| mirrorsURL.startsWith("jar://"))) //$NON-NLS-1$
+		    	UpdateCore.log(Messages.DefaultSiteParser_mirrors, e);
+			return null;
+		}
+	}
+	
+	private static IURLEntry[] getAssociateSites(String associateSitesURL, SiteModelFactory factory) {
+	    
+		try {
+		    DocumentBuilderFactory domFactory = 
+		    DocumentBuilderFactory.newInstance();
+		    DocumentBuilder builder = domFactory.newDocumentBuilder();
+		    Document document = builder.parse(associateSitesURL);
+		    if (document == null)
+		    	return null;
+		    NodeList mirrorNodes = document.getElementsByTagName(ASSOCIATE_SITE); 
+		    URLEntry[] mirrors = new URLEntry[mirrorNodes.getLength()];
+		    for (int i=0; i<mirrorNodes.getLength(); i++) {
+		    	Element mirrorNode = (Element)mirrorNodes.item(i);
+				mirrors[i] = new URLEntry();
+				String infoURL = mirrorNode.getAttribute("url"); //$NON-NLS-1$
+				String label = mirrorNode.getAttribute("label"); //$NON-NLS-1$
+				mirrors[i].setURLString(infoURL);
+				mirrors[i].setAnnotation(label);
+
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+					debug("Processed mirror: url:" + infoURL + " label:" + label); //$NON-NLS-1$ //$NON-NLS-2$
+		    }
+		    return mirrors;
+		}
+		catch (Exception e) {
+		    // log if absolute url
+		    if (associateSitesURL != null &&
+		    		(associateSitesURL.startsWith("http://") //$NON-NLS-1$
+					|| associateSitesURL.startsWith("https://") //$NON-NLS-1$
+					|| associateSitesURL.startsWith("file://") //$NON-NLS-1$
+					|| associateSitesURL.startsWith("ftp://") //$NON-NLS-1$
+					|| associateSitesURL.startsWith("jar://"))) //$NON-NLS-1$
+		    	UpdateCore.log(Messages.DefaultSiteParser_mirrors, e);
+			return null;
+		}
+	}
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureModel.java
new file mode 100644
index 0000000..6535672
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureModel.java
@@ -0,0 +1,946 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ *     James D Miles (IBM Corp.) - bug 191783, NullPointerException in FeatureDownloader
+ *******************************************************************************/
+
+package org.eclipse.update.core.model;
+
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.update.core.IIncludedFeatureReference;
+import org.eclipse.update.core.IncludedFeatureReference;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * Feature model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.Feature
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class FeatureModel extends ModelObject {
+
+	private String featureId;
+	private String featureVersion;
+	private String label;
+	private String localizedLabel;
+	private String provider;
+	private String localizedProvider;
+	private String imageURLString;
+	private URL imageURL;
+	private String os;
+	private String ws;
+	private String nl;
+	private String arch;
+	private boolean primary = false;
+	private boolean exclusive=false;
+	private String primaryPluginID;
+	private String application;
+	private String affinity;
+	private InstallHandlerEntryModel installHandler;
+	private URLEntryModel description;
+	private URLEntryModel copyright;
+	private URLEntryModel license;
+	private URLEntryModel updateSiteInfo;
+	private List /*of InfoModel*/	discoverySiteInfo;
+	private List /*of ImportModel*/	imports;
+	private List /*of PluginEntryModel*/	pluginEntries;
+	private List /*of IncludedFeatureReferenceModel */	featureIncludes;
+	private List /*of NonPluginEntryModel*/	nonPluginEntries;
+
+	// performance
+	private URL bundleURL;
+	private URL base;
+	private boolean resolved = false;
+
+	/**
+	 * Creates an uninitialized feature object.
+	 * 
+	 * @since 2.0
+	 */
+	public FeatureModel() {
+		super();
+	}
+
+	/**
+	 * Compares 2 feature models for equality
+	 *  
+	 * @param obj feature model to compare with
+	 * @return <code>true</code> if the two models are equal, 
+	 * <code>false</code> otherwise
+	 * @since 2.0
+	 */
+	public boolean equals(Object obj) {
+		if (!(obj instanceof FeatureModel))
+			return false;
+		FeatureModel model = (FeatureModel) obj;
+
+		return (featureId.toLowerCase().equals(model.getFeatureIdentifier()) && featureVersion.toLowerCase().equals(model.getFeatureVersion()));
+	}
+
+	/**
+	 * Returns the feature identifier as a string
+	 * 
+	 * @see org.eclipse.update.core.IFeature#getVersionedIdentifier()
+	 * @return feature identifier
+	 * @since 2.0
+	 */
+	public String getFeatureIdentifier() {
+		//delayedResolve(); no delay
+		return featureId;
+	}
+
+	/**
+	 * Returns the feature version as a string
+	 * 
+	 * @see org.eclipse.update.core.IFeature#getVersionedIdentifier()
+	 * @return feature version 
+	 * @since 2.0
+	 */
+	public String getFeatureVersion() {
+		//delayedResolve(); no delay
+		return featureVersion;
+	}
+
+	/**
+	 * Retrieve the displayable label for the feature. If the model
+	 * object has been resolved, the label is localized.
+	 * 
+	 * @return displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getLabel() {
+		delayedResolve();
+		if (localizedLabel != null)
+			return localizedLabel;
+		else
+			return label;
+	}
+
+	/**
+	 * Retrieve the non-localized displayable label for the feature.
+	 * 
+	 * @return non-localized displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getLabelNonLocalized() {
+		return label;
+	}
+
+	/**
+	 * Retrieve the displayable label for the feature provider. If the model
+	 * object has been resolved, the label is localized.
+	 * 
+	 * @return displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getProvider() {
+		delayedResolve();
+		if (localizedProvider != null)
+			return localizedProvider;
+		else
+			return provider;
+	}
+
+	/**
+	 * Retrieve the non-localized displayable label for the feature provider.
+	 * 
+	 * @return non-localized displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getProviderNonLocalized() {
+		return provider;
+	}
+
+	/**
+	 * Returns the unresolved URL string for the feature image.
+	 *
+	 * @return url string, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getImageURLString() {
+		delayedResolve();
+		return imageURLString;
+	}
+
+	/**
+	 * Returns the resolved URL for the image.
+	 * 
+	 * @return url, or <code>null</code>
+	 * @since 2.0
+	 */
+	public URL getImageURL() {
+		delayedResolve();
+		return imageURL;
+	}
+
+	/**
+	 * Get optional operating system specification as a comma-separated string.
+	 * 
+	 * @return the operating system specification string, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getOS() {
+		return os;
+	}
+
+	/**
+	 * Get optional windowing system specification as a comma-separated string.
+	 * @return the windowing system specification string, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getWS() {
+		return ws;
+	}
+
+	/**
+	 * Get optional system architecture specification as a comma-separated string.
+	 * 
+	 * @return the system architecture specification string, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getOSArch() {
+		return arch;
+	}
+
+	/**
+	 * Get optional locale specification as a comma-separated string.
+	 * 
+	 * @return the locale specification string, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getNL() {
+		return nl;
+	}
+
+	/**
+	 * Indicates whether the feature can be used as a primary feature.
+	 * 
+	 * @return <code>true</code> if this is a primary feature, 
+	 * otherwise <code>false</code>
+	 * @since 2.0
+	 */
+	public boolean isPrimary() {
+		return primary;
+	}
+	
+	/**
+	 * Indicates whether the feature must be processed alone
+	 * during installation and configuration. Features that
+	 * are not exclusive can be installed in a batch.
+	 * 
+	 * @return <code>true</code> if feature requires
+	 * exclusive processing, <code>false</code> otherwise.
+	 * @since 2.1
+	 */
+	public boolean isExclusive() {
+		return exclusive;
+	}
+
+	/**
+	 * Returns an optional identifier for the feature application
+	 * 
+	 * @return application identifier, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getApplication() {
+		return application;
+	}
+
+	/**
+	 * Returns an optional identifier for the colocation affinity feature
+	 * 
+	 * @return feature identifier, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getAffinityFeature() {
+		return affinity;
+	}
+
+	/**
+	 * Returns and optional custom install handler entry.
+	 * 
+	 * @return install handler entry, or <code>null</code> if
+	 * none was specified
+	 * @since 2.0
+	 */
+	public InstallHandlerEntryModel getInstallHandlerModel() {
+		//delayedResolve(); no delay
+		return installHandler;
+	}
+
+	/**
+	 * Returns the feature description.
+	 * 
+	 * @return feature rescription, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public URLEntryModel getDescriptionModel() {
+		//delayedResolve(); no delay
+		return description;
+	}
+
+	/**
+	 * Returns the copyright information for the feature.
+	 * 
+	 * @return copyright information, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public URLEntryModel getCopyrightModel() {
+		//delayedResolve(); no delay
+		return copyright;
+	}
+
+	/**
+	 * Returns the license information for the feature.
+	 * 
+	 * @return feature license, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public URLEntryModel getLicenseModel() {
+		//delayedResolve(); no delay;
+		return license;
+	}
+
+	/**
+	 * Returns an information entry referencing the location of the
+	 * feature update site.
+	 * 
+	 * @return update site entry, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public URLEntryModel getUpdateSiteEntryModel() {
+		//delayedResolve(); no delay;
+		return updateSiteInfo;
+	}
+
+	/**
+	 * Return an array of information entries referencing locations of other
+	 * update sites. 
+	 * 
+	 * @return an array of site entries, or an empty array.
+	 * @since 2.0 
+	 * @since 2.0
+	 */
+	public URLEntryModel[] getDiscoverySiteEntryModels() {
+		//delayedResolve(); no delay;
+		if (discoverySiteInfo == null || discoverySiteInfo.size() == 0)
+			return new URLEntryModel[0];
+
+		return (URLEntryModel[]) discoverySiteInfo.toArray(arrayTypeFor(discoverySiteInfo));
+	}
+
+	/**
+	 * Return a list of plug-in dependencies for this feature.
+	 * 
+	 * @return the list of required plug-in dependencies, or an empty array.
+	 * @since 2.0
+	 */
+	public ImportModel[] getImportModels() {
+		//delayedResolve(); no delay;
+		if (imports == null || imports.size() == 0)
+			return new ImportModel[0];
+
+		return (ImportModel[]) imports.toArray(arrayTypeFor(imports));
+	}
+
+	/**
+	 * Returns an array of plug-in entries referenced by this feature
+	 * 
+	 * @return an erray of plug-in entries, or an empty array.
+	 * @since 2.0
+	 */
+	public PluginEntryModel[] getPluginEntryModels() {
+		if (pluginEntries == null || pluginEntries.size() == 0)
+			return new PluginEntryModel[0];
+
+		return (PluginEntryModel[]) pluginEntries.toArray(arrayTypeFor(pluginEntries));
+	}
+
+	/**
+	 * Returns an array of versioned identifier referenced by this feature
+	 * 
+	 * @return an array of versioned identifier, or an empty array.
+	 * @deprecated use getFeatureIncludeIdentifier instead.
+	 * @since 2.0
+	 */
+	public VersionedIdentifier[] getFeatureIncludeVersionedIdentifier() {
+		//delayedResolve(); no delay
+		if (featureIncludes == null)
+			return new VersionedIdentifier[0];
+
+		//
+		Iterator iter = featureIncludes.iterator();
+		VersionedIdentifier[] versionIncluded = new VersionedIdentifier[featureIncludes.size()];
+		int index = 0;
+		while (iter.hasNext()) {
+			IncludedFeatureReferenceModel model = (IncludedFeatureReferenceModel) iter.next();
+			versionIncluded[index] = model.getVersionedIdentifier();
+			index++;
+		}
+		return versionIncluded;
+	}
+
+	/**
+	 * Returns an array of included feature reference model referenced by this feature.
+	 *
+	 * @return an array of included feature reference model, or an empty array.
+	 * @since 2.0
+	 */
+	public IIncludedFeatureReference[] getFeatureIncluded() {
+		//delayedResolve(); no delay
+		if (featureIncludes == null || featureIncludes.size() == 0)
+			return new IIncludedFeatureReference[0];
+		return (IIncludedFeatureReference[]) featureIncludes.toArray(arrayTypeFor(featureIncludes));
+	}
+
+	/**
+	 * Returns an array of non-plug-in entries referenced by this feature
+	 * 
+	 * @return an erray of non-plug-in entries, or an empty array.
+	 * @since 2.0
+	 */
+	public NonPluginEntryModel[] getNonPluginEntryModels() {
+		if (nonPluginEntries == null || nonPluginEntries.size() == 0)
+			return new NonPluginEntryModel[0];
+
+		return (NonPluginEntryModel[]) nonPluginEntries.toArray(arrayTypeFor(nonPluginEntries));
+	}
+
+	/**
+	 * Sets the feature identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param featureId feature identifier
+	 * @since 2.0
+	 */
+	public void setFeatureIdentifier(String featureId) {
+		assertIsWriteable();
+		this.featureId = featureId;
+	}
+
+	/**
+	 * Sets the feature version.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param featureVersion feature version
+	 * @since 2.0
+	 */
+	public void setFeatureVersion(String featureVersion) {
+		assertIsWriteable();
+		this.featureVersion = featureVersion;
+	}
+
+	/**
+	 * Sets the feature displayable label.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param label displayable label
+	 * @since 2.0
+	 */
+	public void setLabel(String label) {
+		assertIsWriteable();
+		this.label = label;
+		this.localizedLabel = null;
+	}
+
+	/**
+	 * Sets the feature provider displayable label.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param provider provider displayable label
+	 * @since 2.0
+	 */
+	public void setProvider(String provider) {
+		assertIsWriteable();
+		this.provider = provider;
+		this.localizedProvider = null;
+	}
+
+	/**
+	 * Sets the unresolved URL for the feature image.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param imageURLString unresolved URL string
+	 * @since 2.0
+	 */
+	public void setImageURLString(String imageURLString) {
+		assertIsWriteable();
+		this.imageURLString = imageURLString;
+		this.imageURL = null;
+	}
+
+	/**
+	 * Sets the operating system specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param os operating system specification as a comma-separated list
+	 * @since 2.0
+	 */
+	public void setOS(String os) {
+		assertIsWriteable();
+		this.os = os;
+	}
+
+	/**
+	 * Sets the windowing system specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param ws windowing system specification as a comma-separated list
+	 * @since 2.0
+	 */
+	public void setWS(String ws) {
+		assertIsWriteable();
+		this.ws = ws;
+	}
+
+	/**
+	 * Sets the locale specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param nl locale specification as a comma-separated list
+	 * @since 2.0
+	 */
+	public void setNL(String nl) {
+		assertIsWriteable();
+		this.nl = nl;
+	}
+
+	/**
+	 * Sets the system architecture specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param arch system architecture specification as a comma-separated list
+	 * @since 2.0
+	 */
+	public void setArch(String arch) {
+		assertIsWriteable();
+		this.arch = arch;
+	}
+
+	/**
+	 * Indicates whether this feature can act as a primary feature.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param primary <code>true</code> if this feature can act as primary,
+	 * <code>false</code> otherwise
+	 * 
+	 * @since 2.0
+	 */
+	public void setPrimary(boolean primary) {
+		assertIsWriteable();
+		this.primary = primary;
+	}
+	
+	/**
+	 * Indicates whether this feature can act as a primary feature.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param exclusive <code>true</code> if this feature must be
+	 * processed independently from other features, <code>false</code> 
+	 * if feature can be processed in a batch with other features.
+	 * 
+	 * @since 2.1
+	 */
+	public void setExclusive(boolean exclusive) {
+		assertIsWriteable();
+		this.exclusive = exclusive;
+	}
+
+	/**
+	 * Sets the feature application identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param application feature application identifier
+	 * @since 2.0
+	 */
+	public void setApplication(String application) {
+		assertIsWriteable();
+		this.application = application;
+	}
+
+	/**
+	 * Sets the identifier of the Feature this feature should be
+	 * installed with.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param affinity the identifier of the Feature
+	 * @since 2.0
+	 */
+	public void setAffinityFeature(String affinity) {
+		assertIsWriteable();
+		this.affinity = affinity;
+	}
+
+	/**
+	 * Sets the custom install handler for the feature.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param installHandler install handler entry
+	 * @since 2.0
+	 */
+	public void setInstallHandlerModel(InstallHandlerEntryModel installHandler) {
+		assertIsWriteable();
+		this.installHandler = installHandler;
+	}
+
+	/**
+	 * Sets the feature description information.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param description feature description information
+	 * @since 2.0
+	 */
+	public void setDescriptionModel(URLEntryModel description) {
+		assertIsWriteable();
+		this.description = description;
+	}
+
+	/**
+	 * Sets the feature copyright information.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param copyright feature copyright information
+	 * @since 2.0
+	 */
+	public void setCopyrightModel(URLEntryModel copyright) {
+		assertIsWriteable();
+		this.copyright = copyright;
+	}
+
+	/**
+	 * Sets the feature license information.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param license feature license information
+	 * @since 2.0
+	 */
+	public void setLicenseModel(URLEntryModel license) {
+		assertIsWriteable();
+		this.license = license;
+	}
+
+	/**
+	 * Sets the feature update site reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param updateSiteInfo feature update site reference
+	 * @since 2.0
+	 */
+	public void setUpdateSiteEntryModel(URLEntryModel updateSiteInfo) {
+		assertIsWriteable();
+		this.updateSiteInfo = updateSiteInfo;
+	}
+
+	/**
+	 * Sets additional update site references.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param discoverySiteInfo additional update site references
+	 * @since 2.0
+	 */
+	public void setDiscoverySiteEntryModels(URLEntryModel[] discoverySiteInfo) {
+		assertIsWriteable();
+		if (discoverySiteInfo == null)
+			this.discoverySiteInfo = null;
+		else
+			this.discoverySiteInfo = new ArrayList(Arrays.asList(discoverySiteInfo));
+	}
+
+	/**
+	 * Sets the feature plug-in dependency information.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param imports feature plug-in dependency information
+	 * @since 2.0
+	 */
+	public void setImportModels(ImportModel[] imports) {
+		assertIsWriteable();
+		if (imports == null)
+			this.imports = null;
+		else
+			this.imports = new ArrayList(Arrays.asList(imports));
+	}
+
+	/**
+	 * Sets the feature plug-in references.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param pluginEntries feature plug-in references
+	 * @since 2.0
+	 */
+	public void setPluginEntryModels(PluginEntryModel[] pluginEntries) {
+		assertIsWriteable();
+		if (pluginEntries == null)
+			this.pluginEntries = null;
+		else
+			this.pluginEntries = new ArrayList(Arrays.asList(pluginEntries));
+	}
+
+	/**
+	 * Sets the feature non-plug-in data references.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param nonPluginEntries feature non-plug-in data references
+	 * @since 2.0
+	 */
+	public void setNonPluginEntryModels(NonPluginEntryModel[] nonPluginEntries) {
+		assertIsWriteable();
+		if (nonPluginEntries == null)
+			this.nonPluginEntries = null;
+		else
+			this.nonPluginEntries = new ArrayList(Arrays.asList(nonPluginEntries));
+	}
+
+	/**
+	 * Adds an additional update site reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param discoverySiteInfo update site reference
+	 * @since 2.0
+	 */
+	public void addDiscoverySiteEntryModel(URLEntryModel discoverySiteInfo) {
+		assertIsWriteable();
+		if (this.discoverySiteInfo == null)
+			this.discoverySiteInfo = new ArrayList();
+		if (!this.discoverySiteInfo.contains(discoverySiteInfo))
+			this.discoverySiteInfo.add(discoverySiteInfo);
+	}
+
+	/**
+	 * Adds a plug-in dependency entry.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param importEntry plug-in dependency entry
+	 * @since 2.0
+	 */
+	public void addImportModel(ImportModel importEntry) {
+		assertIsWriteable();
+		if (this.imports == null)
+			this.imports = new ArrayList();
+		if (!this.imports.contains(importEntry))
+			this.imports.add(importEntry);
+	}
+
+	/**
+	 * Adds a plug-in reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param pluginEntry plug-in reference
+	 * @since 2.0
+	 */
+	public void addPluginEntryModel(PluginEntryModel pluginEntry) {
+		assertIsWriteable();
+		if (this.pluginEntries == null)
+			this.pluginEntries = new ArrayList();
+		//PERF: no ListContains()
+		//if (!this.pluginEntries.contains(pluginEntry))
+		this.pluginEntries.add(pluginEntry);
+	}
+
+	/**
+	 * Adds a feature identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * @param include the included feature
+	 * @since 2.1
+	 */
+	public void addIncludedFeatureReferenceModel(IncludedFeatureReferenceModel include) {
+		assertIsWriteable();
+		if (this.featureIncludes == null)
+			this.featureIncludes = new ArrayList();
+		//PERF: no ListContains()
+		//if (!this.featureIncludes.contains(include))
+		this.featureIncludes.add(new IncludedFeatureReference(include));
+	}
+
+	/**
+	 * Adds a non-plug-in data reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param nonPluginEntry non-plug-in data reference
+	 * @since 2.0
+	 */
+	public void addNonPluginEntryModel(NonPluginEntryModel nonPluginEntry) {
+		assertIsWriteable();
+		if (this.nonPluginEntries == null)
+			this.nonPluginEntries = new ArrayList();
+		//PERF: no ListContains()
+		//if (!this.nonPluginEntries.contains(nonPluginEntry))
+		this.nonPluginEntries.add(nonPluginEntry);
+	}
+
+	/**
+	 * Removes an update site reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param discoverySiteInfo update site reference
+	 * @since 2.0
+	 */
+	public void removeDiscoverySiteEntryModel(URLEntryModel discoverySiteInfo) {
+		assertIsWriteable();
+		if (this.discoverySiteInfo != null)
+			this.discoverySiteInfo.remove(discoverySiteInfo);
+	}
+
+	/**
+	 * Removes a plug-in dependency entry.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param importEntry plug-in dependency entry
+	 * @since 2.0
+	 */
+	public void removeImportModel(ImportModel importEntry) {
+		assertIsWriteable();
+		if (this.imports != null)
+			this.imports.remove(importEntry);
+	}
+
+	/**
+	 * Removes a plug-in reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param pluginEntry plug-in reference
+	 * @since 2.0
+	 */
+	public void removePluginEntryModel(PluginEntryModel pluginEntry) {
+		assertIsWriteable();
+		if (this.pluginEntries != null)
+			this.pluginEntries.remove(pluginEntry);
+	}
+
+	/**
+	 * Removes a non-plug-in data reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param nonPluginEntry non-plug-in data reference
+	 * @since 2.0
+	 */
+	public void removeNonPluginEntryModel(NonPluginEntryModel nonPluginEntry) {
+		assertIsWriteable();
+		if (this.nonPluginEntries != null)
+			this.nonPluginEntries.remove(nonPluginEntry);
+	}
+
+	/**
+	 * Marks the model object as read-only.
+	 * 
+	 * @since 2.0
+	 */
+	public void markReadOnly() {
+		super.markReadOnly();
+		markReferenceReadOnly(getDescriptionModel());
+		markReferenceReadOnly(getCopyrightModel());
+		markReferenceReadOnly(getLicenseModel());
+		markReferenceReadOnly(getUpdateSiteEntryModel());
+		markListReferenceReadOnly(getDiscoverySiteEntryModels());
+		markListReferenceReadOnly(getImportModels());
+		markListReferenceReadOnly(getPluginEntryModels());
+		markListReferenceReadOnly(getNonPluginEntryModels());
+	}
+
+	/**
+	 * Resolve the model object.
+	 * Any URL strings in the model are resolved relative to the 
+	 * base URL argument. Any translatable strings in the model that are
+	 * specified as translation keys are localized using the supplied 
+	 * resource bundle.
+	 * 
+	 * @param base URL
+	 * @param bundleURL resource bundle url
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base,URL bundleURL) throws MalformedURLException {
+		this.bundleURL = bundleURL;
+		this.base = base;
+
+		// plugin entry and nonpluginentry are optimized too
+		resolveListReference(getPluginEntryModels(), base, bundleURL);
+		resolveListReference(getNonPluginEntryModels(), base, bundleURL);
+		
+		//URLSiteModel are optimized
+		resolveReference(getDescriptionModel(),base, bundleURL);
+		resolveReference(getCopyrightModel(),base, bundleURL);
+		resolveReference(getLicenseModel(),base, bundleURL);
+		resolveReference(getUpdateSiteEntryModel(),base, bundleURL);
+		resolveListReference(getDiscoverySiteEntryModels(),base, bundleURL);
+		
+		// Import Models are optimized
+		resolveListReference(getImportModels(),base, bundleURL);
+	}
+
+	private void delayedResolve() {
+
+		// PERF: delay resolution
+		if (resolved)
+			return;
+
+		// resolve local elements
+		localizedLabel = resolveNLString(bundleURL, label);
+		localizedProvider = resolveNLString(bundleURL, provider);
+		try {
+			imageURL = resolveURL(base,bundleURL, imageURLString);
+			resolved = true;
+		} catch (MalformedURLException e){
+			UpdateCore.warn("",e); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Method setPrimaryPlugin.
+	 * @param plugin
+	 */
+	public void setPrimaryPluginID(String plugin) {
+		if (primary && primaryPluginID == null) {
+			primaryPluginID = featureId;
+		}
+		primaryPluginID = plugin;
+	}
+	/**
+	 * Returns the primaryPluginID.
+	 * @return String
+	 */
+	public String getPrimaryPluginID() {
+		return primaryPluginID;
+	}
+
+	/**
+	 * Returns <code>true</code> if this feature is patching another feature,
+	 * <code>false</code> otherwise
+	 * @return boolean
+	 */
+	public boolean isPatch() {
+		ImportModel[] imports = getImportModels();
+
+		for (int i = 0; i < imports.length; i++) {
+			if (imports[i].isPatch())
+				return true;
+		}
+		return false;
+	}
+}
\ No newline at end of file
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureModelFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureModelFactory.java
new file mode 100644
index 0000000..4af40a5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureModelFactory.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.internal.core.Messages;
+import org.xml.sax.SAXException;
+
+/**
+ * Default feature model factory.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead subclass the provided base implementation 
+ * of this factory.
+ * </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>
+ * @see org.eclipse.update.core.BaseFeatureFactory
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class FeatureModelFactory {
+
+
+	//private static DefaultFeatureParser parser = new DefaultFeatureParser();
+
+	/**
+	 * Creates a default model factory.
+	 * 
+	 * @since 2.0
+	 */
+	public FeatureModelFactory() {
+		super();
+	}
+
+	/**
+	 * Creates and populates a default feature from stream.
+	 * The parser assumes the stream contains a default feature manifest
+	 * (feature.xml) as documented by the platform.
+	 * 
+	 * @param stream feature stream
+	 * @return populated feature model
+	 * @exception CoreException
+	 * @exception SAXException
+	 * @since 2.0
+	 */
+	public FeatureModel parseFeature(InputStream stream)
+		throws CoreException, SAXException {
+        return parseFeature(stream, null);
+	}
+    
+    /**
+     * Creates and populates a default feature from stream.
+     * The parser assumes the stream contains a default feature manifest
+     * (feature.xml) as documented by the platform.
+     * 
+     * @param stream feature stream
+     * @param location feature location
+     * @return populated feature model
+     * @exception CoreException
+     * @exception SAXException
+     * @since 3.1
+     */
+    public FeatureModel parseFeature(InputStream stream, String location)
+        throws CoreException, SAXException {
+    	DefaultFeatureParser parser = new DefaultFeatureParser();
+        parser.init(this, location);
+        FeatureModel featureModel = null;
+        try {
+            featureModel = parser.parse(stream);
+            if (parser.getStatus()!=null) {
+                // some internalError were detected
+                IStatus status = parser.getStatus();
+                throw new CoreException(status);
+            }
+        } catch (IOException e) {
+            throw Utilities.newCoreException(Messages.FeatureModelFactory_ErrorAccesingFeatureStream, e); 
+        }
+        return featureModel;
+    }
+
+	/**
+	 * Create a default feature model.
+	 * 
+	 * @see FeatureModel
+	 * @return feature model
+	 * @since 2.0
+	 */
+	public FeatureModel createFeatureModel() {
+		return new FeatureModel();
+	}
+
+	/**
+	 * Create a default included feature reference model.
+	 * 
+	 * @see IncludedFeatureReferenceModel
+	 * @return feature model
+	 * @since 2.1
+	 */
+	public IncludedFeatureReferenceModel createIncludedFeatureReferenceModel() {
+		return new IncludedFeatureReferenceModel();
+	}
+
+
+	/**
+	 * Create a default install handler model.
+	 * 
+	 * @see InstallHandlerEntryModel
+	 * @return install handler entry model
+	 * @since 2.0
+	 */
+	public InstallHandlerEntryModel createInstallHandlerEntryModel() {
+		return new InstallHandlerEntryModel();
+	}
+
+	/**
+	 * Create a default import dependency model.
+	 * 
+	 * @see ImportModel
+	 * @return import dependency model
+	 * @since 2.0
+	 */
+	public ImportModel createImportModel() {
+		return new ImportModel();
+	}
+
+	/**
+	 * Create a default plug-in entry model.
+	 * 
+	 * @see PluginEntryModel
+	 * @return plug-in entry model
+	 * @since 2.0
+	 */
+	public PluginEntryModel createPluginEntryModel() {
+		return new PluginEntryModel();
+	}
+
+	/**
+	 * Create a default non-plug-in entry model.
+	 * 
+	 * @see NonPluginEntryModel
+	 * @return non-plug-in entry model
+	 * @since 2.0
+	 */
+	public NonPluginEntryModel createNonPluginEntryModel() {
+		return new NonPluginEntryModel();
+	}
+
+	/**
+	 * Create a default annotated URL model.
+	 * 
+	 * @see URLEntryModel
+	 * @return annotated URL model
+	 * @since 2.0
+	 */
+	public URLEntryModel createURLEntryModel() {
+		return new URLEntryModel();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureReferenceModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureReferenceModel.java
new file mode 100644
index 0000000..3536dc0
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/FeatureReferenceModel.java
@@ -0,0 +1,427 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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
+ *     James D Miles (IBM Corp.) - bug 191783, NullPointerException in FeatureDownloader
+ *******************************************************************************/
+package org.eclipse.update.core.model;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.update.core.Site;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+
+/**
+ * Feature reference model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.FeatureReference
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class FeatureReferenceModel extends ModelObject {
+
+	private String type;
+	private URL url;
+	private String urlString;
+	private String featureId;
+	private String featureVersion;
+	private SiteModel site;
+	private String label;
+	private String localizedLabel;
+
+	// performance
+	private URL bundleURL;
+	private URL base;
+	private boolean resolved = false;
+	private String os;
+	private String ws;
+	private String nl;
+	private String arch;
+	private String patch;	
+
+	/**
+	 * Creates an uninitialized feature reference model object.
+	 * 
+	 * @since 2.0
+	 */
+	public FeatureReferenceModel() {
+		super();
+	}
+
+	/**
+	 * Constructor FeatureReferenceModel.
+	 * @param ref
+	 */
+	public FeatureReferenceModel(FeatureReferenceModel ref) {
+		setFeatureIdentifier(ref.getFeatureIdentifier());
+		setFeatureVersion(ref.getFeatureVersion());
+		setType(ref.getType());
+		setSiteModel(ref.getSiteModel());
+		setLabel(ref.getLabel());
+		setWS(ref.getWS());
+		setOS(ref.getOS());
+		setArch(ref.getOSArch());
+		setNL(ref.getNL());
+	}
+
+	/**
+	 * Compares 2 feature reference models for equality
+	 *  
+	 * @param object feature reference model to compare with
+	 * @return <code>true</code> if the two models are equal, 
+	 * <code>false</code> otherwise
+	 * @since 2.0 
+	 */
+	public boolean equals(Object object) {
+
+		if (object == null)
+			return false;
+		if (getURL() == null)
+			return false;
+
+		if (!(object instanceof FeatureReferenceModel))
+			return false;
+
+		FeatureReferenceModel f = (FeatureReferenceModel) object;
+
+		return UpdateManagerUtils.sameURL(getURL(), f.getURL());
+	}
+
+	/**
+	 * Returns the referenced feature type.
+	 * 
+	 * @return feature type, or <code>null</code> representing the default
+	 * feature type for the site
+	 * @since 2.0
+	 */
+	public String getType() {
+		return type;
+	}
+
+	/**
+	 * Returns the site model for the reference.
+	 * 
+	 * @return site model
+	 * @since 2.0
+	 */
+	public SiteModel getSiteModel() {
+		return site;
+	}
+
+	/**
+	 * Returns the unresolved URL string for the reference.
+	 *
+	 * @return url string
+	 * @since 2.0
+	 */
+	public String getURLString() {
+		return urlString;
+	}
+
+	/**
+	 * Returns the resolved URL for the feature reference.
+	 * 
+	 * @return url string
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		delayedResolve();
+		return url;
+	}
+
+	/**
+	 * Returns the feature identifier as a string
+	 * 
+	 * @see org.eclipse.update.core.IFeatureReference#getVersionedIdentifier()
+	 * @return feature identifier
+	 * @since 2.0
+	 */
+	public String getFeatureIdentifier() {
+		return featureId;
+	}
+
+	/**
+	 * Returns the feature version as a string
+	 * 
+	 * @see org.eclipse.update.core.IFeatureReference#getVersionedIdentifier()
+	 * @return feature version 
+	 * @since 2.0
+	 */
+	public String getFeatureVersion() {
+		return featureVersion;
+	}
+
+	/**
+	 * Sets the referenced feature type.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param type referenced feature type
+	 * @since 2.0
+	 */
+	public void setType(String type) {
+		assertIsWriteable();
+		this.type = type;
+	}
+
+	/**
+	 * Sets the site for the referenced.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param site site for the reference
+	 * @since 2.0
+	 */
+	public void setSiteModel(SiteModel site) {
+		assertIsWriteable();
+		this.site = site;
+	}
+
+	/**
+	 * Sets the unresolved URL for the feature reference.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param urlString unresolved URL string
+	 * @since 2.0
+	 */
+	public void setURLString(String urlString) {
+		assertIsWriteable();
+		this.urlString = urlString;
+		this.url = null;
+	}
+
+	/**
+	 * Sets the feature identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param featureId feature identifier
+	 * @since 2.0
+	 */
+	public void setFeatureIdentifier(String featureId) {
+		assertIsWriteable();
+		this.featureId = featureId;
+	}
+
+	/**
+	 * Sets the feature version.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param featureVersion feature version
+	 * @since 2.0
+	 */
+	public void setFeatureVersion(String featureVersion) {
+		assertIsWriteable();
+		this.featureVersion = featureVersion;
+	}
+
+	/**
+	 * Resolve the model object.
+	 * Any URL strings in the model are resolved relative to the 
+	 * base URL argument. Any translatable strings in the model that are
+	 * specified as translation keys are localized using the supplied 
+	 * resource bundle.
+	 * 
+	 * @param base URL
+	 * @param bundleURL resource bundle URL
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base,URL bundleURL) throws MalformedURLException {
+		this.base = base;
+		this.bundleURL = bundleURL;
+	}
+
+	private void delayedResolve() {
+
+		// PERF: delay resolution
+		if (resolved)
+			return;
+
+		// resolve local elements
+		localizedLabel = resolveNLString(bundleURL, label);
+		try {
+			url = resolveURL(base, bundleURL, urlString);
+			resolved = true;
+		} catch (MalformedURLException e){
+			UpdateCore.warn("",e); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * @see Object#toString()
+	 */
+	public String toString() {
+		StringBuffer buffer = new StringBuffer();
+		buffer.append(getClass().toString() + " :"); //$NON-NLS-1$
+		buffer.append(" at "); //$NON-NLS-1$
+		if (url != null)
+			buffer.append(url.toExternalForm());
+		return buffer.toString();
+	}
+
+	/**
+	 * @see org.eclipse.update.core.model.ModelObject#getPropertyName()
+	 */
+	protected String getPropertyName() {
+		return Site.SITE_FILE;
+	}
+	
+	/**
+	 * Retrieve the displayable label for the feature reference. If the model
+	 * object has been resolved, the label is localized.
+	 *
+	 * @return displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getLabel() {
+		delayedResolve();
+		if (localizedLabel != null)
+			return localizedLabel;
+		else
+			return label;
+	}
+
+	/**
+	 * Retrieve the non-localized displayable label for the feature reference.
+	 *
+	 * @return non-localized displayable label, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getLabelNonLocalized() {
+		return label;
+	}
+
+	/**
+	 * Sets the label.
+	 * @param label The label to set
+	 */
+	public void setLabel(String label) {
+		this.label = label;
+	}
+
+	/**
+	 * Get optional operating system specification as a comma-separated string.
+	 *
+	 * @return the operating system specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getOS() {
+		return os;
+	}
+
+
+	/**
+	 * Get optional windowing system specification as a comma-separated string.
+	 *
+	 * @return the windowing system specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getWS() {
+		return ws;
+	}
+
+
+	/**
+	 * Get optional system architecture specification as a comma-separated string.
+	 *
+	 * @return the system architecture specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getOSArch() {
+		return arch;
+	}
+
+
+	/**
+	 * Get optional locale specification as a comma-separated string.
+	 *
+	 * @return the locale specification string, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getNL() {
+		return nl;
+	}
+
+	/**
+	 * Sets the operating system specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param os operating system specification as a comma-separated list
+	 * @since 2.1
+	 */
+	public void setOS(String os) {
+		assertIsWriteable();
+		this.os = os;
+	}
+
+
+	/**
+	 * Sets the windowing system specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param ws windowing system specification as a comma-separated list
+	 * @since 2.1
+	 */
+	public void setWS(String ws) {
+		assertIsWriteable();
+		this.ws = ws;
+	}
+
+
+	/**
+	 * Sets the locale specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param nl locale specification as a comma-separated list
+	 * @since 2.1
+	 */
+	public void setNL(String nl) {
+		assertIsWriteable();
+		this.nl = nl;
+	}
+
+
+	/**
+	 * Sets the system architecture specification.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param arch system architecture specification as a comma-separated list
+	 * @since 2.1
+	 */
+	public void setArch(String arch) {
+		assertIsWriteable();
+		this.arch = arch;
+	}
+
+	/**
+	 * Returns the patch mode.
+	 */
+	public String getPatch() {
+		return patch;
+	}
+
+
+	/**
+	 * Sets the patch mode.
+	 */
+	public void setPatch(String patch) {
+		this.patch = patch;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ImportModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ImportModel.java
new file mode 100644
index 0000000..29dada1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ImportModel.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+/**
+ * Plug-in dependency model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.Import
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class ImportModel extends ModelObject {
+
+	private String id;
+	private String version;
+	private String matchingIdRuleName;
+	private String matchingRuleName;
+	private boolean featureImport;
+	private boolean patch;
+	private String osArch;
+	private String ws;
+	private String os;
+	private String nl;
+
+	/**
+	 * Creates a uninitialized plug-in dependency model object.
+	 * 
+	 * @since 2.0
+	 */
+	public ImportModel() {
+		super();
+	}
+
+	/**
+	 * Returns the dependent plug-in identifier.
+	 *
+	 * @deprecated use getIdentifier() instead
+	 * @return plug-in identifier, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getPluginIdentifier() {
+		return id;
+	}
+
+	/**
+	 * Returns the dependent identifier.
+	 *
+	 * @return  identifier, or <code>null</code>.
+	 * @since 2.0.2
+	 */
+	public String getIdentifier() {
+		return id;
+	}
+
+	/**
+	 * Returns the dependent plug-in version.
+	 *
+	 * @deprecated use getVersion() instead
+	 * @return plug-in version, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getPluginVersion() {
+		return version;
+	}
+
+	/**
+	 * Returns the dependent version.
+	 *
+	 * @return version, or <code>null</code>.
+	 * @since 2.0.2
+	 */
+	public String getVersion() {
+		return version;
+	}
+	
+	/**
+	 * Returns the dependent version matching rule name.
+	 *
+	 * @return matching rule name, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getMatchingRuleName() {
+		return matchingRuleName;
+	}
+	
+	/**
+	 * Returns the dependent id matching rule name.
+	 *
+	 * @return matching rule name, or <code>null</code>.
+	 * @since 2.1
+	 */
+	public String getMatchingIdRuleName() {
+		return matchingIdRuleName;
+	}
+
+	/**
+	 * Sets the dependent plug-in identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @deprecated use setIdentifier()
+	 * @param pluginId dependent plug-in identifier
+	 * @since 2.0
+	 */
+	public void setPluginIdentifier(String pluginId) {
+		assertIsWriteable();
+		this.id = pluginId;
+	}
+
+	/**
+	 * Sets the dependent plug-in version.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @deprecated use setVersion()
+	 * @param pluginVersion dependent plug-in version
+	 * @since 2.0
+	 */
+	public void setPluginVersion(String pluginVersion) {
+		assertIsWriteable();
+		this.version = pluginVersion;
+	}
+
+	/**
+	 * Sets the dependent identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param id dependent identifier
+	 * @since 2.0.2
+	 */
+	public void setIdentifier(String id) {
+		assertIsWriteable();
+		this.id = id;
+	}
+
+	/**
+	 * Sets the dependent version.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param version dependent version
+	 * @since 2.0.2
+	 */
+	public void setVersion(String version) {
+		assertIsWriteable();
+		this.version = version;
+	}
+	
+	/**
+	 * Sets the dependent version matching rule name. 
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param matchingRuleName dependent version matching rule.
+	 * @since 2.0
+	 */
+	public void setMatchingRuleName(String matchingRuleName) {
+		assertIsWriteable();
+		this.matchingRuleName = matchingRuleName;
+	}
+	/**
+	 * Sets the dependent id matching rule name. 
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param matchingIdRuleName dependent id matching rule.
+	 * @since 2.1
+	 */
+	public void setMatchingIdRuleName(String matchingIdRuleName) {
+		assertIsWriteable();
+		this.matchingIdRuleName = matchingIdRuleName;
+	}
+	/**
+	 * Returns the isFeatureImport.
+	 * @return boolean
+	 */
+	public boolean isFeatureImport() {
+		return featureImport;
+	}
+	
+	/**
+	 * Sets the featureImport.
+	 * @param featureImport The featureImport to set
+	 */
+	public void setFeatureImport(boolean featureImport) {
+		this.featureImport = featureImport;
+	}
+	
+	/**
+	 * Returns the patch mode.
+	 */
+	public boolean isPatch() {
+		return patch;
+	}
+	
+	/**
+	 * Sets the patch mode.
+	 */
+	public void setPatch(boolean patch) {
+		this.patch = patch;
+	}
+	/**
+	 * Returns the os.
+	 * @return String
+	 */
+	public String getOS() {
+		return os;
+	}
+
+	/**
+	 * Returns the osArch.
+	 * @return String
+	 */
+	public String getOSArch() {
+		return osArch;
+	}
+
+	/**
+	 * Returns the ws.
+	 * @return String
+	 */
+	public String getWS() {
+		return ws;
+	}
+
+	/**
+	 * Sets the os.
+	 * @param os The os to set
+	 */
+	public void setOS(String os) {
+		this.os = os;
+	}
+
+	/**
+	 * Sets the osArch.
+	 * @param osArch The osArch to set
+	 */
+	public void setOSArch(String osArch) {
+		this.osArch = osArch;
+	}
+
+	/**
+	 * Sets the ws.
+	 * @param ws The ws to set
+	 */
+	public void setWS(String ws) {
+		this.ws = ws;
+	}
+
+	/**
+	 * Returns the nl.
+	 * @return String
+	 */
+	public String getNL() {
+		return nl;
+	}
+
+	/**
+	 * Sets the nl.
+	 * @param nl The nl to set
+	 */
+	public void setNL(String nl) {
+		this.nl = nl;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/IncludedFeatureReferenceModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/IncludedFeatureReferenceModel.java
new file mode 100644
index 0000000..a2cce0a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/IncludedFeatureReferenceModel.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import org.eclipse.update.core.FeatureReference;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.IImport;
+import org.eclipse.update.core.IIncludedFeatureReference;
+import org.eclipse.update.core.IUpdateConstants;
+
+/**
+ * Included Feature reference model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.IncludedFeatureReference
+ * @since 2.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class IncludedFeatureReferenceModel extends FeatureReference {
+
+	// since 2.0.2
+	private boolean isOptional;
+	private int searchLocation;
+	
+	// These are already defined by FeatureReferenceModel, we don't need to duplicate them here
+//	// since 2.1
+//	private String os;
+//	private String ws;
+//	private String arch;
+//	private String nl;
+//	
+	/**
+	 * Construct a included feature reference
+	 * 
+	 * @since 2.1
+	 */
+	public IncludedFeatureReferenceModel() {
+		super();
+		isOptional(false);
+		setSearchLocation(IUpdateConstants.SEARCH_ROOT);
+	}
+	
+	
+	
+	/**
+	 * Construct a included feature reference model
+	 * 
+	 * @param includedFeatureRef the included reference model to copy
+	 * @since 2.1
+	 */
+	public IncludedFeatureReferenceModel(IncludedFeatureReferenceModel includedFeatureRef) {
+		super((FeatureReferenceModel)includedFeatureRef);
+		isOptional(includedFeatureRef.isOptional());
+		setLabel(includedFeatureRef.getLabel());
+		setSearchLocation(includedFeatureRef.getSearchLocation());
+		setArch(includedFeatureRef.getOSArch());
+		setWS(includedFeatureRef.getWS());
+		setOS(includedFeatureRef.getOS());
+		setNL(includedFeatureRef.getNL());
+		setPatch(includedFeatureRef.getPatch());
+	}
+
+	/**
+	 * Constructor IncludedFeatureReferenceModel.
+	 * @param featureReference
+	 */
+	public IncludedFeatureReferenceModel(IFeatureReference featureReference) {
+		super((FeatureReferenceModel)featureReference);
+		if (featureReference instanceof IIncludedFeatureReference)
+			  isOptional( ((IIncludedFeatureReference)featureReference).isOptional() );
+			else
+			  isOptional(false);
+		setSearchLocation(IUpdateConstants.SEARCH_ROOT);
+		setLabel(getLabel());		
+	}
+
+	/**
+	 * Returns the matching rule for this included feature.
+	 * The rule will determine the ability of the included feature to move version 
+	 * without causing the overall feature to appear broken.
+	 * 
+	 * The default is <code>MATCH_PERFECT</code>
+	 * 
+	 * @see IImport#RULE_PERFECT
+	 * @see IImport#RULE_EQUIVALENT
+	 * @see IImport#RULE_COMPATIBLE
+	 * @see IImport#RULE_GREATER_OR_EQUAL
+	 * @return int representation of feature matching rule.
+	 * @since 2.0.2
+	 * @deprecated since 3.0 included feature version is exactly specified
+	 */
+	public int getMatch(){
+		return IUpdateConstants.RULE_PERFECT;
+	}
+
+	/**
+	 * Returns the search location for this included feature.
+	 * The location will be used to search updates for this feature.
+	 * 
+	 * The default is <code>SEARCH_ROOT</code>
+	 * 
+	 * @see IUpdateConstants#SEARCH_ROOT
+	 * @see IUpdateConstants#SEARCH_SELF
+	 * @return int representation of feature searching rule.
+	 * @since 2.0.2
+	 */
+
+	public int getSearchLocation(){
+		return searchLocation;
+	}
+	
+
+
+	/**
+	 * Returns the isOptional
+	 * 
+	 * @return isOptional
+	 * @since 2.0.1
+	 */
+	public boolean isOptional() {
+		return isOptional;
+	}
+
+
+	
+
+	/**
+	 * Sets the isOptional.
+	 * @param isOptional The isOptional to set
+	 */
+	public void isOptional(boolean isOptional) {
+		this.isOptional = isOptional;
+	}
+
+	/**
+	 * Sets the matchingRule.
+	 * @param matchingRule The matchingRule to set
+	 * @deprecated since 3.0 included feature version is exactly specified
+	 */
+	public void setMatchingRule(int matchingRule) {
+	}
+
+	/**
+	 * Sets the searchLocation.
+	 * @param searchLocation The searchLocation to set
+	 */
+	public void setSearchLocation(int searchLocation) {
+		this.searchLocation = searchLocation;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InstallAbortedException.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InstallAbortedException.java
new file mode 100644
index 0000000..544a934
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InstallAbortedException.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Exception thrown when the user cancelled an installation.
+ * <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>
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class InstallAbortedException extends CoreException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+	 * Construct the exception indicating enclosing CoreException
+	 * 
+	 * @since 2.0
+	 */
+	public InstallAbortedException(String msg,Exception e) {
+		super(new Status(IStatus.INFO,"org.eclipse.update.core",IStatus.OK,msg,e)); //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InstallHandlerEntryModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InstallHandlerEntryModel.java
new file mode 100644
index 0000000..ea22ec7
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InstallHandlerEntryModel.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Install handler entry model object.
+ * An object which represents the definition of a custom install handler
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.InstallHandlerEntry
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class InstallHandlerEntryModel extends ModelObject {
+
+	private String urlString;
+	private URL url;
+	private String library;
+	private String name;
+
+	/**
+	 * Creates a uninitialized install handler entry model object.
+	 * 
+	 * @since 2.0
+	 */
+	public InstallHandlerEntryModel() {
+		super();
+	}
+
+	/**
+	 * Returns the URL string used for browser-triggered installation handling.
+	 *
+	 * @return url string or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getURLString() {
+		return urlString;
+	}
+
+	/**
+	 * Returns the resolved URL used for browser-triggered installation handling.
+	 * 
+	 * @return url, or <code>null</code>
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		return url;
+	}
+
+	/**
+	 * Returns the name of the custom installer library.
+	 *
+	 * @return library path, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getLibrary() {
+		return library;
+	}
+
+	/**
+	 * Returns the name of the custom installer.
+	 *
+	 * @return handler name, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getHandlerName() {
+		return name;
+	}
+
+	/**
+	 * Sets URL string used for browser-triggered installation handling.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param urlString trigget page URL string, may be <code>null</code>.
+	 * @since 2.0
+	 */
+	public void setURLString(String urlString) {
+		assertIsWriteable();
+		this.urlString = urlString;
+		this.url = null;
+	}
+
+	/**
+	 * Sets the custom install handler library name.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param library name, may be <code>null</code>.
+	 * @since 2.0
+	 */
+	public void setLibrary(String library) {
+		assertIsWriteable();
+		this.library = library;
+	}
+
+	/**
+	 * Sets the name of the custom install handler.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param name name of the install handler, may be <code>null</code>.
+	 * @since 2.0
+	 */
+	public void setHandlerName(String name) {
+		assertIsWriteable();
+		this.name = name;
+	}
+
+	/**
+	 * Resolve the model object.
+	 * Any URL strings in the model are resolved relative to the 
+	 * base URL argument. Any translatable strings in the model that are
+	 * specified as translation keys are localized using the supplied 
+	 * resource bundle.
+	 * 
+	 * @param base URL
+	 * @param bundleURL resource bundle URL
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base,URL bundleURL)
+		throws MalformedURLException {
+		// resolve local elements
+		url = resolveURL(base,bundleURL, urlString);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InvalidSiteTypeException.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InvalidSiteTypeException.java
new file mode 100644
index 0000000..50c9214
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/InvalidSiteTypeException.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+/**
+ * Exception thrown when the type of the site discovered in the site manifest
+ * does not correspond to the type expected by the concrete site factory.
+ * 
+ * <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>
+ * @see org.eclipse.update.core.model.SiteModelFactory#canParseSiteType(String)
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class InvalidSiteTypeException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+    private String newSiteType;
+
+	/**
+	 * Construct the exception indicating the detected site type
+	 * 
+	 * @since 2.0
+	 */
+	public InvalidSiteTypeException(String newType) {
+		super();
+		newSiteType = newType;
+	}
+
+	/**
+	 * Returns the site type detected in the parsed site manifest
+	 * 
+	 * @return site type
+	 * @since 2.0
+	 */
+	public String getNewType() {
+		return newSiteType;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ModelObject.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ModelObject.java
new file mode 100644
index 0000000..9b55b02
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/ModelObject.java
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.lang.reflect.Array;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.update.core.Feature;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+
+/**
+ * Root model object. Extended by all model objects.
+ * <p>
+ * This class cannot be instantiated and must be subclassed.
+ * </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>
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class ModelObject extends PlatformObject {
+
+	private boolean readOnly = false;
+
+	private static final String KEY_PREFIX = "%"; //$NON-NLS-1$
+	private static final String KEY_DOUBLE_PREFIX = KEY_PREFIX + KEY_PREFIX;
+
+	private static Map bundles;
+
+	/**
+	 * Creates a base model object.
+	 * 
+	 * @since 2.0
+	 */
+	protected ModelObject() {
+	}
+
+	/**
+	 * Checks that this model object is writeable.  A runtime exception
+	 * is thrown if it is not.
+	 * 
+	 * @since 2.0
+	 */
+	protected final void assertIsWriteable() {
+		Assert.isTrue(!isReadOnly(), Messages.ModelObject_ModelReadOnly);	
+	}
+
+	/**
+	 * Sets this model object and all of its descendents to be read-only.
+	 * Subclasses may extend this implementation.
+	 * 
+	 * @see #isReadOnly
+	 * @since 2.0
+	 */
+	public void markReadOnly() {
+		readOnly = true;
+	}
+
+	/**
+	 * Returns whether or not this model object is read-only.
+	 * 
+	 * @return <code>true</code> if this model object is read-only,
+	 *		<code>false</code> otherwise
+	 * @see #markReadOnly
+	 * @since 2.0
+	 */
+	public boolean isReadOnly() {
+		return readOnly;
+	}
+
+	/**
+	 * Delegate setting of read-only
+	 *
+	 * @param o object to delegate to. Must be of type ModelObject.
+	 * @see #isReadOnly
+	 * @since 2.0
+	 */
+	protected void markReferenceReadOnly(ModelObject o) {
+		if (o == null)
+			return;
+		o.markReadOnly();
+	}
+
+	/**
+	 * Delegate setting of read-only
+	 *
+	 * @param o object array to delegate to. Each element must be of type ModelObject.
+	 * @see #isReadOnly
+	 * @since 2.0
+	 */
+	protected void markListReferenceReadOnly(ModelObject[] o) {
+		if (o == null)
+			return;
+		for (int i = 0; i < o.length; i++) {
+			o[i].markReadOnly();
+		}
+	}
+
+	/**
+	 * Resolve the model element. This method allows any relative URL strings
+	 * to be resolved to actual URL. It also allows any translatable strings
+	 * to be localized.
+	 * 
+	 * Subclasses need to override this method to perform the actual resolution.
+	 * @param base base URL.
+	 * @param bundleURL resource bundle URL.
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base, URL bundleURL) throws MalformedURLException {
+		return;
+	}
+
+	/**
+	 * Delegate resolution to referenced model
+	 *
+	 * @param o object to delegate to. Must be of type ModelObject.
+	 * @param url base URL.
+	 * @param bundleURL resource bundle URL.
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	protected void resolveReference(ModelObject o, URL url, URL bundleURL) throws MalformedURLException {
+		if (o == null)
+			return;
+		o.resolve(url, bundleURL);
+	}
+
+	/**
+	 * Delegate resolution to list of referenced models
+	 *
+	 * @param o object array to delegate to. Each element must be of type ModelObject.
+	 * @param url base URL.
+	 * @param bundleURL resource bundle URL.
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	protected void resolveListReference(ModelObject[] o, URL url, URL bundleURL) throws MalformedURLException {
+		if (o == null)
+			return;
+		for (int i = 0; i < o.length; i++) {
+			o[i].resolve(url, bundleURL);
+		}
+	}
+
+	/**
+	 * Resolve a URL based on context
+	 *
+	 * @param context base URL.
+	 * @param bundleURL resource bundle URL.
+	 * @param urlString url string from model.
+	 * @return URL, or <code>null</code>.
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	protected URL resolveURL(URL context, URL bundleURL, String urlString) throws MalformedURLException {
+
+		// URL string was not specified
+		if (urlString == null || urlString.trim().equals("")) //$NON-NLS-1$
+			return null;
+
+		// check to see if we have NL-sensitive URL
+		String resolvedUrlString = resolveNLString(bundleURL, urlString);
+
+		resolvedUrlString = resolvePlatfromConfiguration(resolvedUrlString);
+
+		// if we don't have a base url, use only the supplied string
+		if (context == null)
+			return new URL(resolvedUrlString);
+
+		// otherwise return new URL in context of base URL
+		return new URL(context, resolvedUrlString);
+	}
+	/**
+	 * Resolves the URL based on platfrom Configuration
+	 * $os$\$ws$\license.txt will become
+	 * win32\win32\license.txt on a system where os=win32 and ws=win32
+	 * 
+	 * @param resolvedUrlString
+	 * @return String
+	 */
+	private String resolvePlatfromConfiguration(String resolvedUrlString) {
+		int osIndex = resolvedUrlString.indexOf("$os$"); //$NON-NLS-1$
+		if (osIndex != -1)
+			return getExtendedString(resolvedUrlString);
+
+		int wsIndex = resolvedUrlString.indexOf("$ws$"); //$NON-NLS-1$
+		if (wsIndex != -1)
+			return getExtendedString(resolvedUrlString);
+
+		int nlIndex = resolvedUrlString.indexOf("$nl$"); //$NON-NLS-1$
+		if (nlIndex != -1)
+			return getExtendedString(resolvedUrlString);
+
+		int archIndex = resolvedUrlString.indexOf("$arch$"); //$NON-NLS-1$
+		if (archIndex != -1)
+			return getExtendedString(resolvedUrlString);
+
+		return resolvedUrlString;
+	}
+
+	private String getExtendedString(String resolvedUrlString) {
+		IPath path = new Path(resolvedUrlString);
+		path = getExpandedPath(path);
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS) {
+			UpdateCore.warn("Resolved :" + resolvedUrlString + " as:" + path.toOSString()); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		return path.toOSString();
+	}
+
+	private IPath getExpandedPath(IPath path) {
+		String first = path.segment(0);
+		if (first != null) {
+			IPath rest = getExpandedPath(path.removeFirstSegments(1));
+			if (first.equals("$ws$")) { //$NON-NLS-1$
+				path = new Path(SiteManager.getWS()).append(rest);
+			} else if (first.equals("$os$")) { //$NON-NLS-1$
+				path = new Path(SiteManager.getOS()).append(rest);
+			} else if (first.equals("$nl$")) { //$NON-NLS-1$
+				path = new Path(SiteManager.getNL()).append(rest);
+			} else if (first.equals("$arch$")) { //$NON-NLS-1$
+				path = new Path(SiteManager.getOSArch()).append(rest);
+			}
+		}
+		return path;
+	}
+
+	/**
+	 * Returns a resource string corresponding to the given argument 
+	 * value and bundle.
+	 * If the argument value specifies a resource key, the string
+	 * is looked up in the given resource bundle. If the argument does not
+	 * specify a valid key, the argument itself is returned as the
+	 * resource string. The key lookup is performed against the
+	 * specified resource bundle. If a resource string 
+	 * corresponding to the key is not found in the resource bundle
+	 * the key value, or any default text following the key in the
+	 * argument value is returned as the resource string.
+	 * A key is identified as a string begining with the "%" character.
+	 * Note that the "%" character is stripped off prior to lookup
+	 * in the resource bundle.
+	 * <p>
+	 * For example, assume resource bundle plugin.properties contains
+	 * name = Project Name
+	 * <pre>
+	 *     resolveNLString(b,"Hello World") returns "Hello World"</li>
+	 *     resolveNLString(b,"%name") returns "Project Name"</li>
+	 *     resolveNLString(b,"%name Hello World") returns "Project Name"</li>
+	 *     resolveNLString(b,"%abcd Hello World") returns "Hello World"</li>
+	 *     resolveNLString(b,"%abcd") returns "%abcd"</li>
+	 *     resolveNLString(b,"%%name") returns "%name"</li>
+	 * </pre>
+	 * </p>
+	 * 
+	 * @param bundleURL resource bundle url.
+	 * @param string translatable string from model
+	 * @return string, or <code>null</code>
+	 * @since 2.0
+	 */
+	protected String resolveNLString(URL bundleURL, String string) {
+
+		if (string == null)
+			return null;
+
+		String s = string.trim();
+
+		if (s.equals("")) //$NON-NLS-1$
+			return string;
+
+		if (!s.startsWith(KEY_PREFIX))
+			return string;
+
+		if (s.startsWith(KEY_DOUBLE_PREFIX))
+			return s.substring(1);
+
+		int ix = s.indexOf(" "); //$NON-NLS-1$
+		String key = ix == -1 ? s : s.substring(0, ix);
+		String dflt = ix == -1 ? s : s.substring(ix + 1);
+
+		ResourceBundle b = getResourceBundle(bundleURL);
+
+		if (b == null)
+			return dflt;
+
+		try {
+			return b.getString(key.substring(1));
+		} catch (MissingResourceException e) {
+			return dflt;
+		}
+	}
+
+	/**
+	 * Returns a concrete array type for the elements of the specified
+	 * list. The method assumes all the elements of the list are the same
+	 * concrete type as the first element in the list.
+	 * 
+	 * @param l list
+	 * @return concrete array type, or <code>null</code> if the array type
+	 * could not be determined (the list is <code>null</code> or empty)
+	 * @since 2.0
+	 */
+	protected Object[] arrayTypeFor(List l) {
+		if (l == null || l.size() == 0)
+			return null;
+		return (Object[]) Array.newInstance(l.get(0).getClass(), 0);
+	}
+
+	/**
+	 * Returns a concrete array type for the elements of the specified
+	 * set. The method assumes all the elements of the set are the same
+	 * concrete type as the first element in the set.
+	 * 
+	 * @param s set
+	 * @return concrete array type, or <code>null</code> if the array type
+	 * could not be determined (the set is <code>null</code> or empty)
+	 * @since 2.0
+	 */
+	protected Object[] arrayTypeFor(Set s) {
+		if (s == null || s.size() == 0)
+			return null;
+		Iterator i = s.iterator();
+		return (Object[]) Array.newInstance(i.next().getClass(), 0);
+	}
+
+	/**
+		* Helper method to access resouce bundle for feature. The default 
+		* implementation attempts to load the appropriately localized 
+		* feature.properties file.
+		* 
+		* @param url base URL used to load the resource bundle.
+		* @return resource bundle, or <code>null</code>.
+		* @since 2.0
+		*/
+	protected ResourceBundle getResourceBundle(URL url) {
+
+		if (url == null)
+			return null;
+
+		if (bundles == null) {
+			bundles = new HashMap();
+		} else {
+			ResourceBundle bundle = (ResourceBundle) bundles.get(url.toExternalForm());
+			if (bundle != null)
+				return bundle;
+		}
+
+		ResourceBundle bundle = null;
+		try {
+			url = UpdateManagerUtils.asDirectoryURL(url);
+			ClassLoader l = new URLClassLoader(new URL[] { url }, null);
+			bundle = ResourceBundle.getBundle(getPropertyName(), Locale.getDefault(), l);
+			bundles.put(url.toExternalForm(), bundle);
+		} catch (MissingResourceException e) {
+			UpdateCore.warn(e.getLocalizedMessage() + ":" + url.toExternalForm()); //$NON-NLS-1$
+		} catch (MalformedURLException e) {
+			UpdateCore.warn(e.getLocalizedMessage()); 
+		}
+		return bundle;
+	}
+
+	/**
+	 * Method getPropertyName.
+	 * @return String
+	 */
+	protected String getPropertyName() {
+		return Feature.FEATURE_FILE;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/NonPluginEntryModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/NonPluginEntryModel.java
new file mode 100644
index 0000000..44ec8f5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/NonPluginEntryModel.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+/**
+ * Non-plug-in entry model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.NonPluginEntry
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class NonPluginEntryModel extends ContentEntryModel {
+
+	private String id = null;
+
+	/**
+	 * Creates a uninitialised non-plug-in entry model object.
+	 * 
+	 * @since 2.0
+	 */
+	public NonPluginEntryModel() {
+		super();
+	}
+
+	/**
+	 * Returns the entry identifier.
+	 *
+	 * @return entry identifier, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getIdentifier() {
+		return id;
+	}
+
+	/**
+	 * Sets the entry identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param id entry identifier.
+	 * @since 2.0
+	 */
+	public void setIdentifier(String id) {
+		assertIsWriteable();
+		this.id = id;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/PluginEntryModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/PluginEntryModel.java
new file mode 100644
index 0000000..b78c1dc
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/PluginEntryModel.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+
+/**
+ * Plug-in entry model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.PluginEntry
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class PluginEntryModel extends ContentEntryModel {
+
+	private String pluginId;
+	private String pluginVersion;
+	private boolean isFragment = false;
+	private boolean unpack = true;
+	
+	/**
+	 * Creates a uninitialized plug-in entry model object.
+	 * 
+	 * @since 2.0
+	 */
+	public PluginEntryModel() {
+		super();
+	}
+
+	/**
+	 * Compares two plug-in models for equality
+	 * 
+	 * @param obj other model to compare to
+	 * @return <code>true</code> if the models are equal, <code>false</code> otherwise
+	 * @since 2.0
+	 */
+	public boolean equals(Object obj) {
+		if (!(obj instanceof PluginEntryModel))
+			return false;
+		PluginEntryModel model = (PluginEntryModel) obj;
+		
+		return (
+			(getPluginIdentifier().equals(model.getPluginIdentifier()))
+				&& (getPluginVersion().equals(model.getPluginVersion()))
+				&& (isFragment() == model.isFragment()));
+	}
+
+	/**
+	 * Returns the plug-in identifier for this entry.
+	 * 
+	 * @return the plug-in identifier, or <code>null</code>
+	 * @since 2.0 
+	 */
+	public String getPluginIdentifier() {
+		return pluginId;
+	}
+
+	/**
+	 * Returns the plug-in version for this entry.
+	 * 
+	 * @return the plug-in version, or <code>null</code>
+	 * @since 2.0 
+	 */
+	public String getPluginVersion() {
+		return pluginVersion;
+	}
+
+	/**
+	 * Indicates whether the entry describes a full plug-in, or 
+	 * a plug-in fragment.
+	 * 
+	 * @return <code>true</code> if the entry is a plug-in fragment, 
+	 * <code>false</code> if the entry is a plug-in
+	 * @since 2.0 
+	 */
+	public boolean isFragment() {
+		return isFragment;
+	}
+
+	/**
+	 * Sets the entry plug-in identifier.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param pluginId the entry identifier.
+	 * @since 2.0
+	 */
+	public void setPluginIdentifier(String pluginId) {
+		assertIsWriteable();
+		this.pluginId = pluginId;
+	}
+
+	/**
+	 * Sets the entry plug-in version.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param pluginVersion the entry version.
+	 * @since 2.0
+	 */
+	public void setPluginVersion(String pluginVersion) {
+		assertIsWriteable();
+		this.pluginVersion = pluginVersion;
+	}
+
+	/**
+	 * Indicates whether this entry represents a fragment or plug-in.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param isFragment fragment setting
+	 * @since 2.0
+	 */
+	public void isFragment(boolean isFragment) {
+		assertIsWriteable();
+		this.isFragment = isFragment;
+	}
+
+	/**
+	 * @return Indicates whether plugin should be unpacked during installation
+	 * or can run from a jar
+	 * @since 3.0
+	 */
+	public boolean isUnpack() {
+		// TODO this is a candidate for IPluginEntry API
+		return unpack;
+	}
+	/**
+	 * @param unpack Sets whether plugin should be unpacked during installation
+	 * or can run from a jar
+	 * @since 3.0
+	 *
+	 */
+	public void setUnpack(boolean unpack) {
+		// TODO this is a candidate for IPluginEntry API
+		assertIsWriteable();
+		this.unpack = unpack;
+	}
+
+	/**
+	 * @see Object#toString()
+	 */
+	public String toString() {
+		String msg = (getPluginIdentifier()!=null)?getPluginIdentifier().toString():""; //$NON-NLS-1$
+		msg += getPluginVersion()!=null?" "+getPluginVersion().toString():""; //$NON-NLS-1$ //$NON-NLS-2$
+		msg += isFragment()?" fragment":" plugin"; //$NON-NLS-1$ //$NON-NLS-2$
+		return msg;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/SiteModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/SiteModel.java
new file mode 100644
index 0000000..bd8f17c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/SiteModel.java
@@ -0,0 +1,465 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.update.core.Site;
+import org.eclipse.update.core.SiteFeatureReferenceModel;
+import org.eclipse.update.internal.core.ExtendedSite;
+import org.eclipse.update.internal.core.SiteURLFactory;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.internal.model.ConfiguredSiteModel;
+
+/**
+ * Site model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.Site
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class SiteModel extends ModelObject {
+
+	private String type;
+	private URLEntryModel description;
+	private List /*of FeatureReferenceModel*/	featureReferences;
+	private List /*of ArchiveReferenceModel*/	archiveReferences;
+	private Set /*of CategoryModel*/	categories;
+	private List /* of URLEntryModel */ mirrors;
+	private String locationURLString;
+	private URL locationURL;
+	private String mirrorsURLString;
+	private ConfiguredSiteModel configuredSiteModel;
+
+	/**
+	 * Creates an uninitialized site model object.
+	 * 
+	 * @since 2.0
+	 */
+	public SiteModel() {
+		super();
+	}
+
+	/** 
+	 * Returns the site type.
+	 * 
+	 * @return site type, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getType() {
+		return type;
+	}
+
+	/**
+	 * Returns the site description.
+	 * 
+	 * @return site description, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public URLEntryModel getDescriptionModel() {
+		return description;
+	}
+
+	/**
+	 * Returns an array of feature reference models on this site.
+	 * 
+	 * @return an array of feature reference models, or an empty array.
+	 * @since 2.0
+	 */
+	public SiteFeatureReferenceModel[] getFeatureReferenceModels() {
+		if (featureReferences == null || featureReferences.size() == 0)
+			return new SiteFeatureReferenceModel[0];
+
+		return (SiteFeatureReferenceModel[]) featureReferences.toArray(arrayTypeFor(featureReferences));
+	}
+
+	/**
+	 * Returns an array of plug-in and non-plug-in archive reference models
+	 * on this site
+	 * 
+	 * @return an array of archive reference models, or an empty array if there are
+	 * no archives known to this site.
+	 * @since 2.0
+	 */
+	public ArchiveReferenceModel[] getArchiveReferenceModels() {
+		if (archiveReferences == null || archiveReferences.size() == 0)
+			return new ArchiveReferenceModel[0];
+
+		return (ArchiveReferenceModel[]) archiveReferences.toArray(arrayTypeFor(archiveReferences));
+	}
+
+	/**
+	 * Returns an array of category models for this site.
+	 * 
+	 * @return array of site category models, or an empty array.
+	 * @since 2.0
+	 */
+	public CategoryModel[] getCategoryModels() {
+		if (categories == null || categories.size()==0)
+			return new CategoryModel[0];
+
+		return (CategoryModel[]) categories.toArray(arrayTypeFor(categories));
+	}
+
+	/**
+	 * Returns the unresolved URL string for the site.
+	 *
+	 * @return url string, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getLocationURLString() {
+		return locationURLString;
+	}
+
+	/**
+	 * Returns the resolved URL for the site.
+	 * 
+	 * @return url, or <code>null</code>
+	 * @since 2.0
+	 */
+	public URL getLocationURL() {
+		return locationURL;
+	}
+
+	/**
+	 * Sets the site type.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param type site type
+	 * @since 2.0
+	 */
+	public void setType(String type) {
+		assertIsWriteable();
+		this.type = type;
+	}
+
+	/**
+	 * Sets the site description.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param description site description
+	 * @since 2.0
+	 */
+	public void setDescriptionModel(URLEntryModel description) {
+		assertIsWriteable();
+		this.description = description;
+	}
+
+	/**
+	 * Sets the feature references for this site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param featureReferences an array of feature reference models
+	 * @since 2.0
+	 */
+	public void setFeatureReferenceModels(FeatureReferenceModel[] featureReferences) {
+		assertIsWriteable();
+		if (featureReferences == null)
+			this.featureReferences = null;
+		else
+			this.featureReferences = new ArrayList(Arrays.asList(featureReferences));
+	}
+
+	/**
+	 * Sets the archive references for this site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param archiveReferences an array of archive reference models
+	 * @since 2.0
+	 */
+	public void setArchiveReferenceModels(ArchiveReferenceModel[] archiveReferences) {
+		assertIsWriteable();
+		if (archiveReferences == null)
+			this.archiveReferences = null;
+		else
+			this.archiveReferences = new ArrayList(Arrays.asList(archiveReferences));
+	}
+
+	/**
+	 * Sets the site categories.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param categories an array of category models
+	 * @since 2.0
+	 */
+	public void setCategoryModels(CategoryModel[] categories) {
+		assertIsWriteable();
+		if (categories == null)
+			this.categories = null;
+		else {
+			this.categories = new TreeSet(CategoryModel.getComparator());
+			this.categories.addAll(Arrays.asList(categories));
+		}
+	}
+
+	/**
+	 * Sets the unresolved URL for the site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param locationURLString url for the site (as a string)
+	 * @since 2.0
+	 */
+	public void setLocationURLString(String locationURLString) {
+		assertIsWriteable();
+		this.locationURLString = locationURLString;
+	}
+
+	/**
+	 * Adds a feature reference model to site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param featureReference feature reference model
+	 * @since 2.0
+	 */
+	public void addFeatureReferenceModel(SiteFeatureReferenceModel featureReference) {
+		assertIsWriteable();
+		if (this.featureReferences == null)
+			this.featureReferences = new ArrayList();
+		// PERF: do not check if already present 
+		//if (!this.featureReferences.contains(featureReference))
+			this.featureReferences.add(featureReference);
+	}
+
+	/**
+	 * Adds an archive reference model to site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param archiveReference archive reference model
+	 * @since 2.0
+	 */
+	public void addArchiveReferenceModel(ArchiveReferenceModel archiveReference) {
+		assertIsWriteable();
+		if (this.archiveReferences == null)
+			this.archiveReferences = new ArrayList();
+		if (!this.archiveReferences.contains(archiveReference))
+			this.archiveReferences.add(archiveReference);
+	}
+
+	/**
+	 * Adds a category model to site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param category category model
+	 * @since 2.0
+	 */
+	public void addCategoryModel(CategoryModel category) {
+		assertIsWriteable();
+		if (this.categories == null)
+			this.categories = new TreeSet(CategoryModel.getComparator());
+		if (!this.categories.contains(category))
+			this.categories.add(category);
+	}
+	
+	/**
+	 * Adds a mirror site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param mirror mirror model 
+	 * @since 3.1
+	 */
+	public void addMirrorModel(URLEntryModel mirror) {
+		assertIsWriteable();
+		if (this.mirrors == null)
+			this.mirrors = new ArrayList();
+		if (!this.mirrors.contains(mirror))
+			this.mirrors.add(mirror);
+	}
+
+	/**
+	 * Removes a feature reference model from site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param featureReference feature reference model
+	 * @since 2.0
+	 */
+	public void removeFeatureReferenceModel(FeatureReferenceModel featureReference) {
+		assertIsWriteable();
+		if (this.featureReferences != null)
+			this.featureReferences.remove(featureReference);
+	}
+
+	/**
+	 * Removes an archive reference model from site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param archiveReference archive reference model
+	 * @since 2.0
+	 */
+	public void removeArchiveReferenceModel(ArchiveReferenceModel archiveReference) {
+		assertIsWriteable();
+		if (this.archiveReferences != null)
+			this.archiveReferences.remove(archiveReference);
+	}
+
+	/**
+	 * Removes a category model from site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param category category model
+	 * @since 2.0
+	 */
+	public void removeCategoryModel(CategoryModel category) {
+		assertIsWriteable();
+		if (this.categories != null)
+			this.categories.remove(category);
+	}
+
+	/**
+	 * Removes a mirror from site.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param mirror mirror to remove
+	 * @since 3.1
+	 */
+	public void removeMirror(URLEntryModel mirror) {
+		assertIsWriteable();
+		if (this.mirrors != null)
+			this.mirrors.remove(mirror);
+	}
+	
+	/**
+	 * Marks the model object as read-only.
+	 * 
+	 * @since 2.0
+	 */
+	public void markReadOnly() {
+		super.markReadOnly();
+		markReferenceReadOnly(getDescriptionModel());
+		markListReferenceReadOnly(getFeatureReferenceModels());
+		markListReferenceReadOnly(getArchiveReferenceModels());
+		markListReferenceReadOnly(getCategoryModels());
+	}
+
+	/**
+	 * Resolve the model object.
+	 * Any URL strings in the model are resolved relative to the 
+	 * base URL argument. Any translatable strings in the model that are
+	 * specified as translation keys are localized using the supplied 
+	 * resource bundle.
+	 * 
+	 * @param base URL
+	 * @param bundleURL resource bundle URL
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base, URL bundleURL) throws MalformedURLException {
+
+		// Archives and feature are relative to location URL
+		// if the Site element has a URL tag: see spec	
+		locationURL = resolveURL(base, bundleURL, getLocationURLString());
+		if (locationURL == null)
+			locationURL = base;
+		resolveListReference(getFeatureReferenceModels(), locationURL, bundleURL);
+		resolveListReference(getArchiveReferenceModels(), locationURL, bundleURL);
+
+		resolveReference(getDescriptionModel(), base, bundleURL);
+		resolveListReference(getCategoryModels(), base, bundleURL);
+		
+		URL url = resolveURL(base, bundleURL, mirrorsURLString);
+		if (url != null)
+			mirrorsURLString = url.toString();
+		
+		if ( (this instanceof ExtendedSite) && ((ExtendedSite)this).isDigestExist()) {
+			ExtendedSite extendedSite = (ExtendedSite)this;
+			extendedSite.setLiteFeatures(UpdateManagerUtils.getLightFeatures(extendedSite));
+		}
+	}
+
+	/**
+	 * 
+	 */
+	public ConfiguredSiteModel getConfiguredSiteModel() {
+		return this.configuredSiteModel;
+	}
+
+	/**
+	 * 
+	 */
+	public void setConfiguredSiteModel(ConfiguredSiteModel configuredSiteModel) {
+		this.configuredSiteModel = configuredSiteModel;
+	}
+
+	/**
+	 * @see org.eclipse.update.core.model.ModelObject#getPropertyName()
+	 */
+	protected String getPropertyName() {
+		return Site.SITE_FILE;
+	}
+
+	/**
+	 * Return an array of updat site mirrors
+	 * 
+	 * @return an array of mirror entries, or an empty array.
+	 * @since 3.1
+	 */
+	public URLEntryModel[] getMirrorSiteEntryModels() {
+		//delayedResolve(); no delay;
+		if ( mirrors == null || mirrors.size() == 0) 
+			// see if we can get mirrors from the provided url
+			if (mirrorsURLString != null) 
+				doSetMirrorSiteEntryModels(DefaultSiteParser.getMirrors(mirrorsURLString, new SiteURLFactory()));
+				
+		if (mirrors == null || mirrors.size() == 0)
+			return new URLEntryModel[0];
+		else
+			return (URLEntryModel[]) mirrors.toArray(arrayTypeFor(mirrors));
+	}
+	
+	/**
+	 * Sets additional mirror sites
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param mirrors additional update site mirrors
+	 * @since 3.1
+	 */
+	public void setMirrorSiteEntryModels(URLEntryModel[] mirrors) {
+		assertIsWriteable();
+		doSetMirrorSiteEntryModels(mirrors);
+	}
+	
+	private void doSetMirrorSiteEntryModels(URLEntryModel[] mirrors) {
+		if (mirrors == null || mirrors.length == 0)
+			this.mirrors = null;
+		else
+			this.mirrors = new ArrayList(Arrays.asList(mirrors));
+	}
+	
+	/**
+	 * Sets the mirrors url. Mirror sites will then be obtained from this mirror url later.
+	 * This method is complementary to setMirrorsiteEntryModels(), and only one of these 
+	 * methods should be called.
+	 * Throws a runtime exception if this object is marked read-only.
+	 * 
+	 * @param mirrorsURL additional update site mirrors
+	 * @since 3.1
+	 */
+	public void setMirrorsURLString(String mirrorsURL) {
+		assertIsWriteable();
+		this.mirrorsURLString = mirrorsURL;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/SiteModelFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/SiteModelFactory.java
new file mode 100644
index 0000000..097884f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/SiteModelFactory.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.update.core.SiteFeatureReferenceModel;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.internal.core.Messages;
+import org.xml.sax.SAXException;
+
+/**
+ * Default site model factory.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead subclass the provided base implementation 
+ * of this factory.
+ * </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>
+ * @see org.eclipse.update.core.BaseSiteFactory
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class SiteModelFactory {
+	
+	private static DefaultSiteParser parser = new DefaultSiteParser();
+
+	/**
+	 * Creates a default site factory.
+	 * 
+	 * @since 2.0
+	 */
+	public SiteModelFactory() {
+		super();
+	}
+
+	/**
+	 * Indicates whether this factory can handle the specified site type. This
+	 * method is intended to be overridden by subclasses.
+	 * 
+	 * @param type site type identifier
+	 * @return <code>true</code> if the type can be handled, otherwise <code>false</code>
+	 * @since 2.0
+	 */
+	public boolean canParseSiteType(String type) {
+		// return true if type was not specified (ie. is null or empty string)
+		return (type == null || type.trim().equals("")); //$NON-NLS-1$
+	}
+
+	/**
+	 * Creates and populates a default site from stream.
+	 * The parser assumes the stream contains a default site manifest
+	 * (site.xml) as documented by the platform.
+	 * 
+	 * @param stream site stream
+	 * @return populated site model
+	 * @exception CoreException
+	 * @exception InvalidSiteTypeException
+	 * @since 2.0
+	 */
+	public SiteModel parseSite(InputStream stream)
+		throws CoreException, InvalidSiteTypeException {
+		SiteModel result = null;
+		try {
+			parser.init(this);
+			result = parser.parse(stream);
+			if (parser.getStatus()!=null) {
+				// some internalError were detected
+				IStatus status = parser.getStatus();
+				throw new CoreException(status);
+			}
+		} catch (SAXException e) {
+			// invalid Site type
+			if (e.getException() instanceof InvalidSiteTypeException) {
+				throw (InvalidSiteTypeException) e.getException();
+			}
+
+			throw Utilities.newCoreException(Messages.SiteModelObject_ErrorParsingSiteStream,e); 
+		} catch (IOException e){
+			throw Utilities.newCoreException(Messages.SiteModelObject_ErrorAccessingSiteStream,e); 
+		}
+		return result;
+	}
+
+	/**
+	 * Create a default site model.
+	 * 
+	 * @see SiteModel
+	 * @return site model
+	 * @since 2.0
+	 */
+	public SiteModel createSiteMapModel() {
+		return new SiteModel();
+	}
+
+	/**
+	 * Create a default site feature reference model.
+	 * 
+	 * @see SiteFeatureReferenceModel
+	 * @return site feature reference model
+	 * @since 2.0
+	 */
+	public SiteFeatureReferenceModel createFeatureReferenceModel() {
+		return new SiteFeatureReferenceModel();
+	}
+
+	/**
+	 * Create a default archive reference model.
+	 * 
+	 * @see ArchiveReferenceModel
+	 * @return archive reference model
+	 * @since 2.0
+	 */
+	public ArchiveReferenceModel createArchiveReferenceModel() {
+		return new ArchiveReferenceModel();
+	}
+
+	/**
+	 * Create a default annotated URL model.
+	 * 
+	 * @see URLEntryModel
+	 * @return annotated URL model
+	 * @since 2.0
+	 */
+	public URLEntryModel createURLEntryModel() {
+		return new URLEntryModel();
+	}
+
+	/**
+	 * Create a default category model.
+	 * 
+	 * @see CategoryModel
+	 * @return category model
+	 * @since 2.0
+	 */
+	public CategoryModel createSiteCategoryModel() {
+		return new CategoryModel();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/URLEntryModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/URLEntryModel.java
new file mode 100644
index 0000000..2d4f866
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/URLEntryModel.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.core.model;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.update.core.IURLEntry;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * Annotated URL model object.
+ * <p>
+ * This class may be instantiated or subclassed by clients. However, in most 
+ * cases clients should instead instantiate or subclass the provided 
+ * concrete implementation of this model.
+ * </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>
+ * @see org.eclipse.update.core.URLEntry
+ * @since 2.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class URLEntryModel extends ModelObject {
+	
+	private String annotation;
+	private String localizedAnnotation;
+	private String urlString;
+	private URL url;
+	
+	private int type = IURLEntry.UPDATE_SITE;
+	
+	//performance
+	private URL bundleURL;
+	private URL base;
+	private boolean resolved=false;
+	
+	/**
+	 * Creates a uninitialized annotated URL model object.
+	 * 
+	 * @since 2.0
+	 */
+	public URLEntryModel() {
+		super();
+	}
+		
+	/**
+	 * Returns the url annotation. If the model object has been resolved, 
+	 * the annotation is localized.
+	 * 
+	 * @return url annotation, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getAnnotation() {
+		delayedResolve();
+		if (localizedAnnotation != null)
+			return localizedAnnotation;
+		else
+			return annotation;
+	}
+		
+	/**
+	 * returns the non-localized url annotation.
+	 * 
+	 * @return non-localized url annotation, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public String getAnnotationNonLocalized() {
+		return annotation;
+	}
+
+	/**
+	 * Returns the unresolved url string.
+	 *
+	 * @return url string, or <code>null</code>
+	 * @since 2.0
+	 */
+	public String getURLString() {
+		delayedResolve();
+		return urlString;
+	}
+	
+	/**
+	 * Returns the resolved URL.
+	 * 
+	 * @return url, or <code>null</code>
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		delayedResolve();
+		return url;
+	}
+	
+	/**
+	 * Sets the annotation.
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param annotation annotation
+	 * @since 2.0
+	 */	
+	public void setAnnotation(String annotation) {
+		assertIsWriteable();
+		this.annotation = annotation;
+		this.localizedAnnotation = null;
+	}
+	
+	/**
+	 * Sets the url string
+	 * Throws a runtime exception if this object is marked read-only.
+	 *
+	 * @param urlString url string
+	 * @since 2.0
+	 */	
+	public void setURLString(String urlString) {
+		assertIsWriteable();
+		this.urlString = urlString;
+		this.url = null;
+	}
+	
+	/**
+	 * Resolve the model object.
+	 * Any URL strings in the model are resolved relative to the 
+	 * base URL argument. Any translatable strings in the model that are
+	 * specified as translation keys are localized using the supplied 
+	 * resource bundle.
+	 * 
+	 * @param base URL
+	 * @param bundleURL  resource bundle url
+	 * @exception MalformedURLException
+	 * @since 2.0
+	 */
+	public void resolve(URL base, URL bundleURL) throws MalformedURLException {
+		this.base = base;
+		this.bundleURL = bundleURL;
+	}
+
+
+	private void delayedResolve() {
+		
+		//PERF: delay resolution
+		if (resolved)return;
+		
+		resolved= true;
+		// resolve local elements
+		localizedAnnotation = resolveNLString(bundleURL, annotation);
+		try {
+			url = resolveURL(base,bundleURL, urlString);
+		} catch (MalformedURLException e){
+			UpdateCore.warn("",e); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Returns the specified type.
+	 * 
+	 * @since 2.1
+	 */
+	public int getType() {
+		return type;
+	}
+
+	/**
+	 * Method setType.
+	 * @param i
+	 */
+	public void setType(int i) {
+		type = i;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/model/package.html b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/package.html
new file mode 100644
index 0000000..03886d3
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/model/package.html
@@ -0,0 +1,27 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.72 [en] (Windows NT 5.0; U) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides model support for extending the base installation
+and update services.
+<h2>
+Package Specification</h2>
+This package provides a convenience implementation of the default model
+objects and parsers used for writing pluggable support of alternate feature
+packaging schemes and update site access mechanisms. In general, most developers
+do not need to directly use the classes defined in this package. Instead,
+they can use the corresponding derived implementation classes contained
+in package <b>org.eclipse.update.core</b>.
+<p>Classes in this package would typically only be used directly when writing
+and alternate set of model objects for a feature and type implementation.
+<p>
+<b>Note:</b> This package has been deprecated and will be deleted in a future
+release.  See bug 311590 for details.
+</p>
+</body>
+</html>
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/core/package.html b/update/org.eclipse.update.core/src/org/eclipse/update/core/package.html
new file mode 100644
index 0000000..693e7b1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/core/package.html
@@ -0,0 +1,29 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.72 [en] (Windows NT 5.0; U) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides support for extending the base installation
+and update services.
+<h2>
+Package Specification</h2>
+This package specifies the API required for writing pluggable support of
+alternate feature packaging schemes and update site access mechanisms.
+For the most part, the classes and interfaces in this package implement
+a code pattern <tt>class Foo extends FooModel implements IFoo</tt>. The
+interface (<tt>IFoo</tt> in the pattern) defines the actual API. The convenience
+classes (<tt>Foo</tt> in the pattern) provide a reference implementation
+of the API based on a model (<tt>IFooModel</tt> in the pattern) contained
+in package <b>org.eclipse.update.core.model</b>. In general, developers
+should extend the convenience classes rather than completely reimplementing
+the specified interfaces.
+<p>
+<b>Note:</b> This package has been deprecated and will be deleted in a future
+release.  See bug 311590 for details.
+</p>
+</body>
+</html>
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/BaseSiteLocalFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/BaseSiteLocalFactory.java
new file mode 100644
index 0000000..2ab5714
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/BaseSiteLocalFactory.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.model.*;
+/**
+ * 
+ */
+public class BaseSiteLocalFactory {
+	/*
+	 * 
+	 */
+	public InstallConfigurationModel createInstallConfigurationModel() {
+		return new InstallConfiguration();
+	}
+	/*
+	 * 
+	 */
+	public ConfigurationActivityModel createConfigurationActivityModel() {
+		return new ConfigurationActivity();
+	}
+	/*
+	 * 
+	 */
+	public ConfiguredSiteModel createConfigurationSiteModel() {
+		return new ConfiguredSite();
+	}
+	/*
+	 * 
+	 */
+	public ConfigurationPolicyModel createConfigurationPolicyModel() {
+		return new ConfigurationPolicy();
+	}
+	/**
+	 * 
+	 */
+	public ConfiguredSiteModel createConfigurationSiteModel(SiteModel site, int policy) {
+		//create config site
+		ConfiguredSiteModel configSite = this.createConfigurationSiteModel();
+		configSite.setSiteModel(site);
+		ConfigurationPolicyModel policyModel = this.createConfigurationPolicyModel();
+		policyModel.setPolicy(policy);
+		configSite.setConfigurationPolicyModel(policyModel);
+		((ConfigurationPolicy) policyModel).setConfiguredSiteModel(configSite);
+		return configSite;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfigurationActivity.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfigurationActivity.java
new file mode 100644
index 0000000..d6bbc84
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfigurationActivity.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+import org.eclipse.update.configuration.IActivity;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.internal.model.ConfigurationActivityModel;
+public class ConfigurationActivity
+	extends ConfigurationActivityModel
+	implements IActivity {
+		
+	/**
+	 * Default constructor
+	 */		
+	public ConfigurationActivity() {
+	}
+	
+	/**
+	 * Constructor with action
+	 */
+	public ConfigurationActivity(int action) {
+		super();
+		setAction(action);
+		setStatus(STATUS_NOK);
+	}
+	
+	/*
+	 * @see IActivity#getInstallConfiguration()
+	 */
+	public IInstallConfiguration getInstallConfiguration() {
+		return (IInstallConfiguration) getInstallConfigurationModel();
+	}
+	
+	public boolean equals(Object other) {
+		if (!(other instanceof ConfigurationActivity))
+			return false;
+		if (this == other)
+			return true;
+		
+		ConfigurationActivity activity = (ConfigurationActivity) other;
+		return getAction() == activity.getAction()
+				&& getLabel().equals(activity.getLabel())
+				&& getStatus() == activity.getStatus();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfigurationPolicy.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfigurationPolicy.java
new file mode 100644
index 0000000..5d7e482
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfigurationPolicy.java
@@ -0,0 +1,524 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+import java.io.*;
+import java.net.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.configurator.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.model.*;
+
+/**
+ * 
+ */
+public class ConfigurationPolicy extends ConfigurationPolicyModel {
+
+	/**
+	 * Constructor for ConfigurationPolicyModel.
+	 */
+	public ConfigurationPolicy() {
+	}
+
+	/**
+	 * Copy Constructor for ConfigurationPolicyModel.
+	 */
+	public ConfigurationPolicy(ConfigurationPolicy configPolicy) {
+		super();
+		setPolicy(configPolicy.getPolicy());
+		setConfiguredFeatureReferences(configPolicy.getConfiguredFeatures());
+		setUnconfiguredFeatureReferences(configPolicy.getUnconfiguredFeatures());
+		setConfiguredSiteModel(configPolicy.getConfiguredSiteModel());
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	private boolean isUnconfigured(IFeatureReference featureReference) {
+
+		if (featureReference == null)
+			return false;
+
+		// returns true if the feature is part of the configured list
+		IFeatureReference[] refs = getUnconfiguredFeatures();
+		for (int i = 0; i < refs.length; i++) {
+			if (featureReference.equals(refs[i])) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public boolean isConfigured(IFeatureReference featureReference) {
+
+		if (featureReference == null)
+			return false;
+
+		// returns true if the feature is part of the configured list
+		IFeatureReference[] refs = getConfiguredFeatures();
+		for (int i = 0; i < refs.length; i++) {
+			if (featureReference.equals(refs[i])) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * adds the feature to the list of features if the policy is USER_INCLUDE
+	 */
+	public void configure(IFeatureReference featureReference, boolean callInstallHandler, boolean createActivity) throws CoreException {
+
+		if (isConfigured(featureReference)) // already configured
+			return;
+
+		if (featureReference == null) {
+			UpdateCore.warn("The feature reference to configure is null"); //$NON-NLS-1$
+			return;
+		}
+
+		IFeature feature = null;
+		try {
+			feature = featureReference.getFeature(null);
+		} catch (CoreException e) {
+			if (!UpdateManagerUtils.isOptional(featureReference)) {
+				URL url = featureReference.getURL();
+				String urlString = (url != null) ? url.toExternalForm() : "<no feature reference url>"; //$NON-NLS-1$
+				UpdateCore.warn("Error retrieving feature:" + urlString, e); //$NON-NLS-1$
+				return;
+			}
+		}
+		if (feature == null) {
+			URL url = featureReference.getURL();
+			String urlString = (url != null) ? url.toExternalForm() : "<no feature reference url>"; //$NON-NLS-1$
+			UpdateCore.warn("The feature to unconfigure is null: feature reference is:" + urlString); //$NON-NLS-1$
+		}
+
+		// Setup optional install handler
+		InstallHandlerProxy handler = null;
+		if (callInstallHandler && feature.getInstallHandlerEntry() != null)
+			handler = new InstallHandlerProxy(IInstallHandler.HANDLER_ACTION_CONFIGURE, feature, feature.getInstallHandlerEntry(), null);
+		boolean success = false;
+		Throwable originalException = null;
+
+		// do the configure action
+		try {
+			if (handler != null)
+				handler.configureInitiated();
+
+			ConfigurationActivity activity = null;
+			if (createActivity) {
+				activity = new ConfigurationActivity(IActivity.ACTION_CONFIGURE);
+				activity.setLabel(feature.getVersionedIdentifier().toString());
+				activity.setDate(new Date());
+			}
+
+			addConfiguredFeatureReference((FeatureReferenceModel) featureReference);
+
+			// everything done ok
+			if (activity != null) {
+				InstallConfiguration installConfig = (InstallConfiguration) SiteManager.getLocalSite().getCurrentConfiguration();
+				activity.setStatus(IActivity.STATUS_OK);
+				installConfig.addActivity(activity);
+			}
+
+			if (handler != null)
+				handler.completeConfigure();
+
+			success = true;
+		} catch (Throwable t) {
+			originalException = t;
+		} finally {
+			Throwable newException = null;
+			try {
+				if (handler != null)
+					handler.configureCompleted(success);
+			} catch (Throwable t) {
+				newException = t;
+			}
+			if (originalException != null) // original exception wins
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallHandler_error, (new String[] { feature.getLabel() })), originalException);
+			if (newException != null)
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallHandler_error, (new String[] { feature.getLabel() })), newException);
+		}
+	}
+
+	/**
+	 * check if the plugins to unconfigure are required by other configured feature and
+	 * adds the feature to the list of unconfigured features 
+	 */
+	public boolean unconfigure(IFeatureReference featureReference, boolean callInstallHandler, boolean createActivity) throws CoreException {
+
+		if (isUnconfigured(featureReference)) {
+			UpdateCore.warn("Feature already unconfigured"); //$NON-NLS-1$
+			return true;
+		}
+
+		if (featureReference == null) {
+			UpdateCore.warn("The feature reference to unconfigure is null"); //$NON-NLS-1$
+			return false;
+		}
+
+		IFeature feature = null;
+		try {
+			feature = featureReference.getFeature(null);
+		} catch (CoreException e) {
+			if (!UpdateManagerUtils.isOptional(featureReference)) {
+				URL url = featureReference.getURL();
+				String urlString = (url != null) ? url.toExternalForm() : "<no feature reference url>"; //$NON-NLS-1$
+				UpdateCore.warn("Error retrieving feature:" + urlString, e); //$NON-NLS-1$
+				return false;
+			}
+		}
+
+		if (feature == null) {
+			URL url = featureReference.getURL();
+			String urlString = (url != null) ? url.toExternalForm() : "<no feature reference url>"; //$NON-NLS-1$
+			UpdateCore.warn("The feature to unconfigure is null: feature reference is:" + urlString); //$NON-NLS-1$
+			return false;
+		}
+
+		// Setup optional install handler
+		InstallHandlerProxy handler = null;
+		if (callInstallHandler && feature.getInstallHandlerEntry() != null) {
+			handler = new InstallHandlerProxy(IInstallHandler.HANDLER_ACTION_UNCONFIGURE, feature, feature.getInstallHandlerEntry(), null);
+		}
+
+		boolean success = false;
+		Throwable originalException = null;
+
+		// do the unconfigure action
+		try {
+
+			ConfigurationActivity activity = null;
+			if (createActivity) {
+				activity = new ConfigurationActivity(IActivity.ACTION_UNCONFIGURE);
+				activity.setLabel(feature.getVersionedIdentifier().toString());
+				activity.setDate(new Date());
+			}
+
+			InstallConfiguration installConfig = null;
+
+			// only ask for install config is activity created.
+			// prevents loops during reconciliation
+			if (activity != null)
+				installConfig = ((InstallConfiguration) SiteManager.getLocalSite().getCurrentConfiguration());
+
+			// Allow unconfigure if the feature is optional from all the parents
+			// or if the feature is mandatory and non of its parent are configured
+			// removed, not a core issue (so deep down)
+			//if (validateNoConfiguredParents(feature)) {
+			if (handler != null)
+				handler.unconfigureInitiated();
+			addUnconfiguredFeatureReference((FeatureReferenceModel) featureReference);
+			if (handler != null)
+				handler.completeUnconfigure();
+
+			// everything done ok
+			if (activity != null) {
+				activity.setStatus(IActivity.STATUS_OK);
+				installConfig.addActivity(activity);
+			}
+			success = true;
+			//} else {
+			//	if (activity != null) {
+			//		activity.setStatus(IActivity.STATUS_NOK);
+			//		installConfig.addActivityModel((ConfigurationActivityModel) activity);
+			//	}
+			//}
+		} catch (Throwable t) {
+			originalException = t;
+		} finally {
+			Throwable newException = null;
+			try {
+				if (handler != null)
+					handler.unconfigureCompleted(success);
+			} catch (Throwable t) {
+				newException = t;
+			}
+			if (originalException != null) // original exception wins
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallHandler_error, (new String[] { feature.getLabel() })), originalException);
+			if (newException != null)
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallHandler_error, (new String[] { feature.getLabel() })), newException);
+		}
+
+		if (!success) {
+			URL url = featureReference.getURL();
+			String urlString = (url != null) ? url.toExternalForm() : "<no feature reference url>"; //$NON-NLS-1$
+			UpdateCore.warn("Unable to unconfigure:" + urlString); //$NON-NLS-1$
+		}
+		return success;
+	}
+
+	/**
+	 * Calculates the plugin list for the policy. For "INCLUDE" policy, this
+	 * corresponds to the plugins for configured features. For "EXCLUDE"
+	 * policy, this corresponds to the plugins for unconfigured features that
+	 * are not referenced by any configured features.
+	 */
+	public String[] getPluginPath(ISite site) throws CoreException {
+		// TODO we may need to exclude patched plugins here, but this should be good enough for now
+		if (getPolicy() == IPlatformConfiguration.ISitePolicy.MANAGED_ONLY)
+			return new String[0];
+			
+		String[] pluginPaths;
+		// Note: Since 3.0M7 we leave patched features configured,
+		// and take this into account when computing configured plugins
+		// all unconfigured features. Note that patched features are still
+		// configured
+		IFeatureReference[] unconfiguredFeatures = getUnconfiguredFeatures();
+		// all configured features, including patches and patched features
+		IFeatureReference[] configuredFeatures = getConfiguredFeatures();
+		if (!isEnabled()) {
+			if (getPolicy() == IPlatformConfiguration.ISitePolicy.USER_INCLUDE) {
+				// disabled site, INCLUDE policy
+				pluginPaths = new String[0];
+			} else {
+				// disabled site, EXCLUDE policy
+				pluginPaths = getAllKnownPluginStrings(site,
+						configuredFeatures, unconfiguredFeatures);
+			}
+		} else {
+			// PatchedFeatures (may have no patches) with corresponding patches
+			PatchedFeature[] patchedFeatures = buildPatchedFeatures(configuredFeatures);
+			if (getPolicy() == IPlatformConfiguration.ISitePolicy.USER_INCLUDE) {
+				// enabled site, INCLUDE policy
+				pluginPaths = getConfiguredPluginStrings(site, patchedFeatures);
+			} else {
+				// enabled site, EXCLUDE policy - the usual scenario for local
+				// site.
+				// return all known MINUS configured plugins
+				pluginPaths = subtract(getAllKnownPluginStrings(site,
+						configuredFeatures, unconfiguredFeatures),
+						getConfiguredPluginStrings(site, patchedFeatures));
+			}
+		}
+		//TRACE
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+			UpdateCore
+					.debug("GetPluginPath for: " //$NON-NLS-1$
+							+ ((site == null) ? "<No site>" : site.getURL() //$NON-NLS-1$
+									.toString()));
+			for (int i = 0; i < pluginPaths.length; i++) {
+				UpdateCore.debug("To write:" + pluginPaths[i]); //$NON-NLS-1$
+			}
+		}
+		return pluginPaths;
+	}
+	
+	/**
+	 * Obtains PatchedFeatures - non patch features with corresponding patches if any
+	 * 
+	 * @param features
+	 *            array of features to operate with
+	 * @return Patches
+	 */
+	private PatchedFeature[] buildPatchedFeatures(IFeatureReference[] features) {
+		// PatchedFeatures by VersionedIdentifier
+		Map map = new HashMap();
+		// Create a map of features (not patches)
+		for (int f = 0; f < features.length; f++) {
+			IFeatureReference featureRef = features[f];
+			try {
+				if(featureRef.isPatch()){
+					continue;
+				}
+				VersionedIdentifier vi = featureRef.getVersionedIdentifier();
+				map.put(vi, new PatchedFeature(features[f]));
+			} catch (CoreException e) {
+				UpdateCore.warn(null, e);
+			}
+		}
+		// attach patches to features
+		for (int f = 0; f < features.length; f++) {
+			IFeatureReference patchCandidate = features[f];
+			try {
+				IFeature feature = patchCandidate.getFeature(null);
+				IImport[] imports = feature.getImports();
+				for (int i = 0; i < imports.length; i++) {
+					IImport oneImport = imports[i];
+					if (!oneImport.isPatch())
+						continue;
+					// it is a patch for
+					VersionedIdentifier patchedIdentifier =
+						oneImport.getVersionedIdentifier();
+					PatchedFeature pf=(PatchedFeature) map.get(patchedIdentifier);
+					if (pf!=null) {
+						pf.addPatch(patchCandidate);
+					} else {
+						// patched feature not enabled
+					}
+				}
+			} catch (CoreException e) {
+				UpdateCore.warn(null, e);
+			}
+		}
+		Collection patchedFeatures=map.values();
+		return (PatchedFeature[])patchedFeatures.toArray(new PatchedFeature[patchedFeatures.size()]);
+	}
+	
+	/**
+	 * @since 2.0
+	 */
+	public IFeatureReference[] getConfiguredFeatures() {
+		FeatureReferenceModel[] result = getConfiguredFeaturesModel();
+		if (result.length == 0)
+			return new IFeatureReference[0];
+		else
+			return (IFeatureReference[]) result;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public IFeatureReference[] getUnconfiguredFeatures() {
+		FeatureReferenceModel[] result = getUnconfiguredFeaturesModel();
+		if (result.length == 0)
+			return new IFeatureReference[0];
+		else
+			return (IFeatureReference[]) result;
+	}
+
+	/**
+	 * Gets the configuredSite.
+	 * @return Returns a IConfiguredSite
+	 */
+	public IConfiguredSite getConfiguredSite() {
+		return (IConfiguredSite) getConfiguredSiteModel();
+	}
+
+	/**
+	 * removes a feature reference
+	 */
+	public void removeFeatureReference(IFeatureReference featureRef) {
+		if (featureRef instanceof FeatureReferenceModel) {
+			removeFeatureReference((FeatureReferenceModel) featureRef);
+		}
+	}
+
+	/**
+	 * @return an array of plugin path for the array of feature reference. For
+	 *         features that have patches, plugin path will
+	 *         point to plugin with the same ID provided by the patch if it
+	 *         exists. Each plugin path only appears once [bug 21750]
+	 */
+	private String[] getConfiguredPluginStrings(ISite site, PatchedFeature[] features) throws CoreException {
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION){
+			UpdateCore.warn("CONFIGURED PLUGINS"); //$NON-NLS-1$
+		}
+	
+		// Use set to eliminate plugins with same ID and version.
+		// Different versions of plugins with same ID are allowed if coming from different features
+		Set featurePlugins = new HashSet();
+		for (int i = 0; i < features.length; i++) {
+			FeaturePlugin[] plugins = features[i].getPlugins();
+			featurePlugins.addAll(Arrays.asList(plugins));
+		}
+		Set pluginStrings = getPluginStrings(site, (FeaturePlugin[]) featurePlugins.toArray(new FeaturePlugin[featurePlugins.size()]));
+		return (String[]) pluginStrings.toArray(new String[pluginStrings.size()]);
+	}
+	/**
+	 * @return an array of plugin path for every plugin in known features
+	 */
+	private String[] getAllKnownPluginStrings(ISite site, IFeatureReference[] configured,IFeatureReference[] unconfigured) throws CoreException {
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION){
+			UpdateCore.warn("ALL PLUGINS"); //$NON-NLS-1$
+		}
+		// Add features, patched features, or patches
+		IFeatureReference[] all=new IFeatureReference[configured.length+unconfigured.length];
+		System.arraycopy(configured, 0, all, 0, configured.length);
+		System.arraycopy(unconfigured, 0, all, configured.length, unconfigured.length);
+		//
+		Set patchedPlugins = new HashSet();
+		for (int i=0; i< all.length; i++) {
+			try {
+				IFeature feature = all[i].getFeature(null);
+				if (feature == null) {
+					UpdateCore.warn("Null Feature", new Exception()); //$NON-NLS-1$
+					continue;
+				}
+
+				IPluginEntry[] entries = feature.getPluginEntries();
+				// add every plugin to the map
+				for (int entr = 0; entr < entries.length; entr++) {
+					patchedPlugins.add(new FeaturePlugin(entries[entr], feature));
+				}
+
+			} catch (CoreException e) {
+				UpdateCore.warn(null, e);
+			}
+		}
+		Set pluginStrings = getPluginStrings(site,  (FeaturePlugin[])patchedPlugins.toArray(new FeaturePlugin[patchedPlugins.size()]));
+		return (String[]) pluginStrings.toArray(new String[pluginStrings.size()]);
+	}
+	/**
+	 * @param site
+	 * @param plugins[]
+	 * @return valid string pointing to plugins in given features
+	 * @throws CoreException
+	 */
+	private Set getPluginStrings(ISite site, FeaturePlugin[] plugins) throws CoreException {
+		Set pluginStrings=new HashSet();
+		for (int i=0; i< plugins.length; i++) {
+			IPluginEntry entry = plugins[i].getEntry();
+			IFeature feature=plugins[i].getFeature();
+
+			// obtain the path of the plugin directories on the site
+			ContentReference[] featureContentReference = null;
+			try {
+				featureContentReference = feature.getFeatureContentProvider().getPluginEntryArchiveReferences(entry, null /*IProgressMonitor*/
+				);
+			} catch (CoreException e) {
+				UpdateCore.warn(null, e);
+			}
+
+			// transform into a valid String
+			if (featureContentReference != null) {
+				for (int j = 0; j < featureContentReference.length; j++) {
+					URL url = site.getSiteContentProvider().getArchiveReference(featureContentReference[j].getIdentifier());
+					if (url != null) {
+						// make it relative to the site
+						String path = UpdateManagerUtils.getURLAsString(site.getURL(), url);
+						// add end "/"
+						if(!path.endsWith(".jar")) //$NON-NLS-1$
+							path += (path.endsWith(File.separator) || path.endsWith("/")) ? "" : "/"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+						pluginStrings.add(path);
+						if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+							UpdateCore.warn("Add plugin: " + path + " to the list"); //$NON-NLS-1$ //$NON-NLS-2$
+					}
+				}
+			}
+		}
+		return pluginStrings;
+	}
+
+	/**
+	 *	 Obtains strings existing in the allStrings array, but not in the stringsToRemove
+	 */
+	private String[] subtract(String[] allStrings, String[] stringsToRemove) {
+		HashSet resultList = new HashSet(Arrays.asList(allStrings));
+		resultList.removeAll(Arrays.asList(stringsToRemove));
+		return (String[])resultList.toArray(new String[resultList.size()]);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfiguredSite.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfiguredSite.java
new file mode 100644
index 0000000..3b59270
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ConfiguredSite.java
@@ -0,0 +1,1136 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.update.internal.core;
+
+
+import org.eclipse.core.runtime.ListenerList;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.PropertyResourceBundle;
+import java.util.Set;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.IActivity;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.configuration.IConfiguredSiteChangedListener;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.configuration.IProblemHandler;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.IIncludedFeatureReference;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFeatureReference;
+import org.eclipse.update.core.IVerificationListener;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.core.model.InstallAbortedException;
+import org.eclipse.update.internal.model.ConfiguredSiteModel;
+import org.eclipse.update.internal.operations.UpdateUtils;
+
+/**
+ * A Configured site manages the Configured and unconfigured features of a Site
+ */
+public class ConfiguredSite extends ConfiguredSiteModel implements IConfiguredSite {
+
+	private static final String PRODUCT_SITE_MARKER = ".eclipseproduct"; //$NON-NLS-1$
+	private static final String EXTENSION_SITE_MARKER = ".eclipseextension"; //$NON-NLS-1$
+
+	// listeners	
+	private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+
+	// verification status
+	private IStatus verifyStatus;
+
+	// transient: true if the site was just created so we can remove it
+	private transient boolean justCreated = false;
+
+	/*
+	 * Default Constructor
+	 */
+	public ConfiguredSite() {
+	}
+
+	/*
+	 * Copy Constructor
+	 * As of now, configSite can only be of type ConfiguredSite
+	 */
+	public ConfiguredSite(IConfiguredSite configSite) {
+		ConfiguredSite cSite = (ConfiguredSite) configSite;
+		setSiteModel(cSite.getSiteModel());
+		setConfigurationPolicyModel(new ConfigurationPolicy(cSite.getConfigurationPolicy()));
+		setUpdatable(cSite.isUpdatable());
+		setEnabled(cSite.isEnabled());
+		setPreviousPluginPath(cSite.getPreviousPluginPath());
+		setPlatformURLString(cSite.getPlatformURLString());
+	}
+
+	/*
+	 *  Adds a listener
+	 */
+	public void addConfiguredSiteChangedListener(IConfiguredSiteChangedListener listener) {
+		listeners.add(listener);
+	}
+
+	/*
+	 * Removes a listener
+	 */
+	public void removeConfiguredSiteChangedListener(IConfiguredSiteChangedListener listener) {
+		listeners.remove(listener);
+	}
+
+	/*
+	 * @see IConfiguredSite#install(IFeature,IVerificationListener, IProgressMonitor)
+	 */
+	public IFeatureReference install(IFeature feature, IVerificationListener verificationListener, IProgressMonitor monitor) throws InstallAbortedException, CoreException {
+		return install(feature, null, verificationListener, monitor);
+	}
+
+	/*
+	 * @see IConfiguredSite#install(IFeature, IFeatureReference, IVerificationListener, IProgressMonitor)
+	 */
+	public IFeatureReference install(IFeature feature, IFeatureReference[] optionalFeatures, IVerificationListener verificationListener, IProgressMonitor monitor) throws InstallAbortedException, CoreException {
+
+		// change the status if justCreated
+		if (justCreated) justCreated=false;
+
+		// ConfigSite is read only 
+		if (!isUpdatable()) {
+			String errorMessage = NLS.bind(Messages.ConfiguredSite_NonInstallableSite, (new String[] { getSite().getURL().toExternalForm() }));
+			IStatus status = verifyUpdatableStatus();
+			if (status != null)
+				errorMessage += " " + status.getMessage(); //$NON-NLS-1$
+			throw Utilities.newCoreException(errorMessage, null);
+		}
+
+		// feature is null
+		if (feature == null) {
+			String errorMessage = Messages.ConfiguredSite_NullFeatureToInstall; 
+			throw Utilities.newCoreException(errorMessage, null);
+		}
+
+		// feature reference to return
+		IFeatureReference installedFeatureRef;
+		IFeature installedFeature = null;
+
+		// create the Activity (INSTALL)
+		ConfigurationActivity activity = new ConfigurationActivity(IActivity.ACTION_FEATURE_INSTALL);
+		activity.setLabel(feature.getVersionedIdentifier().toString());
+		activity.setDate(new Date());
+
+		try {
+			installedFeatureRef = getSite().install(feature, optionalFeatures, verificationListener, monitor);
+
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL) {
+				UpdateCore.debug("Sucessfully installed: " + installedFeatureRef.getURL().toExternalForm()); //$NON-NLS-1$
+			}
+
+			if (installedFeatureRef != null) {
+				try {
+					installedFeature = installedFeatureRef.getFeature(null);
+				} catch (CoreException e) {
+					UpdateCore.warn(null, e);
+				}
+			}
+
+			// everything done ok
+			activity.setStatus(IActivity.STATUS_OK);
+
+			// notify listeners
+			Object[] siteListeners = listeners.getListeners();
+			for (int i = 0; i < siteListeners.length; i++) {
+				if (installedFeature != null) {
+					IConfiguredSiteChangedListener listener = ((IConfiguredSiteChangedListener) siteListeners[i]);
+					listener.featureInstalled(installedFeature);
+				}
+			}
+		} catch (CoreException e) {
+			// not ok, set Activity status
+			activity.setStatus(IActivity.STATUS_NOK);
+			throw e;
+		} finally {
+			IInstallConfiguration current = SiteManager.getLocalSite().getCurrentConfiguration();
+			((InstallConfiguration) current).addActivity(activity);
+		}
+		// call the configure task	
+		if (installedFeature != null)
+			configure(installedFeature, optionalFeatures, true);
+		/*callInstallHandler*/
+
+		return installedFeatureRef;
+	}
+
+	/*
+	 * @see IConfiguredSite#remove(IFeature, IProgressMonitor)
+	 */
+	public void remove(IFeature feature, IProgressMonitor monitor) throws CoreException {
+
+		// ConfigSite is read only
+		if (!isUpdatable()) {
+			String errorMessage = NLS.bind(Messages.ConfiguredSite_NonUninstallableSite, (new String[] { getSite().getURL().toExternalForm() }));
+			throw Utilities.newCoreException(errorMessage, null);
+		}
+
+		// create the Activity
+		ConfigurationActivity activity = new ConfigurationActivity(IActivity.ACTION_FEATURE_REMOVE);
+		activity.setLabel(feature.getVersionedIdentifier().toString());
+		activity.setDate(new Date());
+
+		try {
+			IFeatureReference referenceToRemove = null;
+			ISiteFeatureReference[] featureRef = getSite().getFeatureReferences();
+			ISiteFeatureReference ref = getSite().getFeatureReference(feature);
+			for (int i = 0; i < featureRef.length; i++) {
+				if (featureRef[i].equals(ref)) {
+					referenceToRemove = featureRef[i];
+					break;
+				}
+			}
+
+			// we found a feature reference on the site matching the feature			
+			if (referenceToRemove != null) {
+				// Check if feature is unconfigured before we remove it
+				// our UI will check.
+				// For non-UI application, throw error is feature is configured
+				if (getConfigurationPolicy().isConfigured(referenceToRemove)) {
+					IFeature featureToRemove = referenceToRemove.getFeature(null);
+					String featureLabel = (featureToRemove == null) ? null : featureToRemove.getLabel();
+					throw Utilities.newCoreException(NLS.bind(Messages.ConfiguredSite_UnableToRemoveConfiguredFeature, (new String[] { featureLabel })), null);
+				}
+			} else {
+				throw Utilities.newCoreException(NLS.bind(Messages.ConfiguredSite_UnableToFindFeature, (new String[] { feature.getURL().toString() })),
+				null);
+			}
+
+			// remove the feature
+			getSite().remove(feature, monitor);
+			getConfigurationPolicy().removeFeatureReference(referenceToRemove);
+			// everything done ok
+			activity.setStatus(IActivity.STATUS_OK);
+			// notify listeners
+			Object[] siteListeners = listeners.getListeners();
+			for (int i = 0; i < siteListeners.length; i++) {
+				((IConfiguredSiteChangedListener) siteListeners[i]).featureRemoved(feature);
+			}
+		} catch (CoreException e) {
+			activity.setStatus(IActivity.STATUS_NOK);
+			throw e;
+		} finally {
+			IInstallConfiguration current = SiteManager.getLocalSite().getCurrentConfiguration();
+			((InstallConfiguration) current).addActivity(activity);
+		}
+	}
+
+	/*
+	 * @see IConfiguredSite#configure(IFeature) 
+	 */
+	public void configure(IFeature feature) throws CoreException {
+		configure(feature, null, true /*callInstallHandler*/
+		);
+	}
+
+	/*
+	 * 
+	 */
+	private void configure(IFeature feature, IFeatureReference[] optionalFeatures, boolean callInstallHandler) throws CoreException {
+
+		if (feature == null) {
+			UpdateCore.warn("Attempting to configure a null feature in site:" + getSite().getURL().toExternalForm()); //$NON-NLS-1$
+			return;
+		}
+
+		ConfigurationPolicy configPolicy = getConfigurationPolicy();
+		if (configPolicy == null)
+			return;
+
+		// bottom up approach, same configuredSite
+		IIncludedFeatureReference[] childrenRef = feature.getIncludedFeatureReferences();
+		if (optionalFeatures != null) {
+			childrenRef = childrenToConfigure(childrenRef, optionalFeatures);
+		}
+
+		for (int i = 0; i < childrenRef.length; i++) {
+			try {
+				IFeature child = childrenRef[i].getFeature(null);
+				configure(child, optionalFeatures, callInstallHandler);
+			} catch (CoreException e) {
+				// if not an optional feature, throw exception
+				if (!childrenRef[i].isOptional()) {
+					UpdateCore.warn("Unable to configure child feature: " + childrenRef[i] + " " + e); //$NON-NLS-1$ //$NON-NLS-2$
+					throw e;
+				}
+			}
+		}
+
+		// configure root feature 	
+		IFeatureReference featureReference = getSite().getFeatureReference(feature);
+		configPolicy.configure(featureReference, callInstallHandler, true);
+
+		// notify listeners
+		Object[] siteListeners = listeners.getListeners();
+		for (int i = 0; i < siteListeners.length; i++) {
+			((IConfiguredSiteChangedListener) siteListeners[i]).featureConfigured(feature);
+		}
+	}
+
+	/*
+	 * Return the optional children to configure
+	 * 
+	 * @param children all the nested features
+	 * @param optionalfeatures optional features to install
+	 * @return IFeatureReference[]
+	 */
+	private IIncludedFeatureReference[] childrenToConfigure(IIncludedFeatureReference[] children, IFeatureReference[] optionalfeatures) {
+
+		List childrenToInstall = new ArrayList();
+		for (int i = 0; i < children.length; i++) {
+			IIncludedFeatureReference optionalFeatureToConfigure = children[i];
+			if (!optionalFeatureToConfigure.isOptional()) {
+				childrenToInstall.add(optionalFeatureToConfigure);
+			} else {
+				for (int j = 0; j < optionalfeatures.length; j++) {
+					// must compare feature as optionalFeatures are from the install site
+					// where children are on the local site
+					try {
+						IFeature installedChildren = optionalfeatures[j].getFeature(null);
+						if (installedChildren.equals(optionalFeatureToConfigure.getFeature(null))) {
+							childrenToInstall.add(optionalFeatureToConfigure);
+							break;
+						}
+					} catch (CoreException e) {
+						UpdateCore.warn("", e); //$NON-NLS-1$
+					}
+				}
+			}
+		}
+
+		IIncludedFeatureReference[] result = new IIncludedFeatureReference[childrenToInstall.size()];
+		if (childrenToInstall.size() > 0) {
+			childrenToInstall.toArray(result);
+		}
+
+		return result;
+	}
+
+	/*
+	 * @see IConfiguredSite#unconfigure(IFeature)
+	 */
+	public boolean unconfigure(IFeature feature) throws CoreException {
+		// the first call sould disable without checking for enable parent
+		return unconfigure(feature, true, false);
+	}
+
+	private boolean unconfigure(IFeature feature, boolean includePatches, boolean verifyEnableParent) throws CoreException {
+		IFeatureReference featureReference = getSite().getFeatureReference(feature);
+
+		if (featureReference == null) {
+			UpdateCore.warn("Unable to retrieve Feature Reference for feature" + feature); //$NON-NLS-1$
+			return false;
+		}
+
+		ConfigurationPolicy configPolicy = getConfigurationPolicy();
+		if (configPolicy == null)
+			return false;
+
+		// verify no enable parent
+		if (verifyEnableParent && !validateNoConfiguredParents(feature)) {
+			UpdateCore.warn("The feature " + feature.getVersionedIdentifier() + " to disable is needed by another enable feature"); //$NON-NLS-1$ //$NON-NLS-2$
+			return false;
+		}
+
+		boolean sucessfullyUnconfigured = false;
+		try {
+			sucessfullyUnconfigured = configPolicy.unconfigure(featureReference, true, true);
+		} catch (CoreException e) {
+			URL url = featureReference.getURL();
+			String urlString = (url != null) ? url.toExternalForm() : "<no feature reference url>"; //$NON-NLS-1$
+			UpdateCore.warn("Unable to unconfigure" + urlString, e); //$NON-NLS-1$
+			throw e;
+		}
+		if (sucessfullyUnconfigured) {
+			// 2.0.2: unconfigure patches that reference this feature.
+			// A patch is a feature that contains an import
+			// statement with patch="true" and an id/version
+			// that matches an already installed and configured
+			// feature. When patched feature is unconfigured,
+			// all the patches that reference it must be 
+			// unconfigured as well
+			// (in contrast, patched features can be
+			// configured without the patches).
+			if (includePatches)
+				unconfigurePatches(feature);
+
+			// top down approach, same configuredSite
+			IIncludedFeatureReference[] childrenRef = feature.getIncludedFeatureReferences();
+			for (int i = 0; i < childrenRef.length; i++) {
+				try {
+					IFeature child = childrenRef[i].getFeature(null); // disable the exact feature
+					unconfigure(child, includePatches, true); // check for parent as we should be the only parent.
+				} catch (CoreException e) {
+					// skip any bad children
+					UpdateCore.warn("Unable to unconfigure child feature: " + childrenRef[i] + " " + e); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+			}
+
+			// notify listeners
+			Object[] siteListeners = listeners.getListeners();
+			for (int i = 0; i < siteListeners.length; i++) {
+				IConfiguredSiteChangedListener listener = ((IConfiguredSiteChangedListener) siteListeners[i]);
+				listener.featureUnconfigured(feature);
+			}
+
+			return true;
+		} else {
+			URL url = featureReference.getURL();
+			String urlString = (url != null) ? url.toExternalForm() : "<no feature reference url>"; //$NON-NLS-1$
+			UpdateCore.warn("Unable to unconfigure:" + urlString); //$NON-NLS-1$
+			return false;
+		}
+	}
+
+	/*
+	 * Look for features that have an import reference
+	 * that points to this feature and where patch=true.
+	 * Unconfigure all the matching patches, but
+	 * do not do the same lookup for them
+	 * because patches cannot have patches themselves.
+	 */
+
+	private void unconfigurePatches(IFeature feature) {
+		IFeatureReference[] frefs = getConfiguredFeatures();
+		for (int i = 0; i < frefs.length; i++) {
+			IFeatureReference fref = frefs[i];
+			try {
+				IFeature candidate = fref.getFeature(null);
+				if (candidate.equals(feature))
+					continue;
+
+				if (UpdateUtils.isPatch(feature, candidate))
+					unconfigure(candidate, false, false);
+			} catch (CoreException e) {
+				UpdateCore.warn("", e); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/*
+	 * @see IConfiguredSite#getConfiguredFeatures()
+	 */
+	public IFeatureReference[] getConfiguredFeatures() {
+		if (isEnabled())
+			return getRawConfiguredFeatures();
+		else
+			return new ISiteFeatureReference[0];
+	}
+
+	/*
+	 * @see IConfiguredSite#getConfiguredFeatures()
+	 */
+	private IFeatureReference[] getRawConfiguredFeatures() {
+		ConfigurationPolicy configPolicy = getConfigurationPolicy();
+		if (configPolicy == null)
+			return new ISiteFeatureReference[0];
+
+		return configPolicy.getConfiguredFeatures();
+	}
+
+	/*
+	 * adds configured and unconfigured feature references
+	 */
+	public IFeatureReference[] getFeatureReferences() {
+
+		ConfigurationPolicy configPolicy = getConfigurationPolicy();
+		if (configPolicy == null)
+			return new ISiteFeatureReference[0];
+
+		IFeatureReference[] configuredFeatures = getConfiguredFeatures();
+		int confLen = configuredFeatures.length;
+		IFeatureReference[] unconfiguredFeatures = configPolicy.getUnconfiguredFeatures();
+		int unconfLen = unconfiguredFeatures.length;
+
+		IFeatureReference[] result = new IFeatureReference[confLen + unconfLen];
+		if (confLen > 0) {
+			System.arraycopy(configuredFeatures, 0, result, 0, confLen);
+		}
+		if (unconfLen > 0) {
+			System.arraycopy(unconfiguredFeatures, 0, result, confLen, unconfLen);
+		}
+
+		return result;
+	}
+
+	/*
+	 * Configure and unconfigure appropriate feature to
+	 * become 'like' currentConfiguration which is the configuration
+	 * the user wants to revert to.
+	 * 
+	 * All features from currentConfiguration should be configured
+	 */
+	public void revertTo(IConfiguredSite oldConfiguration, IProgressMonitor monitor, IProblemHandler handler) throws CoreException, InterruptedException {
+
+		ConfiguredSite oldConfiguredSite = (ConfiguredSite) oldConfiguration;
+
+		// retrieve the feature that were configured
+		IFeatureReference[] configuredFeatures = oldConfiguredSite.validConfiguredFeatures(handler);
+
+		for (int i = 0; i < configuredFeatures.length; i++) {
+			getConfigurationPolicy().configure(configuredFeatures[i], true, true);
+		}
+
+		// calculate all the features we have to unconfigure from the current state to this state
+		// in the history. 				
+		List featureToUnconfigure = oldConfiguredSite.calculateUnconfiguredFeatures(configuredFeatures);
+
+		// for each unconfigured feature check if it still exists
+		// if so add as unconfigured
+		Iterator iter = featureToUnconfigure.iterator();
+		while (iter.hasNext()) {
+			IFeatureReference element = (IFeatureReference) iter.next();
+			try {
+				// do not log activity
+				getConfigurationPolicy().unconfigure(element, true, true);
+			} catch (CoreException e) {
+				// log no feature to unconfigure
+				String url = element.getURL().toString();
+				ISite site = element.getSite();
+				String siteString = (site != null) ? site.getURL().toExternalForm() : Messages.ConfiguredSite_NoSite; 
+				UpdateCore.warn(NLS.bind(Messages.ConfiguredSite_CannotFindFeatureToUnconfigure, (new String[] { url, siteString })), e); 
+			}
+		}
+		//} // end USER_EXCLUDE
+	}
+
+	/*
+	 * We have to keep our configured feature
+	 * check if they are all valid
+	 * Return the valid configured features
+	 */
+	private IFeatureReference[] validConfiguredFeatures(IProblemHandler handler) throws InterruptedException {
+
+		IFeatureReference[] configuredFeatures = getConfiguredFeatures();
+		if (configuredFeatures != null) {
+			for (int i = 0; i < configuredFeatures.length; i++) {
+				IFeature feature = null;
+
+				// attempt to access the feature
+				try {
+					feature = configuredFeatures[i].getFeature(null);
+				} catch (CoreException e) {
+					// notify we cannot find the feature
+					UpdateCore.warn(null, e);
+					String featureString = configuredFeatures[i].getURL().toExternalForm();
+					if (!handler.reportProblem(NLS.bind(Messages.ConfiguredSite_CannotFindFeatureToConfigure, (new String[] { featureString })))) {
+						throw new InterruptedException();
+					}
+				}
+
+				// verify all the plugins still exist
+				if (feature != null) {
+					// get plugin identifier
+					List sitePluginIdentifiers = new ArrayList();
+					ISite site = feature.getSite();
+					IPluginEntry[] sitePluginEntries = null;
+
+					if (site != null) {
+						sitePluginEntries = site.getPluginEntries();
+						for (int index = 0; index < sitePluginEntries.length; index++) {
+							IPluginEntry entry = sitePluginEntries[index];
+							sitePluginIdentifiers.add(entry.getVersionedIdentifier());
+						}
+					}
+
+					if (sitePluginEntries.length > 0) {
+						IPluginEntry[] featurePluginEntries = feature.getPluginEntries();
+						for (int index = 0; index < featurePluginEntries.length; index++) {
+							IPluginEntry currentFeaturePluginEntry = featurePluginEntries[index];
+							if (!contains(currentFeaturePluginEntry.getVersionedIdentifier(), sitePluginIdentifiers)) {
+								// the plugin defined by the feature
+								// doesn't seem to exist on the site
+								String msg = "Error verifying existence of plugin:" + currentFeaturePluginEntry.getVersionedIdentifier().toString(); //$NON-NLS-1$
+								UpdateCore.log(msg, new Exception());
+
+								String siteString = (site != null) ? site.getURL().toExternalForm() : Messages.ConfiguredSite_NoSite;	
+								String errorLabel = NLS.bind(Messages.ConfiguredSite_CannotFindPluginEntry, (new String[] { currentFeaturePluginEntry.getVersionedIdentifier().toString(), siteString }));
+								if (handler == null) {
+									throw new InterruptedException(errorLabel);
+								}
+								if (!handler.reportProblem(errorLabel)) {
+									throw new InterruptedException();
+								}
+							} // end if not found in site
+						} // end for
+					}
+				}
+			} // end for configured feature
+		}
+		return configuredFeatures;
+	}
+
+	/*
+	 * We are in the process of calculating the delta between what was configured in the current
+	 * configuration that is not configured now
+	 * 
+	 * we have to figure out what feature have been unconfigured for the whole
+	 * history between current and us... 
+	 * 
+	 * is it as simple as  get all configured, and unconfigured,
+	 * the do the delta with what should be configured
+	 * 
+	 */
+	private List calculateUnconfiguredFeatures(IFeatureReference[] configuredFeatures) throws CoreException {
+
+		Set featureToUnconfigureSet = new HashSet();
+
+		// loop for all history
+		// try to see if the configured site existed
+		// if it does, get the unconfigured features 
+		// and the configured one
+		IInstallConfiguration[] history = SiteManager.getLocalSite().getConfigurationHistory();
+
+		for (int i = 0; i < history.length; i++) {
+			IInstallConfiguration element = history[i];
+			IConfiguredSite[] configSites = element.getConfiguredSites();
+			for (int j = 0; j < configSites.length; j++) {
+				ConfiguredSite configSite = (ConfiguredSite) configSites[j];
+				if (configSite.getSite().equals(getSite())) {
+					featureToUnconfigureSet.addAll(Arrays.asList(configSite.getConfigurationPolicy().getUnconfiguredFeatures()));
+					featureToUnconfigureSet.addAll(Arrays.asList(configSite.getConfigurationPolicy().getConfiguredFeatures()));
+				}
+			}
+		}
+
+		// remove the unconfigured feature we found that are now to be configured 
+		// (they may have been unconfigured in the past, but the revert makes them configured)
+		List featureToUnconfigureList = remove(configuredFeatures, featureToUnconfigureSet);
+
+		return featureToUnconfigureList;
+	}
+
+	/*
+	 * Utilities: Remove an array of feature references
+	 * from a list
+	 */
+	private List remove(IFeatureReference[] featureRefs, Set set) {
+		List result = new ArrayList();
+
+		if (set == null)
+			return result;
+
+		// if an element of the list is NOT found in the array,
+		// add it to the result list			
+		Iterator iter = set.iterator();
+		while (iter.hasNext()) {
+			IFeatureReference element = (IFeatureReference) iter.next();
+			boolean found = false;
+			for (int i = 0; i < featureRefs.length; i++) {
+				if (element.equals(featureRefs[i])) {
+					found = true;
+				}
+			}
+
+			if (!found)
+				result.add(element);
+		}
+		return result;
+	}
+
+	/*
+	 * I have issues when running list.contain(versionedIdentifier)
+	 * The code runs the Object.equals instead of the VersionedIdentifier.equals
+	 */
+	private boolean contains(VersionedIdentifier id, List list) {
+		boolean found = false;
+		if (list != null && !list.isEmpty()) {
+			Iterator iter = list.iterator();
+			while (iter.hasNext() && !found) {
+				VersionedIdentifier element = (VersionedIdentifier) iter.next();
+				if (element.equals(id)) {
+					found = true;
+				}
+			}
+		}
+		return found;
+	}
+
+	/*
+	 * 
+	 */
+	public ConfigurationPolicy getConfigurationPolicy() {
+		return (ConfigurationPolicy) getConfigurationPolicyModel();
+	}
+
+	/*
+	 * 
+	 */
+	public ISite getSite() {
+		return (ISite) getSiteModel();
+	}
+
+	/*
+	 * 
+	 */
+	public IInstallConfiguration getInstallConfiguration() {
+		return (IInstallConfiguration) getInstallConfigurationModel();
+	}
+
+	/*
+	 * 
+	 */
+	public IStatus getBrokenStatus(IFeature feature) {
+
+		IStatus featureStatus = createStatus(IStatus.OK, IFeature.STATUS_HAPPY, "", null); //$NON-NLS-1$
+
+		// check the Plugins of all the features
+		// every plugin of the feature must be on the site
+		IPluginEntry[] siteEntries = getSite().getPluginEntries();
+		IPluginEntry[] featuresEntries = feature.getPluginEntries();
+		IPluginEntry[] result = UpdateManagerUtils.diff(featuresEntries, siteEntries);
+		if (result != null && (result.length != 0)) {
+			String msg = Messages.SiteLocal_FeatureUnHappy; 
+			MultiStatus multi = new MultiStatus(featureStatus.getPlugin(), IFeature.STATUS_UNHAPPY, msg, null);
+
+			for (int k = 0; k < result.length; k++) {
+				VersionedIdentifier id = result[k].getVersionedIdentifier();
+				Object[] values = new String[] { "", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+				if (id != null) {
+					values = new Object[] { id.getIdentifier(), id.getVersion()};
+				}
+				String msg1 = NLS.bind(Messages.ConfiguredSite_MissingPluginsBrokenFeature, values);
+				UpdateCore.warn(msg1);
+				IStatus status = createStatus(IStatus.ERROR, IFeature.STATUS_UNHAPPY, msg1, null);
+				multi.add(status);
+			}
+			return multi;
+		}
+
+		// check os, arch, and ws
+
+		String msg = Messages.SiteLocal_FeatureHappy; 
+		return createStatus(IStatus.OK, IFeature.STATUS_HAPPY, msg, null);
+	}
+
+	/*
+	 * 
+	 */
+	public boolean isConfigured(IFeature feature) {
+		if (!isEnabled())
+			return false;
+
+		if (getConfigurationPolicy() == null)
+			return false;
+		IFeatureReference featureReference = getSite().getFeatureReference(feature);
+		if (featureReference == null) {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS)
+				UpdateCore.warn("Unable to retrieve featureReference for feature:" + feature); //$NON-NLS-1$
+			return false;
+		}
+		return getConfigurationPolicy().isConfigured(featureReference);
+	}
+
+	/**
+	 * @see Object#toString()
+	 */
+	public String toString() {
+		if (getSite() == null)
+			return "No Site"; //$NON-NLS-1$
+		if (getSite().getURL() == null)
+			return "No URL"; //$NON-NLS-1$
+		return getSite().getURL().toExternalForm();
+	}
+
+	/**
+	 * @see IConfiguredSite#verifyUpdatableStatus()
+	 */
+	public IStatus verifyUpdatableStatus() {
+
+		if (verifyStatus != null)
+			return verifyStatus;
+
+		URL siteURL = getSite().getURL();
+		if (siteURL == null) {
+			verifyStatus = createStatus(IStatus.ERROR, Messages.ConfiguredSite_SiteURLNull, null); 
+			return verifyStatus;
+		}
+
+		if (!"file".equalsIgnoreCase(siteURL.getProtocol())) { //$NON-NLS-1$
+			verifyStatus = createStatus(IStatus.ERROR, Messages.ConfiguredSite_NonLocalSite, null); 
+			return verifyStatus;
+		}
+
+		String siteLocation = siteURL.getFile();
+		File file = new File(siteLocation);
+
+		// get the product name of the private marker
+		// if there is no private marker, check if the site is contained in another site
+		// if there is a marker and this is a different product, return false
+		// otherwise don't check if we are contained in another site
+		String productName = getProductName(file);
+		if (productName != null) {
+			if (!productName.equals(getProductIdentifier("id", getProductFile()))) { //$NON-NLS-1$
+				verifyStatus = createStatus(IStatus.ERROR, NLS.bind(Messages.ConfiguredSite_NotSameProductId, (new String[] { productName })), null);
+				return verifyStatus;
+			}
+		} else {
+			File container = getSiteContaining(file);
+			// allow the install location to pass even though it looks like this
+			// site is contained in another site
+			if (container != null && !siteLocation.equals(Platform.getInstallLocation().getURL().getFile())) {
+				verifyStatus = createStatus(IStatus.ERROR, NLS.bind(Messages.ConfiguredSite_ContainedInAnotherSite, (new String[] { container.getAbsolutePath() })), null);
+				return verifyStatus;
+			}
+		}
+
+		if (!canWrite(file)) {
+			verifyStatus = createStatus(IStatus.ERROR, Messages.ConfiguredSite_ReadOnlySite, null); 
+			return verifyStatus;
+		}
+
+		verifyStatus = createStatus(IStatus.OK, "", null); //$NON-NLS-1$
+		setUpdatable(true);
+		return verifyStatus;
+	}
+
+	/*
+	 * Verify we can write on the file system
+	 */
+    public static boolean canWrite(File file) {
+        if (file.canWrite() == false)
+            return false;
+
+        if (!file.isDirectory())
+            return false;
+
+        File fileTest = null;
+        try {
+        	// we use the .dll suffix to properly test on Vista virtual directories
+        	// on Vista you are not allowed to write executable files on virtual directories like "Program Files"
+            fileTest = File.createTempFile("writtableArea", ".dll", file); //$NON-NLS-1$ //$NON-NLS-2$
+        } catch (IOException e) {
+            //If an exception occurred while trying to create the file, it means that it is not writable
+            return false;
+        } finally {
+            if (fileTest != null)
+                fileTest.delete();
+        }
+        return true;
+    }
+
+	/*
+	 * Check if the directory contains a marker
+	 * if not ask all directory children to check
+	 * if one validates the condition, returns the marker
+	 */
+	private static File getSiteContaining(File file) {
+
+		if (file == null)
+			return null;
+
+		UpdateCore.warn("IsContained: Checking for markers at:" + file); //$NON-NLS-1$
+		if (file.exists() && file.isDirectory()) {
+			File productFile = new File(file, PRODUCT_SITE_MARKER);
+			File extensionFile = new File(file, EXTENSION_SITE_MARKER);
+			if (productFile.exists() || extensionFile.exists())
+				return file;
+//			// do not check if a marker exists in the current but start from the parent
+//			// the current is analyze by getProductname()
+//			if (file.getParentFile() != null) {
+//				File privateFile = new File(file.getParentFile(), PRIVATE_SITE_MARKER);
+//				if (privateFile.exists())
+//					return file.getParentFile();
+//			}
+		}
+		return getSiteContaining(file.getParentFile());
+	}
+
+	/*
+	 * Returns the name of the product if the identifier of the private Site markup is not
+	 * the same as the identifier of the product the workbench was started with.
+	 * If the product is the same, return null.
+	 */
+	private static String getProductName(File file) {
+
+		if (file == null)
+			return null;
+
+		File markerFile = new File(file, EXTENSION_SITE_MARKER );
+		if (!markerFile.exists()) {
+			return null;
+		}
+
+		File productFile = getProductFile();
+		String productId = null;
+		String privateId = null;
+		if (productFile != null) {
+			productId = getProductIdentifier("id", productFile); //$NON-NLS-1$
+			privateId = getProductIdentifier("id", markerFile); //$NON-NLS-1$
+			if (productId == null) {
+				UpdateCore.warn("Product ID is null at:" + productFile); //$NON-NLS-1$
+				return null;
+			}
+			if (!productId.equalsIgnoreCase(privateId)) {
+				UpdateCore.warn("Product id at" + productFile + " Different than:" + markerFile); //$NON-NLS-1$ //$NON-NLS-2$
+				String name = getProductIdentifier("name", markerFile); //$NON-NLS-1$
+				String version = getProductIdentifier("version", markerFile); //$NON-NLS-1$
+				String markerID = (name == null) ? version : name + ":" + version; //$NON-NLS-1$
+				if (markerID == null)
+					markerID = ""; //$NON-NLS-1$
+				return markerID;
+			} else {
+				return privateId;
+			}
+		} else {
+			UpdateCore.warn("Product Marker doesn't exist:" + productFile); //$NON-NLS-1$
+		}
+
+		return null;
+	}
+
+	/*
+	 * Returns the identifier of the product from the property file
+	 */
+	private static String getProductIdentifier(String identifier, File propertyFile) {
+		String result = null;
+		if (identifier == null)
+			return result;
+		InputStream in = null;
+		try {
+			in = new FileInputStream(propertyFile);
+			PropertyResourceBundle bundle = new PropertyResourceBundle(in);
+			result = bundle.getString(identifier);
+		} catch (IOException e) {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
+				UpdateCore.debug("Exception reading property file:" + propertyFile); //$NON-NLS-1$
+		} catch (MissingResourceException e) {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
+				UpdateCore.debug("Exception reading '" + identifier + "' from property file:" + propertyFile); //$NON-NLS-1$ //$NON-NLS-2$
+		} finally {
+			if (in != null)
+				try {
+					in.close();
+				} catch (IOException e1) {
+				}
+		}
+		return result;
+	}
+
+	/*
+	 * Returns the identifier of the product from the property file
+	 */
+	private static File getProductFile() {
+
+		String productInstallDirectory = ConfiguratorUtils.getInstallURL().getFile();
+		if (productInstallDirectory != null) {
+			File productFile = new File(productInstallDirectory, PRODUCT_SITE_MARKER);
+			if (productFile.exists()) {
+				return productFile;
+			} else {
+				UpdateCore.warn("Product marker doesn't exist:" + productFile); //$NON-NLS-1$
+			}
+		} else {
+			UpdateCore.warn("Cannot retrieve install URL from BootLoader"); //$NON-NLS-1$
+		}
+		return null;
+	}
+
+	/*
+	 * 
+	 */
+	/*package*/
+	boolean createPrivateSiteMarker() {
+		URL siteURL = getSite().getURL();
+		if (siteURL == null) {
+			UpdateCore.warn("Unable to create marker. The Site url is null."); //$NON-NLS-1$
+			return false;
+		}
+
+		if (!"file".equalsIgnoreCase(siteURL.getProtocol())) { //$NON-NLS-1$
+			UpdateCore.warn("Unable to create private marker. The Site is not on the local file system."); //$NON-NLS-1$
+			return false;
+		}
+
+		String siteLocation = siteURL.getFile();
+		File productFile = getProductFile();
+		boolean success = false;
+		if (productFile != null) {
+			String productId = getProductIdentifier("id", productFile); //$NON-NLS-1$
+			String productName = getProductIdentifier("name", productFile); //$NON-NLS-1$
+			String productVer = getProductIdentifier("version", productFile); //$NON-NLS-1$
+			if (productId != null) {
+				File file = new File(siteLocation, EXTENSION_SITE_MARKER);
+				if (!file.exists()) {
+					OutputStream out = null;
+					OutputStreamWriter outWriter = null;
+					try {
+						out = new FileOutputStream(file);
+						outWriter = new OutputStreamWriter(out, "UTF8"); //$NON-NLS-1$
+						outWriter.write("id=" + productId+"\n"); //$NON-NLS-1$ //$NON-NLS-2$
+						if (productName != null)
+							outWriter.write("name=" + productName+"\n"); //$NON-NLS-1$ //$NON-NLS-2$
+						if (productVer != null)
+							outWriter.write("version=" + productVer+"\n"); //$NON-NLS-1$ //$NON-NLS-2$
+						success = true;
+						justCreated = true;
+					} catch (Exception e) {
+						UpdateCore.warn("Unable to create private Marker at:" + file, e); //$NON-NLS-1$
+					} finally {
+						try {
+							if (outWriter != null)
+								outWriter.close();
+						} catch (IOException e1) {
+						}
+						try {
+							if (out != null)
+								out.close();
+						} catch (IOException e2) {
+						}
+					}
+				}
+			}
+		}
+		return success;
+	}
+
+
+	/*
+	 * Returns true if the directory of the Site contains
+	 * .eclipseextension
+	 */
+	public boolean isExtensionSite() {
+		return containsMarker(EXTENSION_SITE_MARKER);
+	}
+
+	/*
+	 * Returns true if the directory of the Site contains
+	 * .eclipseextension
+	 */
+	public boolean isProductSite() {
+		return containsMarker(PRODUCT_SITE_MARKER);
+	}
+
+	/*
+	 * Returns true if the directory of the Site contains
+	 * .eclipseextension
+	 */
+	public boolean isPrivateSite() {
+		return isExtensionSite();
+	}
+
+	/*
+	 * 
+	 */
+	private boolean containsMarker(String marker) {
+		ISite site = getSite();
+		if (site == null) {
+			UpdateCore.warn("Contains Markers:The site is null"); //$NON-NLS-1$
+			return false;
+		}
+
+		URL url = site.getURL();
+		if (url == null) {
+			UpdateCore.warn("Contains Markers:Site URL is null"); //$NON-NLS-1$
+			return false;
+		}
+		if (!"file".equalsIgnoreCase(url.getProtocol())) { //$NON-NLS-1$
+			UpdateCore.warn("Contains Markers:Non file protocol"); //$NON-NLS-1$
+			return false;
+		}
+		File file = new File(url.getFile());
+		if (!file.exists()) {
+			UpdateCore.warn("Contains Markers:The site doesn't exist:" + file); //$NON-NLS-1$
+			return false;
+		}
+		File extension = new File(file, marker);
+		if (!extension.exists()) {
+			UpdateCore.warn("Contains Markers:The extensionfile does not exist:" + extension); //$NON-NLS-1$
+			return false;
+		}
+		return true;
+	}
+
+	/*
+	 * Returns true if the Site is already natively linked
+	 */
+	public boolean isNativelyLinked() throws CoreException {
+		String platformString = getPlatformURLString();
+		if (platformString == null) {
+			UpdateCore.warn("Unable to retrieve platformString"); //$NON-NLS-1$
+			return false;
+		}
+
+		URL siteURL = null;
+		try {
+			// check if the site exists and is updateable
+			// update configSite
+			URL urlToCheck = new URL(platformString);
+			IPlatformConfiguration runtimeConfig = ConfiguratorUtils.getCurrentPlatformConfiguration();
+			IPlatformConfiguration.ISiteEntry entry = runtimeConfig.findConfiguredSite(urlToCheck);
+			if (entry != null) {
+				return entry.isNativelyLinked();
+			} else {
+				UpdateCore.warn("Unable to retrieve site:" + platformString + " from platform."); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			// check by comparing URLs
+			IPlatformConfiguration.ISiteEntry[] sites = runtimeConfig.getConfiguredSites();
+			for (int i = 0; i < sites.length; i++) {
+				siteURL = sites[i].getURL();
+				URL resolvedURL = FileLocator.resolve(siteURL);
+				if (UpdateManagerUtils.sameURL(resolvedURL, urlToCheck))
+					return true;
+			}
+		} catch (MalformedURLException e) {
+			String msg = NLS.bind(Messages.ConfiguredSite_UnableResolveURL, (new String[] { platformString }));
+			throw Utilities.newCoreException(msg, e);
+		} catch (IOException e) {
+			String msg = NLS.bind(Messages.ConfiguredSite_UnableToAccessSite, (new Object[] { siteURL }));
+			throw Utilities.newCoreException(msg, e);
+		}
+
+		return false;
+	}
+
+	/*
+	* we have to check that no configured/enable parent include this feature
+	*/
+	private boolean validateNoConfiguredParents(IFeature feature) throws CoreException {
+		if (feature == null) {
+			UpdateCore.warn("ConfigurationPolicy: validate Feature is null"); //$NON-NLS-1$
+			return true;
+		}
+
+		IFeatureReference[] parents = UpdateManagerUtils.getParentFeatures(feature, getConfiguredFeatures(), false);
+		return (parents.length == 0);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ContentConsumer.java
new file mode 100644
index 0000000..5278a7c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ContentConsumer.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+
+import org.eclipse.update.core.*;
+
+/**
+ * A default implementation for IFeatureContentConsumer
+ * </p>
+ * @since 2.0
+ */
+
+public abstract class ContentConsumer implements IContentConsumer {
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/CoreExceptionWithRootCause.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/CoreExceptionWithRootCause.java
new file mode 100644
index 0000000..d822959
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/CoreExceptionWithRootCause.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+public class CoreExceptionWithRootCause extends CoreException {
+
+	private static final long serialVersionUID = 6832993239926767403L;
+	
+	private Throwable e = null;
+	
+	public CoreExceptionWithRootCause(IStatus status) {
+		super(status);
+	}
+
+	public void setRootException(Throwable e) {
+		this.e = e;	
+	}
+
+	public Throwable getRootException() {
+		return e;
+	}
+
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DefaultInstallHandler.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DefaultInstallHandler.java
new file mode 100644
index 0000000..5029b04
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DefaultInstallHandler.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+
+import java.io.*;
+import java.util.jar.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.JarContentReference.*;
+import org.eclipse.update.core.model.*;
+
+/**
+ * Default Implementation of InstallHandler
+ */
+public class DefaultInstallHandler extends BaseInstallHandler {
+
+	/*
+	 * @see IInstallHandler#nonPluginDataDownloaded(INonPluginEntry[], IVerificationListener)
+	 */
+	public void nonPluginDataDownloaded(
+		INonPluginEntry[] nonPluginData,
+		IVerificationListener listener)
+		throws CoreException {
+
+		// verify non-plugin archives. The DefaultInstallHandler assumes
+		// the verifier associated with the feature is able to verify the
+		// data archives.
+		if (nonPluginData == null || nonPluginData.length == 0)
+			return;
+
+		this.nonPluginEntries = nonPluginData;
+		IFeatureContentProvider provider = this.feature.getFeatureContentProvider();
+		IVerifier verifier = provider.getVerifier();
+		if (verifier == null)
+			return;
+
+		for (int i = 0; i < this.nonPluginEntries.length; i++) {
+			ContentReference[] archives =
+				provider.getNonPluginEntryArchiveReferences(nonPluginEntries[i], this.monitor);
+			IVerificationResult result;
+			for (int j = 0; j < archives.length; j++) {
+
+				// see if the data entry is a jar
+				ContentReference archive = archives[j];
+				if (!(archives[j] instanceof JarContentReference)
+					&& archives[j].getIdentifier().endsWith(".jar")) { //$NON-NLS-1$
+					try {
+						archive =
+							new JarContentReference(archives[j].getIdentifier(), archives[j].asFile());
+					} catch (IOException e) {
+					}
+				}
+
+				result = verifier.verify(this.feature, archive, false, this.monitor);
+				if (result != null)
+					promptForVerification(result, listener);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#completeInstall(IFeatureContentConsumer)
+	 */
+	public void completeInstall(IFeatureContentConsumer consumer)
+		throws CoreException {
+
+		// plugins have been installed. Check to see if we have any
+		// non-plugin entries that need to be handled.
+		if (this.nonPluginEntries == null || this.nonPluginEntries.length == 0)
+			return;
+
+		// install non-plugin archives
+		IFeatureContentProvider provider = this.feature.getFeatureContentProvider();
+		for (int i = 0; i < this.nonPluginEntries.length; i++) {
+			ContentReference[] archive =
+				provider.getNonPluginEntryArchiveReferences(nonPluginEntries[i], this.monitor);
+			IContentConsumer nonPluginConsumer = consumer.open(nonPluginEntries[i]);
+			for (int j = 0; j < archive.length; j++) {
+				String id = archive[j].getIdentifier();
+				if (id.endsWith(".jar")) { //$NON-NLS-1$
+					// the non-plugin archive is a jar. Unpack it into
+					// a directory constructed using the archive id
+					try {
+						final String prefix = id.substring(0, id.length() - 4) + "/"; //$NON-NLS-1$
+						JarContentReference jarRef = new JarContentReference("", archive[j].asFile()); //$NON-NLS-1$
+						ContentSelector selector = new ContentSelector() {
+							public String defineIdentifier(JarEntry entry) {
+								if (entry == null)
+									return null;
+								else
+									return prefix + entry.getName();
+							}
+						};
+						ContentReference[] entries = jarRef.peek(selector, this.monitor);
+						for (int k = 0; k < entries.length; k++) {
+							nonPluginConsumer.store(entries[k], this.monitor);
+						}
+					} catch (IOException e) {
+						throw Utilities
+							.newCoreException(NLS.bind(Messages.JarVerificationService_CancelInstall, (new String[] { id })),
+						e);
+					}
+
+				} else {
+					// the non-plugin archive is not a jar. Install it asis.
+					nonPluginConsumer.store(archive[j], this.monitor);
+				}
+			}
+			nonPluginConsumer.close();
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private void promptForVerification(
+		IVerificationResult verificationResult,
+		IVerificationListener listener)
+		throws CoreException {
+
+		if (listener == null)
+			return;
+		int result = listener.prompt(verificationResult);
+
+		if (result == IVerificationListener.CHOICE_ABORT) {
+			Exception e = verificationResult.getVerificationException();
+			throw new InstallAbortedException(Messages.JarVerificationService_CancelInstall,e); 
+		}
+		if (result == IVerificationListener.CHOICE_ERROR) {
+			throw Utilities
+				.newCoreException(Messages.JarVerificationService_UnsucessfulVerification, 
+			verificationResult.getVerificationException());
+		}
+
+		return;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DeltaInstallHandler.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DeltaInstallHandler.java
new file mode 100644
index 0000000..d92d735
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DeltaInstallHandler.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import org.eclipse.core.runtime.IStatus;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.update.core.BaseInstallHandler;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureContentConsumer;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.PluginEntry;
+import org.eclipse.update.core.Site;
+import org.eclipse.update.internal.operations.UpdateUtils;
+
+/**
+ * Install handler for partial plugin delivery: copy delta content from old plugin into the new one.
+ * The new plugin should only contain files that have changed.
+ */
+public class DeltaInstallHandler extends BaseInstallHandler {
+	private final static String PLUGIN_XML = "plugin.xml"; //$NON-NLS-1$
+	private final static String FRAGMENT_XML = "fragment.xml"; //$NON-NLS-1$
+	private final static String META_MANIFEST = "META-INF/MANIFEST.MF"; //$NON-NLS-1$
+
+	protected IFeature oldFeature;
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.core.IInstallHandler#completeInstall(org.eclipse.update.core.IFeatureContentConsumer)
+	 */
+	public void completeInstall(IFeatureContentConsumer consumer)
+		throws CoreException {
+		try {
+			if (pluginEntries == null)
+				return;
+
+			if (!feature.isPatch()) {
+				// Get the old feature
+				IFeature[] oldFeatures = UpdateUtils
+						.getInstalledFeatures(feature);
+				if (oldFeatures.length == 0)
+					return;
+				oldFeature = oldFeatures[0];
+			} else {
+				oldFeature = UpdateUtils.getPatchedFeature(feature);
+				if (oldFeature == null) {
+					return;
+				}
+			}
+			
+			IPluginEntry[] oldPlugins = oldFeature.getPluginEntries();
+			for (int i = 0; i < pluginEntries.length; i++) {
+				IPluginEntry newPlugin = pluginEntries[i];
+				IPluginEntry oldPlugin =
+					getPluginEntry(
+						oldPlugins,
+						newPlugin.getVersionedIdentifier().getIdentifier());
+				if (oldPlugin == null)
+					continue;
+				try {
+					overlayPlugin(oldPlugin, newPlugin, consumer);
+				} catch (IOException e) {
+					throw new CoreException(
+						new Status(
+							IStatus.ERROR,
+							UpdateUtils.getPluginId(),
+							1,
+							"", //$NON-NLS-1$
+							e));
+				}
+			}
+		} finally {
+			//if (contentConsumer != null)
+			//	contentConsumer.close();
+		}
+	}
+
+	protected IPluginEntry getPluginEntry(IPluginEntry[] plugins, String id) {
+		for (int i = 0; i < plugins.length; i++)
+			if (plugins[i].getVersionedIdentifier().getIdentifier().equals(id))
+				return plugins[i];
+		return null;
+	}
+
+	protected boolean referenceExists(
+		ContentReference[] references,
+		ContentReference ref) {
+		String id = ref.getIdentifier();
+		if (id == null)
+			return false;
+
+		for (int i = 0; i < references.length; i++)
+			if (id.equals(references[i].getIdentifier()))
+				return true;
+		return false;
+	}
+
+	protected void overlayPlugin(
+		IPluginEntry oldPlugin,
+		IPluginEntry newPlugin,
+		IFeatureContentConsumer consumer)
+		throws CoreException, IOException {
+		if(newPlugin instanceof PluginEntry && !((PluginEntry)newPlugin).isUnpack()){
+			// partial plug-ins (in patches) must always be unpacked
+			return;
+		}
+		
+		// copy the content of the old plugin over the new one, but only
+		// those files that do not exist on the target
+		ContentReference[] oldReferences =
+			oldFeature
+				.getFeatureContentProvider()
+				.getPluginEntryContentReferences(
+				oldPlugin,
+				null);
+		ContentReference[] newReferences =
+			feature
+				.getFeatureContentProvider()
+				.getPluginEntryContentReferences(
+				newPlugin,
+				null);
+
+		URL newURL = new URL(consumer.getFeature().getSite().getURL(),
+				Site.DEFAULT_PLUGIN_PATH
+						+ newPlugin.getVersionedIdentifier().toString());
+		String pluginPath = newURL.getFile();
+		for (int i = 0; i < oldReferences.length; i++) {
+			if (isPluginManifest(oldReferences[i])
+				|| referenceExists(newReferences, oldReferences[i]))
+				continue;
+
+			InputStream input = null;
+			try {
+				input = oldReferences[i].getInputStream();
+				File targetFile =
+					new File(pluginPath, oldReferences[i].getIdentifier());
+				UpdateManagerUtils.copyToLocal(
+					input,
+					targetFile.getAbsolutePath(),
+					null);
+				UpdateManagerUtils.checkPermissions(
+					oldReferences[i],
+					pluginPath);
+				// 20305
+			} catch (IOException e) {
+				continue;
+			} finally {
+				if(input != null){
+					try{
+						input.close();
+					} catch (IOException ioe) {
+					}
+				}
+			}
+		}
+	}
+
+	protected boolean isPluginManifest(ContentReference ref) {
+		String id = ref.getIdentifier();
+		return PLUGIN_XML.equals(id) || FRAGMENT_XML.equals(id) || META_MANIFEST.equals(id);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Digest.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Digest.java
new file mode 100644
index 0000000..27e1616
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Digest.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.update.core.JarContentReference;
+import org.eclipse.update.core.model.FeatureModel;
+import org.xml.sax.SAXException;
+
+public class Digest {
+	
+	private URL source;
+	private File localSource;
+	private JarFile digestJar;
+	private InputStream inputStream;
+	
+
+	public Digest(URL source){
+		this.source = source;
+	}
+	
+	public FeatureModel[] parseDigest() throws IOException, CoreException, SAXException {
+		DigestContentProvider digestContentProvider = new DigestContentProvider(source);
+		localSource = digestContentProvider.asLocalReference(new JarContentReference( null, source), null).asFile();
+		digestJar = new JarFile(localSource);
+		
+		ZipEntry digestEntry = digestJar.getEntry("digest.xml"); //$NON-NLS-1$
+		
+		if (digestEntry != null) {
+			inputStream = digestJar.getInputStream(digestEntry);
+			DigestParser digest = new DigestParser();
+			digest.init(new LiteFeatureFactory());
+			return digest.parse(inputStream);
+		} else {
+			throw new CoreException(null);
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DigestContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DigestContentProvider.java
new file mode 100644
index 0000000..ec848aa
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DigestContentProvider.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.FeatureContentProvider;
+import org.eclipse.update.core.INonPluginEntry;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.IVerifier;
+import org.eclipse.update.core.InstallMonitor;
+
+public class DigestContentProvider extends FeatureContentProvider {
+
+	public DigestContentProvider(URL base) {
+		super(base);
+		// TODO Auto-generated constructor stub
+	}
+	
+	
+	public ContentReference asLocalReference( ContentReference ref,
+			InstallMonitor monitor) throws IOException, CoreException {
+		return super.asLocalReference(ref, monitor);
+	}
+
+	public ContentReference[] getArchiveReferences(InstallMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public ContentReference[] getFeatureEntryArchiveReferences(
+			InstallMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public ContentReference[] getFeatureEntryContentReferences(
+			InstallMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public ContentReference getFeatureManifestReference(InstallMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public ContentReference[] getNonPluginEntryArchiveReferences(
+			INonPluginEntry nonPluginEntry, InstallMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public ContentReference[] getPluginEntryArchiveReferences(
+			IPluginEntry pluginEntry, InstallMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public ContentReference[] getPluginEntryContentReferences(
+			IPluginEntry pluginEntry, InstallMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IVerifier getVerifier() throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DigestParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DigestParser.java
new file mode 100644
index 0000000..ca0782b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/DigestParser.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.update.core.model.FeatureModelFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class DigestParser extends DefaultHandler {
+
+	private InternalFeatureParser featureParser;
+	
+	private ArrayList featureModels;
+
+	private SAXParser parser;
+
+	private FeatureModelFactory factory;
+
+	private String location;
+	
+	private final static SAXParserFactory parserFactory =
+		SAXParserFactory.newInstance();
+	
+	public DigestParser() {
+		super();
+		featureParser = new InternalFeatureParser();
+		try {
+			parserFactory.setNamespaceAware(true);
+			this.parser = parserFactory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+			UpdateCore.log(e);
+		} catch (SAXException e) {
+			UpdateCore.log(e);
+		}
+	}
+
+	public void init(FeatureModelFactory factory) {
+		init(factory, null);
+	}
+    
+    /**
+     * @param factory
+     * @param location
+     * @since 3.1
+     */
+    public void init(FeatureModelFactory factory, String location) {
+    	
+    	this.factory = factory;
+    	this.location = location;
+    	factory = new LiteFeatureFactory();
+    	featureModels = new ArrayList();
+    	featureParser.internalInit(factory, location);
+    }
+
+	/**
+	 * Parses the specified input steam and constructs a feature model.
+	 * The input stream is not closed as part of this operation.
+	 * 
+	 * @param in input stream
+	 * @return feature model
+	 * @exception SAXException
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public LiteFeature[] parse(InputStream in) throws SAXException, IOException {
+		
+		parser.parse(new InputSource(in), this);	
+		return (LiteFeature[])featureModels.toArray( new LiteFeature[featureModels.size()]);
+	}
+
+
+	/**
+	 * Returns all status objects accumulated by the parser.
+	 *
+	 * @return multi-status containing accumulated status, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public MultiStatus getStatus() {
+		return featureParser.getStatus();
+	}
+
+	/**
+	 * Handle start of element tags
+	 * @see DefaultHandler#startElement(String, String, String, Attributes)
+	 * @since 2.0
+	 */
+	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+		if(localName.equals("digest")) //$NON-NLS-1$
+			return;
+		if(localName.equals("feature")) //$NON-NLS-1$
+			featureParser.internalInit(factory, location);
+		
+		featureParser.startElement(uri, localName, qName, attributes);
+	}
+
+	/**
+	 * Handle end of element tags
+	 * @see DefaultHandler#endElement(String, String, String)
+	 * @since 2.0
+	 */
+	public void endElement(String uri, String localName, String qName) {
+		if(localName.equals("digest")) //$NON-NLS-1$
+			return;
+		featureParser.endElement(uri, localName, qName);
+		if(localName.equals("feature")) { //$NON-NLS-1$
+			try {
+				featureModels.add(featureParser.getFeatureModel());
+			} catch (SAXException e) {
+				e.printStackTrace();
+			} 
+		}
+	}
+	
+
+	/**
+	 * Handle character text
+	 * @see DefaultHandler#characters(char[], int, int)
+	 * @since 2.0
+	 */
+	public void characters(char[] ch, int start, int length) {
+		featureParser.characters(ch, start, length);
+	}
+
+	/**
+	 * Handle errors
+	 * @see DefaultHandler#error(SAXParseException)
+	 * @since 2.0
+	 */
+	public void error(SAXParseException ex) {
+		featureParser.error(ex);
+	}
+
+	/**
+	 * Handle fatal errors
+	 * @see DefaultHandler#fatalError(SAXParseException)
+	 * @exception SAXException
+	 * @since 2.0
+	 */
+	public void fatalError(SAXParseException ex) throws SAXException {
+		featureParser.fatalError(ex);
+	}
+
+	/**
+	 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+	 */
+	public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
+		featureParser.ignorableWhitespace(arg0, arg1, arg2);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/EmptyDirectoryException.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/EmptyDirectoryException.java
new file mode 100644
index 0000000..732e174
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/EmptyDirectoryException.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+public class EmptyDirectoryException extends CoreException {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -762930667883265228L;
+
+	public EmptyDirectoryException(IStatus status) {
+		super(status);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ErrorRecoveryLog.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ErrorRecoveryLog.java
new file mode 100644
index 0000000..d87764b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ErrorRecoveryLog.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.Utilities;
+
+
+/**
+ * Manages the error/recover log file
+ */
+public class ErrorRecoveryLog {
+	private static final String ERROR_RECOVERY_LOG = "error_recovery.log"; //$NON-NLS-1$
+	private static final String LOG_ENTRY_KEY = "LogEntry."; //$NON-NLS-1$
+	private static final String RETURN_CARRIAGE = "\r\n"; //$NON-NLS-1$
+	private static final String END_OF_FILE = "eof=eof"; //$NON-NLS-1$
+
+	//
+	public static final String START_INSTALL_LOG = 	"START_INSTALL_LOG"; //$NON-NLS-1$
+	public static final String PLUGIN_ENTRY = 		"PLUGIN"; //$NON-NLS-1$
+	public static final String FRAGMENT_ENTRY = 		"FRAGMENT";	 //$NON-NLS-1$
+	public static final String BUNDLE_MANIFEST_ENTRY = 		"BUNDLE_MANIFEST";	 //$NON-NLS-1$
+	public static final String BUNDLE_JAR_ENTRY = 			"BUNDLE";	 //$NON-NLS-1$
+	public static final String FEATURE_ENTRY = 		"FEATURE"; //$NON-NLS-1$2
+	public static final String ALL_INSTALLED = 		"ALL_FEATURES_INSTALLED"; //$NON-NLS-1$
+	public static final String RENAME_ENTRY = 		"RENAME"; //$NON-NLS-1$
+	public static final String END_INSTALL_LOG = 	"END_INSTALL_LOG"; //$NON-NLS-1$
+	public static final String START_REMOVE_LOG = 	"REMOVE_LOG"; //$NON-NLS-1$
+	public static final String END_ABOUT_REMOVE =	"END_ABOUT_TO_REMOVE"; //$NON-NLS-1$
+	public static final String DELETE_ENTRY = 		"DELETE"; //$NON-NLS-1$
+	public static final String END_REMOVE_LOG = 		"END_REMOVE_LOG"; //$NON-NLS-1$
+
+	private static ErrorRecoveryLog inst;
+	private FileWriter out;
+	private int index;
+	private List paths;
+	
+	private boolean open = false;
+	private int nbOfOpen = 0;
+	
+
+	/**
+	 * Constructor for ErrorRecoveryLog.
+	 */
+	private ErrorRecoveryLog() {
+		super();
+	}
+
+	/**
+	 * Singleton
+	 */
+	public static ErrorRecoveryLog getLog() {
+		if (inst == null){
+			inst = new ErrorRecoveryLog();
+		}
+		return inst;
+	}
+
+	/**
+	 * get a unique identifer for the file, ensure uniqueness up to now
+	 */
+	public static String getLocalRandomIdentifier(String path) {
+		
+		if (path==null) return null;
+		
+		// verify if it will be a directory without creating the file
+		// as it doesn't exist yet
+		if (path.endsWith(File.separator) || path.endsWith("/")) //$NON-NLS-1$
+			return path;
+		File file = new File(path);
+		String newName =
+			UpdateManagerUtils.getLocalRandomIdentifier(file.getName(), new Date());
+		while (new File(file.getParentFile(), newName).exists()) {
+			newName =
+				UpdateManagerUtils.getLocalRandomIdentifier(file.getName(), new Date());
+		}
+		File newFile = new File(file.getParentFile(),newName);
+		return newFile.getAbsolutePath();
+	}
+
+	/**
+	 * returns the log file 
+	 * We do not check if the file exists
+	 */
+	public File getRecoveryLogFile() {
+		IPlatformConfiguration configuration =
+			ConfiguratorUtils.getCurrentPlatformConfiguration();
+		URL location = configuration.getConfigurationLocation();
+		String locationString = location.getFile();
+		File platformConfiguration = new File(locationString);
+		if (!platformConfiguration.isDirectory()) platformConfiguration = platformConfiguration.getParentFile();
+		return new File(platformConfiguration, ERROR_RECOVERY_LOG);
+	}
+
+
+	/**
+	 * Open the log
+	 */
+	public void open(String logEntry) throws CoreException {
+		if (open) {
+			nbOfOpen++;			
+			UpdateCore.warn("Open nested Error/Recovery log #"+nbOfOpen+":"+logEntry);				 //$NON-NLS-1$ //$NON-NLS-2$
+			return;
+		}
+		
+		File logFile = null;		
+		try {
+			logFile = getRecoveryLogFile();
+			out = new FileWriter(logFile);
+			index = 0;
+			paths=null;
+			open=true;
+			nbOfOpen=0;
+			UpdateCore.warn("Start new Error/Recovery log #"+nbOfOpen+":"+logEntry);							 //$NON-NLS-1$ //$NON-NLS-2$
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.UpdateManagerUtils_UnableToLog, (new Object[] { logFile })),
+				e);
+		}
+		
+		append(logEntry);
+	}
+
+	/**
+	 * Append the string to the log and flush
+	 */
+	public void append(String logEntry) throws CoreException {
+		File logFile = null;
+		try {
+			if (!open) {
+				UpdateCore.warn("Internal Error: The Error/Recovery log is not open:"+logEntry);				 //$NON-NLS-1$
+				return;
+			}
+
+			StringBuffer buffer = new StringBuffer(LOG_ENTRY_KEY);
+			buffer.append(index);
+			buffer.append("="); //$NON-NLS-1$
+			buffer.append(logEntry);
+			buffer.append(RETURN_CARRIAGE);
+
+			out.write(buffer.toString());
+			out.flush();
+			index++;
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.UpdateManagerUtils_UnableToLog, (new Object[] { logFile })),
+				e);
+		}
+	}
+
+	/**
+	 * Append the string to the log and flush
+	 */
+	public void appendPath(String logEntry, String path) throws CoreException {
+		if (path == null)
+			return;
+		StringBuffer buffer = new StringBuffer(logEntry);
+		buffer.append(" "); //$NON-NLS-1$
+		buffer.append(path);
+		append(buffer.toString());
+		
+		addPath(path);
+	}
+
+	/**
+	 * Close any open recovery log
+	 */
+	public void close(String logEntry) throws CoreException {
+		
+		if (nbOfOpen>0){
+			UpdateCore.warn("Close nested Error/Recovery log #"+nbOfOpen+":"+logEntry);			 //$NON-NLS-1$ //$NON-NLS-2$
+			nbOfOpen--;			
+			return;
+		}			
+		
+		UpdateCore.warn("Close Error/Recovery log #"+nbOfOpen+":"+logEntry); //$NON-NLS-1$ //$NON-NLS-2$
+		append(logEntry);
+		if (out != null) {
+			try {
+				out.write(END_OF_FILE);
+				out.flush();
+				out.close();
+			} catch (IOException e) { //eat the exception
+			} finally {
+				out = null;
+				open=false;
+			}
+		}
+	}
+
+	/**
+	 * Delete the file from the file system
+	 */
+	public void delete() {
+		//File logFile = getRecoveryLogFile();
+		getRecoveryLogFile();
+		//if (logFile.exists())
+			//logFile.delete();	
+	}
+
+	/**
+	 * 
+	 */
+	private void addPath(String path){
+		if (paths==null) paths = new ArrayList();
+		paths.add(path);
+	}
+	
+	/*
+	 * creates a Status
+	 */
+	private IStatus createStatus(int statusSeverity, String msg, Exception e){
+		String id =
+			UpdateCore.getPlugin().getBundle().getSymbolicName();
+	
+		StringBuffer completeString = new StringBuffer(""); //$NON-NLS-1$
+		if (msg!=null)
+			completeString.append(msg);
+		if (e!=null){
+			completeString.append("\r\n["); //$NON-NLS-1$
+			completeString.append(e.toString());
+			completeString.append("]\r\n"); //$NON-NLS-1$
+		}
+		return new Status(statusSeverity, id, IStatus.OK, completeString.toString(), e);
+	}	
+	
+	/**
+	 * return a multi status, 
+	 * the children are the file that couldn't be removed
+	 */
+	public IStatus removeFromFileSystem(File file) {
+		
+		IStatus mainStatus = createStatus(IStatus.OK,"",null); //$NON-NLS-1$
+		MultiStatus multi = new MultiStatus(mainStatus.getPlugin(),mainStatus.getCode(),"",null);		 //$NON-NLS-1$
+		
+		if (!file.exists()){
+			multi.add(createStatus(IStatus.ERROR,Messages.ErrorRecoveryLog_noFiletoRemove+file,null)); 
+			return multi;
+		}
+			
+		if (file.isDirectory()) {
+			String[] files = file.list();
+			if (files != null) // be careful since file.list() can return null
+				for (int i = 0; i < files.length; ++i){
+					multi.addAll(removeFromFileSystem(new File(file, files[i])));
+				}
+		}
+		
+		if (!file.delete()) {
+			String msg = "Unable to remove file" +file.getAbsolutePath(); //$NON-NLS-1$ 
+			multi.add(createStatus(IStatus.ERROR,msg,null));
+		}
+		return multi;
+	}	
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ExtendedSite.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ExtendedSite.java
new file mode 100644
index 0000000..b010bab
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ExtendedSite.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.update.internal.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.update.core.IURLEntry;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.internal.model.SiteWithTimestamp;
+
+public class ExtendedSite extends SiteWithTimestamp /*Site*/ {
+	
+	private boolean digestExist;
+	private String[] availableLocals;
+	private String digestURL;
+	private LiteFeature[] liteFeatures;
+	private LiteFeature[] allLiteFeatures;
+	private IURLEntry[] associateSites;
+	private boolean pack200 = false;
+	private IURLEntry selectedMirror;
+	
+	
+	public String getDigestURL() {
+		return digestURL;
+	}
+	public void setDigestURL(String digestURL) {
+		this.digestURL = digestURL;
+	}
+
+	public String[] getAvailableLocals() {
+		return availableLocals;
+	}
+	public void setAvailableLocals(String[] availableLocals) {
+		this.availableLocals = availableLocals;
+	}
+	public boolean isDigestExist() {
+		return digestExist;
+	}
+	public void setDigestExist(boolean digestExist) {
+		this.digestExist = digestExist;
+	}
+	public LiteFeature[] getLiteFeatures() {
+		if (getCurrentConfiguredSite()!=null)
+			return filterFeatures(getNonFilteredLiteFeatures());
+		else 
+			return getNonFilteredLiteFeatures();
+	}
+	public void setLiteFeatures(LiteFeature[] liteFeatures) {
+		
+		if ((liteFeatures == null) || (liteFeatures.length == 0))
+			return;
+		this.allLiteFeatures = liteFeatures;
+		List temp = new ArrayList();
+		for(int i = 0; i < allLiteFeatures.length ; i++) {
+			if (getFeatureReference(allLiteFeatures[i]) != null) {
+				temp.add(allLiteFeatures[i]);
+			}
+		}
+		if (!temp.isEmpty()) {
+			this.liteFeatures = (LiteFeature[])temp.toArray( new LiteFeature[temp.size()]);
+		}
+	}
+	
+	public LiteFeature getLiteFeature(VersionedIdentifier vid) {
+		if (allLiteFeatures == null)
+			return null;
+		
+		for(int i = 0; i < allLiteFeatures.length ; i++) {
+			if (vid.equals(allLiteFeatures[i].getVersionedIdentifier())) {
+				return allLiteFeatures[i];
+			}
+		}
+		return null;
+	}
+	
+	public LiteFeature[] getNonFilteredLiteFeatures() {
+		return liteFeatures;
+	}
+	public void setNonFilteredLiteFeatures(LiteFeature[] liteFeatures) {
+		this.liteFeatures = liteFeatures;
+	}
+	
+	
+	/**
+	 * Get whether or not this site may contain jars packed with pack200.
+	 * The packed version of foo.jar, is expected to be foo.jar.pack.gz
+	 * @return
+	 */
+	public boolean supportsPack200() {
+		return pack200;
+	}
+	
+	/**
+	 * Set whether or not this site may contain jars packed with pack200
+	 * The packed version of foo.jar is expected to be foo.jar.pack.gz
+	 * @param pack
+	 */
+	public void setSupportsPack200(boolean pack){
+		pack200 = pack;
+	}
+	
+	/*
+	 * Method filterFeatures.
+	 * Also implemented in Feature
+	 *  
+	 * @param list
+	 * @return List
+	 */
+	private LiteFeature[] filterFeatures(LiteFeature[] allIncluded) {
+		List list = new ArrayList();
+		if (allIncluded!=null){
+			for (int i = 0; i < allIncluded.length; i++) {
+				LiteFeature included = allIncluded[i];
+				if (UpdateManagerUtils.isValidEnvironment(included))
+					list.add(included);
+				else{
+					if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS){
+						UpdateCore.warn("Filtered out feature reference:"+included); //$NON-NLS-1$
+					}
+				}
+			}
+		}
+		
+		LiteFeature[] result = new LiteFeature[list.size()];
+		if (!list.isEmpty()){
+			list.toArray(result);
+		}
+		
+		return result;	
+	}
+	public IURLEntry[] getAssociateSites() {
+		return associateSites;
+	}
+	public void setAssociateSites(IURLEntry[] associateSites) {
+		this.associateSites = associateSites;
+	}
+	
+	public IURLEntry getSelectedMirror() {
+		return selectedMirror;
+	}
+	
+	public void setSelectedMirror(IURLEntry selectedMirror) {
+		this.selectedMirror = selectedMirror;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ExtendedSiteURLFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ExtendedSiteURLFactory.java
new file mode 100644
index 0000000..bea3981
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ExtendedSiteURLFactory.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import org.eclipse.update.core.model.SiteModel;
+
+public class ExtendedSiteURLFactory extends SiteURLFactory {
+	
+	public SiteModel createSiteMapModel() {
+		return new ExtendedSite();
+	}
+
+	
+    //public SiteFeatureReferenceModel createFeatureReferenceModel() {
+    //    return new UpdateSiteLiteFeatureReference();
+    //}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FatalIOException.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FatalIOException.java
new file mode 100644
index 0000000..b82ee06
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FatalIOException.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import java.io.IOException;
+
+public class FatalIOException extends IOException {
+	private static final long serialVersionUID = 7690318087505479039L;
+
+	public FatalIOException(String string) {
+		super(string);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureContentConsumer.java
new file mode 100644
index 0000000..278de69
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureContentConsumer.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+
+/**
+ * A default implementation for IFeatureContentConsumer
+ * </p>
+ * @since 2.0
+ */
+
+public abstract class FeatureContentConsumer extends ModelObject
+	implements IFeatureContentConsumer {
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureDownloadException.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureDownloadException.java
new file mode 100644
index 0000000..cef1d61
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureDownloadException.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Exception thrown when IOException during downloading features
+ * 
+ * @since 3.0
+ */
+public class FeatureDownloadException extends CoreException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+	 * Construct the exception indicating enclosing CoreException
+	 * 
+	 * @since 3.0
+	 */
+	public FeatureDownloadException(String msg, Exception e) {
+		super(
+			new Status(
+				IStatus.INFO,
+				"org.eclipse.update.core", //$NON-NLS-1$
+				IStatus.OK,
+				msg,
+				e));
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureEntryWrapper.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureEntryWrapper.java
new file mode 100644
index 0000000..da1eca5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureEntryWrapper.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import org.eclipse.core.runtime.IProduct;
+import org.eclipse.update.internal.configurator.FeatureEntry;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class used to wrapper feature entry objects. This class is necessary
+ * to remove the depedancy of the org.eclipse.update.configurator on 
+ * the org.eclipse.core.runtime bundle.
+ *
+ * @since 3.2
+ */
+public class FeatureEntryWrapper implements IProduct {
+
+	private FeatureEntry entry;
+
+	/*
+	 * Constructor for the class.
+	 */
+	public FeatureEntryWrapper(FeatureEntry entry) {
+		super();
+		this.entry = entry;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProduct#getApplication()
+	 */
+	public String getApplication() {
+		return entry.getApplication();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProduct#getName()
+	 */
+	public String getName() {
+		return entry.getName();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProduct#getDescription()
+	 */
+	public String getDescription() {
+		return entry.getDescription();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProduct#getId()
+	 */
+	public String getId() {
+		return entry.getId();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProduct#getProperty(java.lang.String)
+	 */
+	public String getProperty(String key) {
+		return entry.getProperty(key);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProduct#getDefiningBundle()
+	 */
+	public Bundle getDefiningBundle() {
+		return entry.getDefiningBundle();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableContentConsumer.java
new file mode 100644
index 0000000..8226d87
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableContentConsumer.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+ 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.IContentConsumer;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureContentConsumer;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.INonPluginEntry;
+import org.eclipse.update.core.IPluginEntry;
+
+/**
+ * ContentConsumer Implementation for a FeatureExecutable
+ */
+
+public class FeatureExecutableContentConsumer extends FeatureContentConsumer {
+
+	private IFeature feature;
+	private boolean closed= false;
+	private boolean aborted= false;	
+	private ISiteContentConsumer contentConsumer;
+	private IFeatureContentConsumer parent = null;
+	private List /* of IFeatureContentCOnsumer */ children;
+
+	/*
+	 * @see IContentConsumer#open(INonPluginEntry)
+	 */
+	public IContentConsumer open(INonPluginEntry nonPluginEntry)
+		throws CoreException {
+		ContentConsumer cons = new NonPluginEntryContentConsumer(
+			getContentConsumer().open(nonPluginEntry));
+		return cons;
+	}
+
+	/*
+	 * @see IContentConsumer#open(IPluginEntry)
+	 */
+	public IContentConsumer open(IPluginEntry pluginEntry) throws CoreException {
+		ContentConsumer cons = new PluginEntryContentConsumer(getContentConsumer().open(pluginEntry));
+		return cons;		
+	}
+
+	/*
+	 * @see IContentConsumer#addChild(IFeature)
+	 */
+	public void addChild(IFeature child) throws CoreException {
+		IFeatureContentConsumer childConsumer = child.getFeatureContentConsumer();
+		childConsumer.setParent(this);
+		if (children==null) children = new ArrayList();
+		children.add(childConsumer);
+		return;
+	}
+
+
+	/*
+	 * @see IFeatureContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor)
+		throws CoreException {
+		getContentConsumer().store(contentReference, monitor);
+	}
+
+	/*
+	 * @see IFeatureContentConsumer#close()
+	 */
+	public IFeatureReference close() throws CoreException {
+		
+		if (!closed && getParent()!=null){
+			closed=true;
+			return null;
+		}
+		
+		// parent consumer, log we are about to rename
+		// log files have been downloaded
+		if (getParent()==null){
+			ErrorRecoveryLog.getLog().append(ErrorRecoveryLog.ALL_INSTALLED);
+		}
+		
+		IFeatureReference ref= null;
+		if (contentConsumer != null)
+			ref = contentConsumer.close();
+		
+		// close nested feature
+		IFeatureContentConsumer[] children = getChildren();
+		for (int i = 0; i < children.length; i++) {
+			children[i].close();
+		}
+							
+		return ref;
+	}
+
+	/*
+	 * @see IFeatureContentConsumer#setFeature(IFeature)
+	 */
+	public void setFeature(IFeature feature) {
+		this.feature= feature;
+	}
+
+	/*
+	 * Sets the parent 
+	 */
+	public void setParent(IFeatureContentConsumer featureContentConsumer) {
+		this.parent= featureContentConsumer;
+	}
+
+	/*
+	 * returns the Content Consumer for the feature
+	 * 
+	 * Right now we are the only one to implement SiteContentConsumer
+	 * Need to be exposed as API post v2.0
+	 */
+	private ISiteContentConsumer getContentConsumer() throws CoreException {
+		if (contentConsumer == null)
+			if (feature.getSite() instanceof SiteFile) {
+				SiteFile site= (SiteFile) feature.getSite();
+				contentConsumer= site.createSiteContentConsumer(feature);
+			} else {
+				throw new UnsupportedOperationException();
+			}
+		return contentConsumer;
+	}
+
+	/*
+	 * @see IFeatureContentConsumer#abort()
+	 */
+	public void abort() throws CoreException {
+
+		if (aborted) return;
+		
+		IFeatureContentConsumer[] children = getChildren();
+		for (int i = 0; i < children.length; i++) {
+			try {
+			children[i].abort();
+			} catch (Exception e){
+				//do Nothing
+			}
+		}
+
+		// do not close plugin and non plugin content consumer
+		// the contentConsumer will abort them
+		// we do not need to abort the NonPluginEntryContentConsumer and PluginEntryContentConsume
+
+		//implement the cleanup
+		if (contentConsumer!=null)
+			contentConsumer.abort();
+		
+		aborted = true;
+	}
+
+	/*
+	 * @see IFeatureContentConsumer#getFeature()
+	 */
+	public IFeature getFeature(){
+		return feature;
+	}
+
+	/*
+	 * @see IFeatureContentConsumer#getParent()
+	 */
+	public IFeatureContentConsumer getParent(){
+		return parent;
+	}
+
+	/*
+	 * @see IFeatureContentConsumer#getChildren()
+	 */
+	public IFeatureContentConsumer[] getChildren(){
+		if (children==null || children.size() == 0)
+			return new IFeatureContentConsumer[0];
+
+		return (IFeatureContentConsumer[]) children.toArray(arrayTypeFor(children));
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableContentProvider.java
new file mode 100644
index 0000000..0bf4c71
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableContentProvider.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+
+/**
+ * Default implementation of a Executable Feature Content Provider
+ */
+
+public class FeatureExecutableContentProvider extends FeatureContentProvider {
+
+	/*
+	 * Constructor 
+	 */
+	public FeatureExecutableContentProvider(URL url) {
+		super(url);
+	}
+
+	/*
+	 * Return the path for a pluginEntry
+	 */
+	private String getPath(IPluginEntry pluginEntry)
+		throws IOException, CoreException {
+
+		// get the URL of the Archive file that contains the plugin entry
+		ISiteContentProvider provider = getFeature().getSite().getSiteContentProvider();
+		URL fileURL = provider.getArchiveReference(getPathID(pluginEntry));
+		String result = fileURL.getFile();
+
+		if (!result.endsWith(".jar") && !result.endsWith("/") && !result.endsWith(File.separator)) //$NON-NLS-1$ //$NON-NLS-2$
+			result += File.separator;
+		File pluginPath = new File(result);
+		if (!pluginPath.exists())
+			throw new IOException(
+				NLS.bind(Messages.FeatureExecutableContentProvider_FileDoesNotExist, (new String[] { result })));
+
+		return result;
+	}
+
+	/*
+	 * Returns the path for the Feature
+	 */
+	private String getFeaturePath() throws IOException {
+		String result = getFeature().getURL().getFile();
+
+		// return the list of all subdirectories
+		if (!(result.endsWith(File.separator) || result.endsWith("/"))) //$NON-NLS-1$
+			result += File.separator;
+		File pluginDir = new File(result);
+		if (!pluginDir.exists())
+			throw new IOException(
+				NLS.bind(Messages.FeatureExecutableContentProvider_FileDoesNotExist, (new String[] { result })));
+
+		return result;
+	}
+
+	/*
+	 * Returns all the files under the directory
+	 * Recursive call
+	 */
+	private List getFiles(File dir) throws IOException {
+		List result = new ArrayList();
+
+		if (!dir.isDirectory()) {
+			String msg =
+				NLS.bind(Messages.FeatureExecutableContentProvider_InvalidDirectory, (new String[] { dir.getAbsolutePath() }));
+
+			throw new IOException(msg);
+
+		}
+
+		File[] files = dir.listFiles();
+		if (files != null)
+			for (int i = 0; i < files.length; ++i) {
+				if (files[i].isDirectory()) {
+					result.addAll(getFiles(files[i]));
+				} else {
+					result.add(files[i]);
+				}
+			}
+
+		return result;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getVerifier()
+	 */
+	public IVerifier getVerifier() throws CoreException {
+		return null;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getFeatureManifestReference()
+	 */
+	public ContentReference getFeatureManifestReference(InstallMonitor monitor)
+		throws CoreException {
+		ContentReference result = null;
+		try {
+			result = new ContentReference(Feature.FEATURE_XML, new URL(getURL(), Feature.FEATURE_XML));
+
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.FeatureExecutableContentProvider_UnableToCreateURLFor, (new String[] { getURL().toExternalForm() + " " + Feature.FEATURE_XML })), //$NON-NLS-1$
+				e);
+		}
+		return result;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getArchiveReferences()
+	 */
+	public ContentReference[] getArchiveReferences(InstallMonitor monitor)
+		throws CoreException {
+		// executable feature does not contain archives
+		return new ContentReference[0];
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getPluginEntryArchiveReferences(IPluginEntry)
+	 */
+	public ContentReference[] getPluginEntryArchiveReferences(
+		IPluginEntry pluginEntry,
+		InstallMonitor monitor)
+		throws CoreException {
+		ContentReference[] result = new ContentReference[1];
+		String archiveID = getPathID(pluginEntry);
+		try {
+			File archiveFile = new File(getPath(pluginEntry));
+			if(!archiveFile.isDirectory() && archiveFile.getName().endsWith(".jar")){ //$NON-NLS-1$
+				result[0] = new JarContentReference(archiveID, archiveFile);				
+			} else {
+				result[0] =
+					new ContentReference(archiveID, archiveFile);
+			}
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+					NLS.bind(Messages.FeatureExecutableContentProvider_UnableToRetrievePluginEntry, (new String[] { pluginEntry.getVersionedIdentifier().toString() })),
+					e);
+		}
+		return result;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getNonPluginEntryArchiveReferences(INonPluginEntry)
+	 */
+	public ContentReference[] getNonPluginEntryArchiveReferences(
+		INonPluginEntry nonPluginEntry,
+		InstallMonitor monitor)
+		throws CoreException {
+
+		ContentReference[] result = new ContentReference[1];
+		URL fileURL;
+
+		//try {
+		// get the URL of the Archive file that contains the plugin entry
+		ISiteContentProvider provider = getFeature().getSite().getSiteContentProvider();
+		fileURL = provider.getArchiveReference(getPathID(nonPluginEntry));
+
+		String fileString = fileURL.getFile();
+		File nonPluginData = new File(fileString);
+		if (!nonPluginData.exists())
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.FeatureExecutableContentProvider_FileDoesNotExist, (new String[] { fileString })), 
+				null); 
+
+		try {
+			result[0] =
+				new ContentReference(nonPluginEntry.getIdentifier(), nonPluginData.toURL());
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.FeatureExecutableContentProvider_UnableToRetrieveNonPluginEntry, (new String[] { nonPluginEntry.getIdentifier().toString() })),
+				e);
+		}
+		return result;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getFeatureEntryArchiveReferences()
+	 */
+	public ContentReference[] getFeatureEntryArchiveReferences(InstallMonitor monitor)
+		throws CoreException {
+		ContentReference[] contentReferences = new ContentReference[1];
+		contentReferences[0] = new ContentReference(null, getURL());
+		return contentReferences;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getFeatureEntryArchivesContentReferences()
+	 */
+	public ContentReference[] getFeatureEntryContentReferences(InstallMonitor monitor)
+		throws CoreException {
+		ContentReference[] result = new ContentReference[0];
+		try {
+			File featureDir = new File(getFeaturePath());
+			List files = getFiles(featureDir);
+			result = new ContentReference[files.size()];
+			for (int i = 0; i < result.length; i++) {
+				File currentFile = (File) files.get(i);
+				result[i] = new ContentReference(currentFile.getName(), currentFile.toURL());
+			}
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.FeatureExecutableContentProvider_UnableToRetrieveFeatureEntry, (new String[] { getFeature().getVersionedIdentifier().toString() })),
+				e);
+		}
+		return result;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getPluginEntryContentReferences(IPluginEntry)
+	 */
+	public ContentReference[] getPluginEntryContentReferences(
+		IPluginEntry pluginEntry,
+		InstallMonitor monitor)
+		throws CoreException {
+
+		ContentReference[] references = getPluginEntryArchiveReferences(pluginEntry, monitor);
+		ContentReference[] result = new ContentReference[0];
+
+		try {
+			if (references[0] instanceof JarContentReference) {
+				result = ((JarContentReference)references[0]).peek(null, monitor);
+			} else {
+				// return the list of all subdirectories
+				File pluginDir = new File(getPath(pluginEntry));
+				URL pluginURL = pluginDir.toURL();
+				List files = getFiles(pluginDir);
+				result = new ContentReference[files.size()];
+				for (int i = 0; i < result.length; i++) {
+					File currentFile = (File) files.get(i);
+					String relativeString = UpdateManagerUtils.getURLAsString(pluginURL, currentFile.toURL());
+					result[i] = new ContentReference(relativeString, currentFile.toURL());
+				}
+			}
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+				Messages.FeatureExecutableContentProvider_UnableToRetriveArchiveContentRef
+					+ pluginEntry.getVersionedIdentifier().toString(),
+				e);
+		}
+		
+		//[20866] we did not preserve executable bit
+		validatePermissions(result);
+		
+		return result;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableFactory.java
new file mode 100644
index 0000000..170a688
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureExecutableFactory.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.BaseFeatureFactory;
+import org.eclipse.update.core.Feature;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureContentConsumer;
+import org.eclipse.update.core.IFeatureContentProvider;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.model.FeatureModel;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+
+/**
+ * FeatureFactory for Executable Features
+ */
+public class FeatureExecutableFactory extends BaseFeatureFactory {
+
+	/*
+	 * @see IFeatureFactory#createFeature(URL,ISite,IProgressMonitor)
+	 */
+	public IFeature createFeature(URL url, ISite site, IProgressMonitor monitor) throws CoreException {
+
+		TargetFeature feature = null;
+		InputStream featureStream = null;
+		if (monitor == null)
+			monitor = new NullProgressMonitor();
+
+		if (url == null)
+			return createFeature(site);
+
+		// the URL should point to a directory
+		url = validate(url);
+
+		try {
+			IFeatureContentProvider contentProvider = new FeatureExecutableContentProvider(url);
+//			// PERF: Do not create FeatureContentConsumer
+// bug 79893
+			IFeatureContentConsumer contentConsumer =new FeatureExecutableContentConsumer();
+
+			URL nonResolvedURL = contentProvider.getFeatureManifestReference(null).asURL();
+			URL resolvedURL = URLEncoder.encode(nonResolvedURL);
+			featureStream = ConnectionFactory.get(resolvedURL).getInputStream();
+
+			feature = (TargetFeature) this.parseFeature(featureStream, resolvedURL.toExternalForm());
+			monitor.worked(1);
+			feature.setSite(site);
+
+			feature.setFeatureContentProvider(contentProvider);
+//			// PERF: FeatureContentConsumer
+// bug 79893
+			feature.setContentConsumer(contentConsumer);
+
+			feature.resolve(url, url);
+			feature.markReadOnly();
+		} catch (CoreException e) {
+			throw e;
+		} catch (Exception e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.FeatureFactory_CreatingError, (new String[] { url.toExternalForm() })), e);
+		} finally {
+			try {
+				if (featureStream != null)
+					featureStream.close();
+			} catch (IOException e) {
+			}
+		}
+		return feature;
+	}
+
+	/*
+	 * @see FeatureModelFactory#createFeatureModel()
+	 */
+	public FeatureModel createFeatureModel() {
+		return new TargetFeature();
+	}
+
+	/*
+	 * Creates an empty feature on the site 
+	 */
+	private IFeature createFeature(ISite site) throws CoreException {
+		TargetFeature feature = null;
+
+		IFeatureContentProvider contentProvider = new FeatureExecutableContentProvider(null);
+		IFeatureContentConsumer contentConsumer = new FeatureExecutableContentConsumer();
+		feature = (TargetFeature) createFeatureModel();
+		feature.setSite(site);
+		feature.setFeatureContentProvider(contentProvider);
+		feature.setContentConsumer(contentConsumer);
+
+		// do not mark read only yet...	
+		return feature;
+	}
+
+	/*
+	 * validates a URL as a directory URL
+	 */
+	private URL validate(URL url) throws CoreException {
+
+		if (url == null)
+			throw Utilities.newCoreException(Messages.FeatureExecutableFactory_NullURL, null); 
+
+		if (!(url.getFile().endsWith("/") || url.getFile().endsWith(File.separator) || url.getFile().endsWith(Feature.FEATURE_XML))) { //$NON-NLS-1$
+			try {
+				String path = url.getFile() + "/"; //$NON-NLS-1$
+				url = new URL(url.getProtocol(), url.getHost(), url.getPort(), path);
+			} catch (MalformedURLException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.FeatureExecutableFactory_CannotCreateURL, (new String[] { url.toExternalForm() })), e);
+			}
+		}
+		return url;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePackagedContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePackagedContentProvider.java
new file mode 100644
index 0000000..03fc180
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePackagedContentProvider.java
@@ -0,0 +1,466 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import org.eclipse.update.core.FeatureContentProvider;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.signedcontent.SignedContentFactory;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.jarprocessor.JarProcessor;
+import org.eclipse.update.internal.jarprocessor.Utils;
+import org.eclipse.update.internal.security.JarVerifier;
+import org.eclipse.update.internal.verifier.CertVerifier;
+
+/**
+ * Content Provider of a Feature Package
+ */
+public class FeaturePackagedContentProvider extends FeatureContentProvider {
+
+	private ContentReference localManifest = null;
+	private ContentReference[] localFeatureFiles = new ContentReference[0];
+	private IVerifier jarVerifier = null;
+	private ExtendedSite siteModel = null;
+	private boolean continueOnError;
+	/*
+	 * filter for file with .jar
+	 */
+	public static final FilenameFilter filter = new FilenameFilter() {
+		public boolean accept(File dir, String name) {
+			return name.endsWith(FeatureContentProvider.JAR_EXTENSION);
+		}
+	};
+
+	/*
+	 * Constructor
+	 */
+	public FeaturePackagedContentProvider(URL url, ISite site) {
+		super(url);
+		if (site instanceof ExtendedSite) {
+			this.siteModel = (ExtendedSite) site;
+		}
+	}
+	
+	/*
+	 * Returns a new verifier for each top-level install
+	 * (if the verifier has a parent, return the verifier
+	 * otherwise reinitialize)
+	 */
+	public IVerifier getVerifier() throws CoreException {
+		SignedContentFactory factory = UpdateCore.getPlugin().getSignedContentFactory();
+		if (jarVerifier == null || jarVerifier.getParent() == null) {
+			if (factory != null)
+				jarVerifier = new CertVerifier(UpdateCore.getPlugin().getSignedContentFactory());
+			else
+				jarVerifier = new JarVerifier();
+			return jarVerifier;
+		}
+
+		// re-init will be done if the parent changes
+		return jarVerifier;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getFeatureManifestReference()
+	 */
+	public ContentReference getFeatureManifestReference(InstallMonitor monitor) throws CoreException {
+
+		// check to see if we already have local copy of the manifest
+		if (localManifest != null)
+			return localManifest;
+		ContentReference[] featureArchiveReference = getFeatureEntryArchiveReferences(monitor);
+		JarContentReference featureJarReference = null;
+		try {
+
+			// force feature archive to local.
+			// This content provider always assumes exactly 1 archive file (index [0])		
+			featureJarReference = (JarContentReference) asLocalReference(featureArchiveReference[0], null);
+			// we need to unpack archive locally for UI browser references to be resolved correctly
+			localFeatureFiles = featureJarReference.unpack(getWorkingDirectory(), null, monitor);
+		} catch (IOException e) {
+			throw errorRetrieving(Feature.FEATURE_XML, featureJarReference, e); 
+		}
+
+		// find the manifest in the unpacked feature files
+		for (int i = 0; i < localFeatureFiles.length; i++) {
+			if (localFeatureFiles[i].getIdentifier().equals(Feature.FEATURE_XML)) {
+				localManifest = localFeatureFiles[i];
+				// cache reference to manifest
+				return localManifest;
+			}
+		}
+
+		// the manifest has not been found
+		String[] values = new String[] { Feature.FEATURE_XML, getURL().toExternalForm()};
+		throw Utilities.newCoreException(NLS.bind(Messages.FeaturePackagedContentProvider_NoManifestFile, values), new Exception()); 
+
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getArchiveReferences()
+	 */
+	public ContentReference[] getArchiveReferences(InstallMonitor monitor) throws CoreException {
+
+		IPluginEntry[] entries = getFeature().getPluginEntries();
+		INonPluginEntry[] nonEntries = getFeature().getNonPluginEntries();
+		List listAllContentRef = new ArrayList();
+		ContentReference[] allContentRef = new ContentReference[0];
+
+		// feature
+		listAllContentRef.addAll(Arrays.asList(getFeatureEntryArchiveReferences(monitor)));
+
+		// plugins
+		for (int i = 0; i < entries.length; i++) {
+			listAllContentRef.addAll(Arrays.asList(getPluginEntryArchiveReferences(entries[i], monitor)));
+		}
+
+		// non plugins
+		for (int i = 0; i < nonEntries.length; i++) {
+			listAllContentRef.addAll(Arrays.asList(getNonPluginEntryArchiveReferences(nonEntries[i], monitor)));
+		}
+
+		// transform List in Array
+		if (listAllContentRef.size() > 0) {
+			allContentRef = new ContentReference[listAllContentRef.size()];
+			listAllContentRef.toArray(allContentRef);
+		}
+
+		return allContentRef;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getFeatureEntryArchiveReferences()
+	 */
+	public ContentReference[] getFeatureEntryArchiveReferences(InstallMonitor monitor) throws CoreException {
+
+		//1 jar file <-> 1 feature
+		// we will return the JAR file 
+		ContentReference[] references = new ContentReference[1];
+		ContentReference currentReference = null;
+		String archiveID = null;
+
+		try {
+			archiveID = (getFeature() != null) ? getFeature().getVersionedIdentifier().toString() : "";	//$NON-NLS-1$
+			currentReference = new JarContentReference(archiveID, getURL());
+			currentReference = asLocalReference(currentReference, monitor);
+			references[0] = currentReference;
+		} catch (IOException e) {
+			references[0] = continueOnErrorOrRethrow(archiveID, e); 
+		}		
+		return references;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getPluginEntryArchiveReferences(IPluginEntry)
+	 */
+	public ContentReference[] getPluginEntryArchiveReferences(IPluginEntry pluginEntry, InstallMonitor monitor) throws CoreException {
+
+		// 1 plugin <-> 1 jar
+		// we return the JAR file	
+		ContentReference[] references = new ContentReference[1];
+		String archiveID = getPathID(pluginEntry);
+		ISite site = (getFeature() == null) ? null : getFeature().getSite();
+		ISiteContentProvider siteContentProvider = (site == null) ? null : site.getSiteContentProvider();
+		URL url = (siteContentProvider == null) ? null : siteContentProvider.getArchiveReference(archiveID);
+
+		try {
+			references[0] = retrieveLocalJar(new JarContentReference(archiveID, url), monitor);
+		} catch (IOException e) {
+			references[0] = continueOnErrorOrRethrow(archiveID, e);
+		}
+		return references;
+	}
+
+	private ContentReference retrieveLocalJar(JarContentReference reference, InstallMonitor monitor) throws IOException, CoreException {
+		//If the site does not support pack200, just get the jar as normal
+		if(siteModel == null || !siteModel.supportsPack200() || !JarProcessor.canPerformUnpack()) {
+			ContentReference contentReference = null;
+			try {
+				contentReference = asLocalReference(reference, monitor);
+			}
+			catch (FileNotFoundException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			catch (IOException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			catch (CoreException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			return contentReference;
+		}
+		
+		ContentReference packedRef = null;
+		String key = reference.toString();
+		Object jarLock = LockManager.getLock(key);
+		synchronized (jarLock) {
+			//do we have this jar already?
+			File localFile = Utilities.lookupLocalFile(key);
+			if (localFile != null) {
+				// check if the cached file is still valid (no newer version on server)
+				if (UpdateManagerUtils.isSameTimestamp(reference.asURL(), localFile.lastModified())) {
+					LockManager.returnLock(key);
+					return reference.createContentReference(reference.getIdentifier(), localFile);
+				}
+			}
+
+			try {
+				//don't have jar, check for pack.gz
+				URL packGZURL = new URL(reference.asURL().toExternalForm() + ".pack.gz"); //$NON-NLS-1$
+				packedRef = asLocalReference(new JarContentReference(reference.getIdentifier(), packGZURL), monitor);
+			} catch (IOException e) {
+				//no pack.gz
+			} catch (CoreException e){
+				//no pack.gz
+			}
+		}
+		
+		if (packedRef == null) {
+			//no pack.gz on server, get normal jar
+			ContentReference contentReference = null;
+			try {
+				contentReference = asLocalReference(reference, monitor);
+			}
+			catch (FileNotFoundException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			catch (IOException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			catch (CoreException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			return contentReference;
+		}
+
+		boolean success = false;
+		synchronized (jarLock) {
+			String packed = packedRef.toString();
+			Object packedLock = LockManager.getLock(packed);
+			synchronized (packedLock) {
+				try {
+					File tempFile = packedRef.asFile();
+					long timeStamp = tempFile.lastModified();
+	
+					JarProcessor processor = JarProcessor.getUnpackProcessor(null);
+					processor.setWorkingDirectory(tempFile.getParent());
+	
+					File packedFile = new File(tempFile.toString() + Utils.PACKED_SUFFIX);
+					tempFile.renameTo(packedFile);
+					
+					if (monitor != null) {
+						monitor.saveState();
+						monitor.subTask(Messages.JarContentReference_Unpacking + " " + reference.getIdentifier() + Utils.PACKED_SUFFIX);  //$NON-NLS-1$
+						monitor.showCopyDetails(false);
+					}
+					//unpacking the jar will strip the ".pack.gz" and leave us back with the original filename
+					try {
+						processor.processJar(packedFile);
+					} catch (Throwable e) {
+						//something is wrong unpacking
+					}
+	
+					if(tempFile.exists() && tempFile.length() > 0){
+						success = true;
+						tempFile.setLastModified(timeStamp);
+						Utilities.mapLocalFile(key, tempFile);
+						UpdateCore.getPlugin().getUpdateSession().markVisited(reference.asURL());
+					}
+				} finally {
+					LockManager.returnLock(packed);
+					LockManager.returnLock(key);
+					if(monitor != null)
+						monitor.restoreState();
+				}
+			}
+		}
+		if(!success){
+			//Something went wrong with the unpack, get the normal jar.
+			ContentReference contentReference = null;
+			try {
+				contentReference = asLocalReference(reference, monitor);
+			}
+			catch (FileNotFoundException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			catch (IOException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			catch (CoreException e) {
+				contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
+			}
+			return contentReference;
+		}
+		return packedRef;
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getNonPluginEntryArchiveReferences(INonPluginEntry)
+	 */
+	public ContentReference[] getNonPluginEntryArchiveReferences(INonPluginEntry nonPluginEntry, InstallMonitor monitor) throws CoreException {
+
+		// archive = feature/<id>_<ver>/<file>
+		String archiveID = Site.DEFAULT_FEATURE_PATH + ((getFeature() != null) ? getFeature().getVersionedIdentifier().toString() : ""); //$NON-NLS-1$
+		archiveID += "/" + nonPluginEntry.getIdentifier(); //$NON-NLS-1$
+
+		ContentReference[] references = new ContentReference[1];
+		ContentReference currentReference = null;
+
+		try {
+			ISite site = (getFeature() == null) ? null : getFeature().getSite();
+			ISiteContentProvider siteContentProvider = (site == null) ? null : site.getSiteContentProvider();
+			URL url = (siteContentProvider == null) ? null : siteContentProvider.getArchiveReference(archiveID);
+
+			currentReference = new ContentReference(nonPluginEntry.getIdentifier(), url);
+			currentReference = asLocalReference(currentReference, monitor);
+			references[0] = currentReference;
+
+		} catch (IOException e) {
+			references[0] = continueOnErrorOrRethrow(archiveID, e);
+		}
+
+		return references;
+	}
+
+
+	/*
+	 * @see IFeatureContentProvider#getFeatureEntryContentReferences()
+	 */
+	public ContentReference[] getFeatureEntryContentReferences(InstallMonitor monitor) throws CoreException {
+
+		return localFeatureFiles; // return cached feature references
+		// Note: assumes this content provider is always called first to
+		//       get the feature manifest. This forces the feature files
+		//       to be unpacked and caches the references
+	}
+
+	/*
+	 * @see IFeatureContentProvider#getPluginEntryContentReferences(IPluginEntry)
+	 */
+	public ContentReference[] getPluginEntryContentReferences(IPluginEntry pluginEntry, InstallMonitor monitor) throws CoreException {
+
+		ContentReference[] references = getPluginEntryArchiveReferences(pluginEntry, monitor);
+		ContentReference[] pluginReferences = new ContentReference[0];
+
+		try {
+			if (references[0] instanceof JarContentReference) {
+				JarContentReference localRef = (JarContentReference) asLocalReference(references[0], monitor);
+				pluginReferences = localRef.peek(null, monitor);
+			} else {
+				// return the list of all subdirectories
+				List files = getFiles(references[0].asFile());
+				pluginReferences = new ContentReference[files.size()];
+				for (int i = 0; i < pluginReferences.length; i++) {
+					File currentFile = (File) files.get(i);
+					pluginReferences[i] = new ContentReference(null, currentFile.toURL());
+				}
+			}
+
+			//[20866] we did not preserve executable bit
+			validatePermissions(pluginReferences);
+
+		} catch (IOException e) {
+			throw errorRetrieving(pluginEntry.getVersionedIdentifier().toString(), references[0], e);
+		}
+		return pluginReferences;
+	}
+
+	/*
+	 * return all the files under the directory
+	 */
+	private List getFiles(File dir) throws IOException {
+		List result = new ArrayList();
+
+		if (!dir.isDirectory())
+			throw new IOException(NLS.bind(Messages.FeaturePackagedContentProvider_InvalidDirectory, (new String[] { dir.getPath() })));
+
+		File[] files = dir.listFiles();
+		if (files != null) // be careful since it can be null
+			for (int i = 0; i < files.length; ++i) {
+				if (files[i].isDirectory()) {
+					result.addAll(getFiles(files[i]));
+				} else {
+					result.add(files[i]);
+				}
+			}
+		return result;
+	}
+
+	/*
+	 * 
+	 */
+	private CoreException errorRetrieving(String obj, ContentReference archive, Exception e) {
+
+		String[] values = new String[] { obj };
+
+		return Utilities.newCoreException(NLS.bind(Messages.FeaturePackagedContentProvider_ErrorRetrieving, values), e);	 	
+
+	}
+
+
+	public void setContinueOnError(boolean continueOnError) {
+		this.continueOnError = continueOnError;
+	}
+	
+	/** 
+	 * This method is used for when a core exception is detected, so, if its decided to rethrow, then 
+	 * a core exception odes not have to be recreated. 
+	 * 
+	 * @param archiveID id of the archive file
+	 * @param CoreException 
+	 * @return NullReference if its decided not to continue
+	 * @throws CoreException
+	 */
+	/*private ContentReference continueOrErrorOrRethrow(String archiveID, CoreException coreException) throws CoreException {
+		ContentReference reference = null;
+
+		if (continueOnError) {
+			// this ContentReference without a file or URL is purely a
+			// "missing jar" reference.
+			reference = new NullContentReference(archiveID);
+
+			String msg = "    ContinueOnError: The following ID was not found, so was skipped, and is not on miror site: " + archiveID; //$NON-NLS-1$
+			String id = UpdateCore.getPlugin().getBundle().getSymbolicName();
+			IStatus status = new Status(IStatus.WARNING, id , 0, msg, null);
+			UpdateCore.log(status);
+			
+		}
+		else {
+			throw coreException;
+		}
+		return reference;
+	}*/
+	
+	private ContentReference continueOnErrorOrRethrow(String archiveID, Exception e) throws CoreException {
+		ContentReference reference = null;
+
+		if (continueOnError) { 
+			// this ContentReference without a file or URL is purely a
+			// "missing jar" reference.
+			reference = new NullContentReference(archiveID);
+			
+			String msg = "    ContinueOnError: The following ID was not found, so was skipped, and is not on miror site: " + archiveID; //$NON-NLS-1$
+			String id = UpdateCore.getPlugin().getBundle().getSymbolicName();
+			IStatus status = new Status(IStatus.WARNING, id , 0, msg, null);
+			UpdateCore.log(status);	
+			
+		}
+		else {
+			throw errorRetrieving(archiveID, reference, e);
+		}
+		return reference;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePackagedFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePackagedFactory.java
new file mode 100644
index 0000000..fc55651
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePackagedFactory.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+import java.io.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+
+/**
+ * Factory for Feature Packaged
+ */
+public class FeaturePackagedFactory extends BaseFeatureFactory {
+
+	/*
+	 * @see IFeatureFactory#createFeature(URL,ISite,IProgressMonitor)
+	 */
+	public IFeature createFeature(URL url,ISite site, IProgressMonitor monitor) throws CoreException {
+		Feature feature = null;
+		InputStream featureStream = null;
+		if (monitor == null)
+			monitor = new NullProgressMonitor();
+		monitor.beginTask(null,2);
+		monitor.worked(1);
+			
+					
+		try {	
+			IFeatureContentProvider contentProvider = new FeaturePackagedContentProvider(url, site);	
+			ContentReference manifest = contentProvider.getFeatureManifestReference(null/*IProgressMonitor*/);
+			featureStream = manifest.getInputStream();
+			feature = (Feature)parseFeature(featureStream);
+			monitor.worked(1);
+	
+			// if there is no update URL for the Feature
+			// use the Site URL
+			if (feature.getUpdateSiteEntry()==null){
+				URLEntryModel entryModel = createURLEntryModel();
+				URL siteUrl = site.getURL();
+				if (siteUrl!=null){
+					entryModel.setURLString(siteUrl.toExternalForm());
+					entryModel.resolve(siteUrl,null);
+					feature.setUpdateSiteEntryModel(entryModel);
+				}
+			}	
+			feature.setFeatureContentProvider(contentProvider);
+			feature.setSite(site);						
+			URL baseUrl = null;
+			try {
+				baseUrl = new URL(manifest.asURL(),"."); // make sure we have URL to feature directory //$NON-NLS-1$
+			} catch(MalformedURLException e) {	
+			}
+			feature.resolve(baseUrl, baseUrl);
+			feature.markReadOnly();			
+		}  catch (CoreException e){
+			throw e;
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw Utilities.newCoreException(NLS.bind(Messages.FeatureFactory_CreatingError, (new String[] { url.toExternalForm() })), e);
+		}finally {
+			try {
+				if (featureStream!=null)	
+					featureStream.close();
+			} catch (IOException e) {
+			}
+		}
+		return feature;
+	}
+    
+    public IncludedFeatureReferenceModel createIncludedFeatureReferenceModel() {
+     return new UpdateSiteIncludedFeatureReference();
+    }
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePlugin.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePlugin.java
new file mode 100644
index 0000000..f002ed7
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeaturePlugin.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+import org.eclipse.update.core.*;
+/**
+ * An IPluginEntry - IFeature pair.
+ * The IFeature is a featue or a patch wich delivered the plugin
+ */
+public class FeaturePlugin {
+	private IPluginEntry pluginEntry;
+	private IFeature feature;
+	public FeaturePlugin(IPluginEntry entry, IFeature feature) {
+		pluginEntry = entry;
+		this.feature = feature;
+	}
+	public IPluginEntry getEntry() {
+		return pluginEntry;
+	}
+	public IFeature getFeature() {
+		return feature;
+	}
+	/**
+	 * Plugins are equal if their IDs and versions are the same.
+	 */
+	public boolean equals(Object o) {
+		if (o instanceof FeaturePlugin) {
+			FeaturePlugin p = (FeaturePlugin) o;
+			return getEntry().getVersionedIdentifier().equals(p.getEntry().getVersionedIdentifier());
+		}
+		return false;
+	}
+	public int hashCode() {
+		return getEntry().getVersionedIdentifier().hashCode();
+	}
+	public String toString() {
+		return pluginEntry + " in " + feature; //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureTypeFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureTypeFactory.java
new file mode 100644
index 0000000..56aeac4
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FeatureTypeFactory.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+
+/**
+ * Manages FeatureFactory extension point
+ */
+public final class FeatureTypeFactory {
+
+	private static FeatureTypeFactory inst;
+	private Map factories;
+
+	private static final String SIMPLE_EXTENSION_ID = "featureTypes";	//$NON-NLS-1$	
+
+	/*
+	 * hide constructor
+	 */
+	private FeatureTypeFactory() {
+	}
+
+	/*
+	 * Singleton pattern
+	 */
+	public static FeatureTypeFactory getInstance() {
+		if (inst == null)
+			inst = new FeatureTypeFactory();
+		return inst;
+	}
+
+	/*
+	 * return the factory for the associated type
+	 */
+	public IFeatureFactory getFactory(String type) throws CoreException {
+		//
+		Object instance = getFactories().get(type);
+		if (instance == null) {
+			instance = createFactoryFor(type);
+			getFactories().put(type, instance);
+		}
+		return (IFeatureFactory) instance;
+	}
+
+	/*
+	 * creates a factory for the associated type and cache it 
+	 */
+	private IFeatureFactory createFactoryFor(String type) throws CoreException {
+		IFeatureFactory result = null;
+
+		String pluginID =
+			UpdateCore.getPlugin().getBundle().getSymbolicName();
+		IExtensionRegistry registry = Platform.getExtensionRegistry();
+		IConfigurationElement[] elements =
+			registry.getConfigurationElementsFor(pluginID, SIMPLE_EXTENSION_ID, type);
+
+		if (elements == null || elements.length == 0) {
+			throw Utilities.newCoreException(
+					NLS.bind(Messages.FeatureTypeFactory_UnableToFindFeatureFactory, (new String[] { type })),
+					null);
+		} 
+
+		IConfigurationElement element = elements[0];
+		result = (IFeatureFactory) element.createExecutableExtension("class");	//$NON-NLS-1$
+		return result;
+	}
+
+	/*
+	 * 
+	 */
+	private Map getFactories() {
+		if (factories == null)
+			factories = new HashMap();
+		return factories;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FileFragment.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FileFragment.java
new file mode 100644
index 0000000..309250f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FileFragment.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+import java.io.*;
+
+
+public class FileFragment{
+	private File file;
+	private long bytes;
+	public FileFragment(File file, long size){
+		this.file=file;
+		this.bytes=size;
+	}
+	public File getFile(){
+		return file;
+	}
+	public long getSize(){
+		return bytes;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FragmentEntry.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FragmentEntry.java
new file mode 100644
index 0000000..2a7511f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/FragmentEntry.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+
+/**
+ * This class is used to retrieve the fragments associated with a plugin.
+ */
+public class FragmentEntry {
+	
+	private String pluginId;
+	private String pluginVersion;
+	private String name; 
+	private String location;
+	
+	public FragmentEntry(String id, String version, String name, String location) {
+		this.pluginId = id;
+		this.pluginVersion = version;
+		this.name = name;
+		this.location = location;
+	}
+
+	/**
+	 * @return the plugin translatable name
+	 */
+	public String getName() {
+		return name;
+	}
+	
+	/**
+	 * @return the location of the plugin
+	 */
+	public String getLocation() {
+		return location;
+	}
+	
+	/**
+	 * Returns the plug-in identifier for this entry.
+	 * 
+	 * @return the plug-in identifier, or <code>null</code>
+	 */
+	public String getPluginIdentifier() {
+		return pluginId;
+	}
+
+	/**
+	 * Returns the plug-in version for this entry.
+	 * 
+	 * @return the plug-in version, or <code>null</code>
+	 */
+	public String getPluginVersion() {
+		return pluginVersion;
+	}
+
+	/**
+	 * @see Object#toString()
+	 */
+	public String toString() {
+		String msg = (getPluginIdentifier()!=null)?getPluginIdentifier().toString():""; //$NON-NLS-1$
+		msg += getPluginVersion()!=null?" "+getPluginVersion().toString():""; //$NON-NLS-1$ //$NON-NLS-2$
+		return msg;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ISiteContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ISiteContentConsumer.java
new file mode 100644
index 0000000..ac8ba7d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ISiteContentConsumer.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+ 
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+ 
+ /**
+  * A site content consumer manages the storage or archives, plugins and
+  * feature inside an <code> ISite</code>
+  */
+ 
+public interface ISiteContentConsumer {
+
+	/**
+	 * Stores a content reference into the SiteContentConsumer
+	 * @param contentReference the content reference to store
+	 * @param monitor the progress monitor
+	 * @throws CoreException if an error occurs storing the content reference
+	 * @since 2.0 
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor) throws CoreException;
+
+	/**
+	 * opens a Non plugin Entry for storage
+	 * @return the new FeatureContentConsumer for this <code>INonPluginEntry</code>
+	 * @throws CoreException if the opens is done on a FeatureContentConsumer parent other than an IFeature.
+	 * @since 2.0 
+	 */
+	public IContentConsumer open(INonPluginEntry nonPluginEntry) throws CoreException;
+
+	/**
+	 * opens a Non plugin Entry for storage
+	 * @return the new FeatureContentConsumer for this <code>IPluginEntry</code>
+	 * @throws CoreException if the opens is done on a FeatureContentConsumer parent other than an IFeature.
+	 * @since 2.0 
+	 */
+	public IContentConsumer open(IPluginEntry pluginEntry) throws CoreException;
+	
+	/**
+	 * closes the opened SiteContentConsumer
+	 * @throws CoreException
+	 * @since 2.0 
+	 */
+	public IFeatureReference close() throws CoreException ;	
+	
+
+	/**
+	 * aborts the opened SiteContentConsumer
+	 * @throws CoreException
+	 * @since 2.0 
+	 */
+	public void abort() throws CoreException;		
+	
+}
+
+
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallConfiguration.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallConfiguration.java
new file mode 100644
index 0000000..c0801dd
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallConfiguration.java
@@ -0,0 +1,974 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import org.eclipse.update.internal.model.SiteLocalModel;
+
+import org.eclipse.core.runtime.ListenerList;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.IActivity;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.configuration.IInstallConfigurationChangedListener;
+import org.eclipse.update.configuration.IProblemHandler;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.FeatureContentProvider;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteContentProvider;
+import org.eclipse.update.core.ISiteFeatureReference;
+import org.eclipse.update.core.Site;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.core.model.SiteModel;
+import org.eclipse.update.internal.configurator.ConfigurationActivator;
+import org.eclipse.update.internal.configurator.FeatureEntry;
+import org.eclipse.update.internal.configurator.PlatformConfiguration;
+import org.eclipse.update.internal.configurator.PluginEntry;
+import org.eclipse.update.internal.configurator.SiteEntry;
+import org.eclipse.update.internal.model.ConfigurationActivityModel;
+import org.eclipse.update.internal.model.ConfiguredSiteModel;
+import org.eclipse.update.internal.model.InstallConfigurationModel;
+import org.osgi.framework.Bundle;
+
+/**
+ * Manages ConfiguredSites
+ *
+ */
+
+public class InstallConfiguration extends InstallConfigurationModel implements IInstallConfiguration {
+	private static boolean isWindows = System.getProperty("os.name").startsWith("Win"); //$NON-NLS-1$ //$NON-NLS-2$
+	private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+
+	/*
+	 * default constructor.
+	 */
+	public InstallConfiguration() {
+	}
+
+	/*
+	 * Copy constructor 
+	 * @since 3.0
+	 */
+	public InstallConfiguration(IInstallConfiguration config) throws MalformedURLException, CoreException {
+		this(config, null, null);
+	}
+	
+	/*
+	 * copy constructor
+	 */
+	public InstallConfiguration(IInstallConfiguration config, URL newLocation, String label) throws CoreException, MalformedURLException {
+		// set current date and timeline as caller can call setDate if the
+		// date on the URL string has to be the same
+		Date now = new Date();
+		setCreationDate(now);
+		setCurrent(false);
+		
+		if (newLocation == null) {
+			String newFileName = UpdateManagerUtils.getLocalRandomIdentifier(SiteLocalModel.CONFIG_FILE, now);
+			newLocation = UpdateManagerUtils.getURL(((LocalSite)SiteManager.getLocalSite()).getLocationURL(), newFileName, null);
+		}
+		setLocationURLString(newLocation.toExternalForm());
+		
+		if (label == null)
+			label = Utilities.format(now);
+		setLabel(label);
+
+		// do not copy list of listeners nor activities
+		// make a copy of the siteConfiguration object
+		if (config != null) {
+			IConfiguredSite[] csites = config.getConfiguredSites();
+			if (csites != null) {
+				for (int i = 0; i < csites.length; i++) {
+					ConfiguredSite configSite = new ConfiguredSite(csites[i]);
+					addConfigurationSiteModel(configSite);
+				}
+			}
+		}
+
+		resolve(newLocation, null);
+		// no need to parse file, all data are initialized
+		initialized = true;
+	}
+	
+
+	/*
+	 * Returns the list of configured sites or an empty array
+	 */
+	public IConfiguredSite[] getConfiguredSites() {
+		ConfiguredSiteModel[] result = getConfigurationSitesModel();
+		if (result.length == 0)
+			return new IConfiguredSite[0];
+		else
+			return (IConfiguredSite[]) result;
+	}
+
+	/*
+	 * Returns the default site policy
+	 */
+	private int getDefaultPolicy() {
+		return PlatformConfiguration.getDefaultPolicy();
+	}
+
+	/**
+	 * Creates a Configuration Site and a new Site
+	 * The policy is from <code> org.eclipse.core.boot.IPlatformConfiguration</code>
+	 */
+	public IConfiguredSite createConfiguredSite(File file) throws CoreException {
+
+		if (!file.getName().equals("eclipse")) { //$NON-NLS-1$
+			file = new File(file, "eclipse"); //$NON-NLS-1$
+			file.mkdirs();
+		}
+		
+		if (isDuplicateSite(file))
+			throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_location_exists, (new String[] { file.getPath() })),null);
+		ISite site = InternalSiteManager.createSite(file);
+
+		//create a config site around the site
+		// even if the site == null
+		BaseSiteLocalFactory factory = new BaseSiteLocalFactory();
+		ConfiguredSite configSite = (ConfiguredSite) factory.createConfigurationSiteModel((SiteModel) site, getDefaultPolicy());
+
+		if (configSite.isNativelyLinked()) {
+			throw Utilities.newCoreException(Messages.InstallConfiguration_AlreadyNativelyLinked, null); 
+		}
+		
+		if (configSite.isProductSite()) {
+			throw Utilities.newCoreException(Messages.InstallConfiguration_AlreadyProductSite, null); 
+		}
+		
+		if (site != null) {
+			configSite.setPlatformURLString(site.getURL().toExternalForm());
+
+			// obtain the list of plugins
+			IPlatformConfiguration runtimeConfiguration = ConfiguratorUtils.getCurrentPlatformConfiguration();
+			ConfigurationPolicy configurationPolicy = configSite.getConfigurationPolicy();
+			String[] pluginPath = new String[0];
+			if (configurationPolicy.getPolicy() == IPlatformConfiguration.ISitePolicy.USER_INCLUDE)
+				pluginPath = configurationPolicy.getPluginPath(site);
+
+			// create new Site in configuration
+			IPlatformConfiguration.ISitePolicy sitePolicy = runtimeConfiguration.createSitePolicy(configurationPolicy.getPolicy(), pluginPath);
+
+			// change runtime
+			IPlatformConfiguration.ISiteEntry siteEntry = runtimeConfiguration.createSiteEntry(site.getURL(), sitePolicy);
+			runtimeConfiguration.configureSite(siteEntry);
+
+			// if the privatre marker doesn't already exist create it
+			configSite.createPrivateSiteMarker();
+			((SiteModel)site).setConfiguredSiteModel(configSite);
+		}
+		// configure all features as enable
+		configure(configSite);
+		
+		return configSite;
+	}
+
+	/**
+	 * Creates a Configuration Site and a new Site as a private link site
+	 * The policy is from <code> org.eclipse.core.boot.IPlatformConfiguration</code>
+	 */
+	public IConfiguredSite createLinkedConfiguredSite(File file) throws CoreException {
+		return createConfiguredSite(file);
+//		if (isDuplicateSite(file))
+//			throw Utilities.newCoreException(UpdateUtils.getFormattedMessage("InstallConfiguration.location.exists", file.getPath()),null);
+//		
+//		ISite site = InternalSiteManager.createSite(file);
+//
+//		//create a config site around the site
+//		// even if the site == null
+//		BaseSiteLocalFactory factory = new BaseSiteLocalFactory();
+//		ConfiguredSite configSite = (ConfiguredSite) factory.createConfigurationSiteModel((SiteModel) site, getDefaultPolicy());
+//
+//		if (!configSite.isExtensionSite()) {
+//			String msg = Policy.bind("InstallConfiguration.NotAnExtensionSite");
+//			throw Utilities.newCoreException(msg, null);
+//		}
+//
+//		if (configSite.isNativelyLinked()) {
+//			throw Utilities.newCoreException("InstallConfiguration.AlreadyNativelyLinked", null);
+//		}
+//
+//		if (site != null) {
+//			configSite.setPlatformURLString(site.getURL().toExternalForm());
+//
+//			// obtain the list of plugins
+//			IPlatformConfiguration runtimeConfiguration = ConfiguratorUtils.getCurrentPlatformConfiguration();
+//			ConfigurationPolicy configurationPolicy = configSite.getConfigurationPolicy();
+//			String[] pluginPath = new String[0];
+//			if (configurationPolicy.getPolicy() == IPlatformConfiguration.ISitePolicy.USER_INCLUDE)
+//				pluginPath = configurationPolicy.getPluginPath(site);
+//
+//			// create new Site in configuration
+//			IPlatformConfiguration.ISitePolicy sitePolicy = runtimeConfiguration.createSitePolicy(configurationPolicy.getPolicy(), pluginPath);
+//
+//			// change runtime
+//			IPlatformConfiguration.ISiteEntry siteEntry = runtimeConfiguration.createSiteEntry(site.getURL(), sitePolicy);
+//			runtimeConfiguration.configureSite(siteEntry);
+//
+//		}
+//
+//		// configure all features as enable
+//		configure(configSite);
+//
+//		return configSite;
+	}
+
+	/*
+	 *Configure all features as Enable Check we only enable highest version
+	 */
+	private void configure(ConfiguredSite linkedSite) throws CoreException {
+		ISite site = linkedSite.getSite();
+		ISiteFeatureReference[] newFeaturesRef = site.getFeatureReferences();
+
+		for (int i = 0; i < newFeaturesRef.length; i++) {
+			// TRACE
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+				String reconciliationType = "enable (optimistic)"; //$NON-NLS-1$
+				UpdateCore.debug("New Linked Site:New Feature: " + newFeaturesRef[i].getURL() + " as " + reconciliationType); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+			ConfigurationPolicy policy = linkedSite.getConfigurationPolicy();
+			policy.configure(newFeaturesRef[i], true, false);
+		}
+		SiteReconciler.checkConfiguredFeatures(linkedSite);
+	}
+
+	/*
+	 *
+	 */
+	public void addConfiguredSite(IConfiguredSite site) {
+		if (!isCurrent() && isReadOnly())
+			return;
+
+		ConfigurationActivity activity = new ConfigurationActivity(IActivity.ACTION_SITE_INSTALL);
+		activity.setLabel(site.getSite().getURL().toExternalForm());
+		activity.setDate(new Date());
+		ConfiguredSiteModel configSiteModel = (ConfiguredSiteModel) site;
+		addConfigurationSiteModel(configSiteModel);
+		configSiteModel.setInstallConfigurationModel(this);
+
+		// notify listeners
+		Object[] configurationListeners = listeners.getListeners();
+		for (int i = 0; i < configurationListeners.length; i++) {
+			IInstallConfigurationChangedListener listener = ((IInstallConfigurationChangedListener) configurationListeners[i]);
+			listener.installSiteAdded(site);
+		}
+
+		// everything done ok
+		activity.setStatus(IActivity.STATUS_OK);
+		this.addActivity(activity);
+	}
+
+	/**
+	 * Method addActivity.
+	 * @param activity
+	 */
+	public void addActivity(IActivity activity) {
+		addActivityModel((ConfigurationActivityModel)activity);
+	}
+
+	/*
+	 *
+	 */
+	public void removeConfiguredSite(IConfiguredSite site) {
+		if (!isCurrent() && isReadOnly())
+			return;
+
+		if (removeConfigurationSiteModel((ConfiguredSiteModel) site)) {
+			// notify listeners
+			Object[] configurationListeners = listeners.getListeners();
+			for (int i = 0; i < configurationListeners.length; i++) {
+				IInstallConfigurationChangedListener listener = ((IInstallConfigurationChangedListener) configurationListeners[i]);
+				listener.installSiteRemoved(site);
+			}
+			
+			//activity
+			ConfigurationActivity activity = new ConfigurationActivity(IActivity.ACTION_SITE_REMOVE);
+			activity.setLabel(site.getSite().getURL().toExternalForm());
+			activity.setDate(new Date());
+			activity.setStatus(IActivity.STATUS_OK);
+			this.addActivity(activity);
+		}
+	}
+
+	/*
+	 * @see IInstallConfiguration#addInstallConfigurationChangedListener(IInstallConfigurationChangedListener)
+	 */
+	public void addInstallConfigurationChangedListener(IInstallConfigurationChangedListener listener) {
+		listeners.add(listener);
+	}
+
+	/*
+	 * @see IInstallConfiguration#removeInstallConfigurationChangedListener(IInstallConfigurationChangedListener)
+	 */
+	public void removeInstallConfigurationChangedListener(IInstallConfigurationChangedListener listener) {
+		listeners.remove(listener);
+	}
+
+
+	/*
+	 * Deletes the configuration from its URL/location
+	 */
+	public void remove() {
+		// save the configuration
+		if ("file".equalsIgnoreCase(getURL().getProtocol())) { //$NON-NLS-1$
+			// the location points to a file
+			File file = new File(getURL().getFile());
+			UpdateManagerUtils.removeFromFileSystem(file);
+		}
+	}
+
+	/**
+	 * Saves the configuration into its URL/location
+	 * and changes the platform configuration.
+	 * The runtime site entries from platform.xml are updated as required
+	 * (cannot recreate these because must preserve other runtime state) [18520]
+	 * @return true if restart is needed
+	 */
+	public boolean save() throws CoreException {
+		
+		// Write info  into platform for the next runtime
+		IPlatformConfiguration runtimeConfiguration = ConfiguratorUtils.getCurrentPlatformConfiguration();
+		ConfiguredSiteModel[] configurationSites = getConfigurationSitesModel();
+
+		// clean configured Entries from platform runtime
+		IPlatformConfiguration.IFeatureEntry[] configuredFeatureEntries = runtimeConfiguration.getConfiguredFeatureEntries();
+		for (int i = 0; i < configuredFeatureEntries.length; i++) {
+			runtimeConfiguration.unconfigureFeatureEntry(configuredFeatureEntries[i]);
+		}
+
+		// [19958] remember sites currently configured by runtime (use
+		// temp configuration object rather than a straight list to ensure
+		// correct lookup)
+		IPlatformConfiguration tempConfig = null;
+		try {
+			tempConfig = ConfiguratorUtils.getPlatformConfiguration(null);
+			IPlatformConfiguration.ISiteEntry[] tmpSites = runtimeConfiguration.getConfiguredSites();
+			for (int i = 0; i < tmpSites.length; i++) {
+				tempConfig.configureSite(tmpSites[i]);
+			}
+		} catch (IOException e) {
+			// assume no currently configured sites
+		}
+
+		//check sites
+		checkSites(configurationSites, runtimeConfiguration);
+
+		// Save the plugin path, primary feature and platform
+		for (int i = 0; i < configurationSites.length; i++) {
+			ConfiguredSite cSite = ((ConfiguredSite) configurationSites[i]);
+			ConfigurationPolicy configurationPolicy = cSite.getConfigurationPolicy();
+
+			savePluginPath(cSite, runtimeConfiguration, tempConfig);
+
+			// IF primary feature URL or platform feature URL that we need to pass to runtime config
+			// is part of platform:base:, write it as platform:base: URL
+			IFeatureReference[] configuredFeaturesRef = configurationPolicy.getConfiguredFeatures();
+			for (int j = 0; j < configuredFeaturesRef.length; j++) {
+				IFeature feature = null;
+				try {
+					feature = configuredFeaturesRef[j].getFeature(null);
+				} catch (CoreException e) {
+					UpdateCore.warn(null, e);
+				}
+				saveFeatureEntry(cSite, feature, runtimeConfiguration);
+			}
+		}
+
+		// [19958] remove any extra site entries from runtime configuration
+		// (site entries that no longer exist in this configuration)
+		if (tempConfig != null) {
+			IPlatformConfiguration.ISiteEntry[] tmpSites = tempConfig.getConfiguredSites();
+			for (int i = 0; i < tmpSites.length; i++) {
+				runtimeConfiguration.unconfigureSite(tmpSites[i]);
+			}
+		}
+
+		try {
+			runtimeConfiguration.save();
+			// log configuration and activities
+			this.date = new Date(runtimeConfiguration.getChangeStamp());
+			if ("file".equalsIgnoreCase(getURL().getProtocol())) //$NON-NLS-1$
+				UpdateCore.log(this);
+			resetActivities();
+			return isRestartNeeded(runtimeConfiguration);
+		} catch (IOException e) {
+			CoreException exc = Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_UnableToSavePlatformConfiguration, (new String[] { runtimeConfiguration.getConfigurationLocation().toExternalForm() })), e);
+			UpdateCore.warn("",exc); //$NON-NLS-1$
+		}
+		return true;
+	}
+
+	/*
+	 * Write the plugin path for each site
+	 * Do not check if the site already existed before [16696].
+	 * Reuse any runtime site objects in platform.cfg (to preserve state) [18520].
+	 */
+	private void savePluginPath(ConfiguredSite cSite, IPlatformConfiguration runtimeConfiguration, IPlatformConfiguration tempConfig) // [19958]
+	throws CoreException {
+
+		ConfigurationPolicy configurationPolicy = cSite.getConfigurationPolicy();
+
+		// create a ISitePolicy (policy, pluginPath)
+		// for the site
+		String[] pluginPath = configurationPolicy.getPluginPath(cSite.getSite());
+		IPlatformConfiguration.ISitePolicy sitePolicy = runtimeConfiguration.createSitePolicy(configurationPolicy.getPolicy(), pluginPath);
+
+		// get the URL of the site that matches the one platform.cfg gave us
+		URL urlToCheck = null;
+		try {
+			urlToCheck = new URL(cSite.getPlatformURLString());
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_UnableToCreateURL, (new String[] { cSite.getPlatformURLString() })), e);
+		} catch (ClassCastException e) {
+			throw Utilities.newCoreException(Messages.InstallConfiguration_UnableToCast, e);	
+		}
+
+		// update runtime configuration [18520]
+		// Note: we must not blindly replace the site entries because they
+		//       contain additional runtime state that needs to be preserved.
+		IPlatformConfiguration.ISiteEntry siteEntry = runtimeConfiguration.findConfiguredSite(urlToCheck);
+		if (siteEntry == null)
+			siteEntry = runtimeConfiguration.createSiteEntry(urlToCheck, sitePolicy);
+		else {
+			siteEntry.setSitePolicy(sitePolicy);
+			((SiteEntry)siteEntry).refreshPlugins();
+			if (tempConfig != null) // [19958] remove reused entries from list
+				tempConfig.unconfigureSite(siteEntry);
+		}
+		((SiteEntry)siteEntry).setEnabled(cSite.isEnabled());
+		runtimeConfiguration.configureSite(siteEntry, true /*replace if exists*/);
+	}
+
+	/*
+	 * Save the Feature entry
+	 * The feature can be a primary feature and/or a platform feature
+	 */
+	private void saveFeatureEntry(ConfiguredSite cSite, IFeature feature, IPlatformConfiguration runtimeConfiguration) throws CoreException {
+		if (feature == null)
+			return;
+
+		// get the URL of the plugin that corresponds to the feature (pluginid = featureid)
+		String id = feature.getVersionedIdentifier().getIdentifier();
+		IPluginEntry[] entries = feature.getPluginEntries();
+		URL url = null;
+		IPluginEntry featurePlugin = null;
+		for (int k = 0; k < entries.length; k++) {
+			if (id.equalsIgnoreCase(entries[k].getVersionedIdentifier().getIdentifier())) {
+				url = getRuntimeConfigurationURL(entries[k], cSite);
+				featurePlugin = entries[k];
+				break;
+			}
+		}
+		String pluginVersion = null;
+		if (featurePlugin != null)
+			pluginVersion = featurePlugin.getVersionedIdentifier().getVersion().toString();
+
+        // Find the site
+        SiteEntry siteEntry = null;
+        try {
+            URL featureUrl = new URL(cSite.getPlatformURLString());
+            siteEntry = (SiteEntry)runtimeConfiguration.findConfiguredSite(featureUrl);
+        } catch (MalformedURLException e) {
+            throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_UnableToCreateURL, (new String[] { cSite.getPlatformURLString() })), e);
+        } catch (ClassCastException e) {
+            throw Utilities.newCoreException(Messages.InstallConfiguration_UnableToCast, e);    
+        }
+
+        // if the URL doesn't exist throw a CoreException
+        if (siteEntry == null) {
+            throw new CoreException(
+                    new Status(IStatus.ERROR, UpdateCore.getPlugin().getBundle().getSymbolicName(),
+                            NLS.bind(Messages.InstallConfiguration_unableToFindSite, (new String[] { cSite.getSite().getURL().toExternalForm(), runtimeConfiguration.getConfigurationLocation().toExternalForm() }))));
+        }
+
+		// write the primary features
+		if (feature.isPrimary()) {
+			// get any fragments for the feature plugin
+			ArrayList list = new ArrayList();
+			if (url != null)
+				list.add(url);
+			if (featurePlugin != null) {
+				URL[] fragments = getRuntimeFragmentURLs(featurePlugin);
+				list.addAll(Arrays.asList(fragments));
+			}
+			URL[] roots = (URL[]) list.toArray(new URL[0]);
+			String pluginIdentifier = feature.getPrimaryPluginID();
+
+			// save information in runtime platform state
+			String version = feature.getVersionedIdentifier().getVersion().toString();
+			String application = feature.getApplication();
+			FeatureEntry featureEntry = (FeatureEntry)runtimeConfiguration.createFeatureEntry(id, version, pluginIdentifier, pluginVersion, true, application, roots);
+			featureEntry.setURL(getFeatureRelativeURL(feature));
+			siteEntry.addFeatureEntry(featureEntry);
+		} else {
+			// write non-primary feature entries
+			String version = feature.getVersionedIdentifier().getVersion().toString();
+			String pluginIdentifier = feature.getPrimaryPluginID();
+			FeatureEntry featureEntry = (FeatureEntry)runtimeConfiguration.createFeatureEntry(id, version, pluginIdentifier, pluginVersion, false, null, null);
+			featureEntry.setURL(getFeatureRelativeURL(feature));
+			siteEntry.addFeatureEntry(featureEntry);
+		}
+
+		// write the platform features (features that contain special platform plugins)
+		IPluginEntry[] platformPlugins = getPlatformPlugins(feature, runtimeConfiguration);
+		for (int k = 0; k < platformPlugins.length; k++) {
+			id = platformPlugins[k].getVersionedIdentifier().getIdentifier();
+			url = getRuntimeConfigurationURL(platformPlugins[k], cSite);
+			if (url != null) {
+				runtimeConfiguration.setBootstrapPluginLocation(id, url);
+			}
+		}
+	}
+
+	/*
+	 * Log if we are about to create a site that didn't exist before
+	 * in platform.cfg [16696].
+	 */
+	private void checkSites(ConfiguredSiteModel[] configurationSites, IPlatformConfiguration runtimeConfiguration) throws CoreException {
+
+		// check all the sites we are about to write already existed
+		// they should have existed either because they were created by
+		// updateManager or because we read them from platform.cfg
+		for (int i = 0; i < configurationSites.length; i++) {
+			// get the URL of the site that matches the one platform.cfg gave us
+			URL urlToCheck = null;
+			try {
+				urlToCheck = new URL(configurationSites[i].getPlatformURLString());
+			} catch (MalformedURLException e) {
+				UpdateCore.warn(NLS.bind(Messages.InstallConfiguration_UnableToCreateURL, (new String[] { configurationSites[i].getPlatformURLString() })), e);
+			} catch (ClassCastException e) {
+				UpdateCore.warn(Messages.InstallConfiguration_UnableToCast, e);
+			}
+
+			// if the URL doesn't exits log it
+			IPlatformConfiguration.ISiteEntry siteEntry = runtimeConfiguration.findConfiguredSite(urlToCheck);
+			if (siteEntry == null) {
+				UpdateCore.warn(NLS.bind(Messages.InstallConfiguration_unableToFindSite, (new String[] { urlToCheck.toExternalForm(), runtimeConfiguration.getConfigurationLocation().toExternalForm() }))); 
+			}
+		}
+	}
+
+
+	/*
+	 * reverts this configuration to the match the new one
+	 *
+	 * Compare the oldSites with the currentOne. the old state is the state we want to revert to.
+	 *
+	 * If a site was in old state, but not in the currentOne, keep it in the hash.
+	 * If a site is in the currentOne but was not in the old state, unconfigure all features and add it in the hash
+	 * If a site was in baoth state, calculate the 'delta' and re-set it in the hash map
+	 *
+	 * At the end, set the configured site from the new sites hash map
+	 *
+	 */
+	public void revertTo(IInstallConfiguration configuration, IProgressMonitor monitor, IProblemHandler handler) throws CoreException, InterruptedException {
+
+		IConfiguredSite[] oldConfigSites = configuration.getConfiguredSites();
+		IConfiguredSite[] nowConfigSites = this.getConfiguredSites();
+
+		// create a hashtable of the *old* and *new* sites
+		Map oldSitesMap = new Hashtable(0);
+		Map newSitesMap = new Hashtable(0);
+		for (int i = 0; i < oldConfigSites.length; i++) {
+			IConfiguredSite element = oldConfigSites[i];
+			oldSitesMap.put(element.getSite().getURL().toExternalForm(), element);
+			newSitesMap.put(element.getSite().getURL().toExternalForm(), element);
+		}
+		// create list of all the sites that map the *old* sites
+		// we want the intersection between the old sites and the current sites
+		if (nowConfigSites != null) {
+			String key = null;
+
+			for (int i = 0; i < nowConfigSites.length; i++) {
+				key = nowConfigSites[i].getSite().getURL().toExternalForm();
+				IConfiguredSite oldSite = (IConfiguredSite) oldSitesMap.get(key);
+				if (oldSite != null) {
+					// the Site existed before, calculate the delta between its current state and the
+					// state we are reverting to and put it back into the map
+					 ((ConfiguredSite) nowConfigSites[i]).revertTo(oldSite, monitor, handler);
+				} else {
+					// the site didn't exist in the InstallConfiguration we are reverting to
+					// unconfigure everything from this site so it is still present
+					ISiteFeatureReference[] featuresToUnconfigure = nowConfigSites[i].getSite().getFeatureReferences();
+					for (int j = 0; j < featuresToUnconfigure.length; j++) {
+						IFeature featureToUnconfigure = null;
+						try {
+							featureToUnconfigure = featuresToUnconfigure[j].getFeature(null);
+						} catch (CoreException e) {
+							UpdateCore.warn(null, e);
+						}
+						if (featureToUnconfigure != null)
+							nowConfigSites[i].unconfigure(featureToUnconfigure);
+					}
+				}
+				newSitesMap.put(key,nowConfigSites[i]);
+			}
+
+			// the new configuration has the exact same sites as the old configuration
+			// the old configuration in the Map are either as-is because they don't exist
+			// in the current one, or they are the delta from the current one to the old one
+			Collection sites = newSitesMap.values();
+			if (sites != null && !sites.isEmpty()) {
+				ConfiguredSiteModel[] sitesModel = new ConfiguredSiteModel[sites.size()];
+				sites.toArray(sitesModel);
+				setConfigurationSiteModel(sitesModel);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallConfiguration#getActivities()
+	 */
+	public IActivity[] getActivities() {
+		if (getActivityModel().length == 0)
+			return new IActivity[0];
+		return (IActivity[]) getActivityModel();
+	}
+
+	/*
+	 * returns the list of platform plugins of the feature or an empty list
+	 * if the feature doesn't contain any platform plugins
+	 */
+	private IPluginEntry[] getPlatformPlugins(IFeature feature, IPlatformConfiguration runtimeConfiguration) {
+		Map featurePlatformPlugins = new HashMap();
+		String[] platformPluginID = runtimeConfiguration.getBootstrapPluginIdentifiers();
+		IPluginEntry[] featurePlugins = feature.getPluginEntries();
+
+		for (int i = 0; i < platformPluginID.length; i++) {
+			String featurePluginId = null;
+			for (int j = 0; j < featurePlugins.length; j++) {
+				featurePluginId = featurePlugins[j].getVersionedIdentifier().getIdentifier();
+				if (platformPluginID[i].equals(featurePluginId)) {
+					featurePlatformPlugins.put(platformPluginID[i], featurePlugins[j]);
+				}
+			}
+		}
+
+		Collection values = featurePlatformPlugins.values();
+		if (values == null || values.size() == 0)
+			return new IPluginEntry[0];
+
+		IPluginEntry[] result = new IPluginEntry[values.size()];
+		Iterator iter = values.iterator();
+		int index = 0;
+		while (iter.hasNext()) {
+			result[index] = ((IPluginEntry) iter.next());
+			index++;
+		}
+		return result;
+	}
+
+	/*
+	 * returns the URL of the pluginEntry on the site
+	 * Transform the URL to use platform: protocol if needed
+	 * return null if the URL to write is not valid
+	 */
+	private URL getRuntimeConfigurationURL(IPluginEntry entry, ConfiguredSite cSite) throws CoreException {
+
+		String rootString = cSite.getPlatformURLString();
+		String pluginPathID = getPathID(entry);
+		try {
+			ISiteContentProvider siteContentProvider = cSite.getSite().getSiteContentProvider();
+			URL pluginEntryfullURL = siteContentProvider.getArchiveReference(pluginPathID);
+
+			//
+			if (!rootString.startsWith("platform")) { //$NON-NLS-1$
+				// DEBUG:
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+					UpdateCore.debug("getRuntimeConfiguration Plugin Entry Full URL:" + pluginEntryfullURL + " Platform String:" + rootString + " [NON PLATFORM URL]."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+				return pluginEntryfullURL;
+			}
+
+			//URL pluginEntryRootURL = Platform.resolve(new URL(rootString));
+			// Do not resolve [16507], just use platform:base/ as a root
+			// rootString = platform:base
+			// pluginRoot = /home/a
+			// pluginFull = /home/a/c/boot.jar
+			// relative = platform:/base/c/boot.jar
+			URL pluginEntryRootURL = cSite.getSite().getURL();
+			String relativeString = UpdateManagerUtils.getURLAsString(pluginEntryRootURL, pluginEntryfullURL);
+			URL result = new URL(new URL(rootString), relativeString);
+
+			// DEBUG:
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+				UpdateCore.debug("getRuntimeConfiguration plugin Entry Full URL:" + pluginEntryfullURL + " Platform String:" + rootString + " Site URL:" + pluginEntryRootURL + " Relative:" + relativeString); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+			// verify we are about to write a valid file URL
+			// check with fullURL as it is not resolved to platform:base/
+			if (pluginEntryfullURL != null) {
+				if ("file".equals(pluginEntryfullURL.getProtocol())) { //$NON-NLS-1$
+					String fileString = pluginEntryfullURL.getFile();
+					if (!new File(fileString).exists()) {
+						UpdateCore.warn("The URL:" + result + " doesn't point to a valid platform plugin.The URL will not be written in the platform configuration", new Exception()); //$NON-NLS-1$ //$NON-NLS-2$
+						return null;
+					}
+				}
+			}
+
+			return result;
+		} catch (IOException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_UnableToCreateURL, (new String[] { rootString })), e);
+		}
+	}
+
+	/*
+	 * Return URLs for any fragments that are associated with the specified plugin entry
+	 */
+	private URL[] getRuntimeFragmentURLs(IPluginEntry entry) throws CoreException {
+
+		// get the identifier associated with the entry
+		VersionedIdentifier vid = entry.getVersionedIdentifier();
+
+		// get the plugin descriptor from the registry
+		Bundle bundle = Platform.getBundle(vid.getIdentifier());
+		ArrayList list = new ArrayList();
+		if (bundle != null && bundle.getState() != Bundle.UNINSTALLED && bundle.getState() != Bundle.INSTALLED) {
+			FragmentEntry[] fragments = UpdateManagerUtils.getFragments(bundle);
+			for (int i = 0; fragments != null && i < fragments.length; i++) {
+				String location = fragments[i].getLocation();
+				try {
+					URL locationURL = new URL(location);
+					locationURL = FileLocator.toFileURL(FileLocator.resolve(locationURL));
+					list.add(asInstallRelativeURL(locationURL));
+				} catch (IOException e) {
+					// skip bad fragments
+				}
+			}
+		}
+		return (URL[]) list.toArray(new URL[0]);
+	}
+
+	/**
+	 * Returns the path identifier for a plugin entry.
+	 * <code>plugins/&lt;pluginId>_&lt;pluginVersion>.jar</code>
+	 * @return the path identifier
+	 */
+	private String getPathID(IPluginEntry entry) {
+		return Site.DEFAULT_PLUGIN_PATH + entry.getVersionedIdentifier().toString() + FeatureContentProvider.JAR_EXTENSION;
+	}
+
+	/**
+	 * Try to recast URL as platform:/base/
+	 */
+	private URL asInstallRelativeURL(URL url) {
+		// get location of install
+		URL install = ConfiguratorUtils.getInstallURL();
+
+		// try to determine if supplied URL can be recast as install-relative
+		if (install.getProtocol().equals(url.getProtocol())) {
+			if (install.getProtocol().equals("file")) { //$NON-NLS-1$
+				String installS = new File(install.getFile()).getAbsolutePath().replace(File.separatorChar, '/');
+				if (!installS.endsWith("/")) //$NON-NLS-1$
+					installS += "/"; //$NON-NLS-1$
+				String urlS = new File(url.getFile()).getAbsolutePath().replace(File.separatorChar, '/');
+				if (!urlS.endsWith("/")) //$NON-NLS-1$
+					urlS += "/"; //$NON-NLS-1$
+				int ix = installS.lastIndexOf("/"); //$NON-NLS-1$
+				if (ix != -1) {
+					installS = installS.substring(0, ix + 1);
+					if (urlS.startsWith(installS)) {
+						try {
+							return new URL("platform:/base/" + urlS.substring(installS.length())); //$NON-NLS-1$
+						} catch (MalformedURLException e) {
+						}
+					}
+				}
+			}
+		}
+		return url;
+	}
+	
+	private boolean isDuplicateSite(File siteDirectory) {
+		IConfiguredSite[] sites = getConfiguredSites();
+		URL fileURL;
+		try {
+			fileURL = siteDirectory.toURL();
+		} catch (MalformedURLException e) {
+			return false;
+		}
+		for (int i = 0; i < sites.length; i++) {
+			URL url = sites[i].getSite().getURL();
+			if (UpdateManagerUtils.sameURL(fileURL, url))
+				return true;
+		}
+		return false;
+	}
+	
+	/*
+	 * Returns the feature url relative to the site.
+	 */
+	private String getFeatureRelativeURL(IFeature feature) {
+		String url = feature.getURL().toExternalForm();
+		String siteURL = feature.getSite().getURL().toExternalForm();
+		// TODO fix this. toURL() returns file:/d:/eclipse/etc... wheareas the 
+		// platform.asLocalURL() returns file:d:/eclipse/etc... (no leading / )
+//		if (url.startsWith("file:/") && Platform.getOS().equals("win32"))
+//			url = "file:" + url.substring(6);
+		
+		if (url.startsWith(siteURL))
+			return url.substring(siteURL.length());
+		else
+			return url;
+	}
+	
+	/**
+	 * @return true if restart is needed
+	 */
+	private boolean isRestartNeeded(IPlatformConfiguration runtimeConfig) {
+
+		// First, create a map for faster lookups
+		Set newPluginsSet = null;
+		if (runtimeConfig instanceof PlatformConfiguration) {
+			newPluginsSet = ((PlatformConfiguration)runtimeConfig).getPluginPaths();
+			// On windows, we will be doing case insensitive search as well, so lower it now
+			if (isWindows) {
+				String[] newPluginsSetArray = (String[])newPluginsSet.toArray( new String[newPluginsSet.size()]);
+				for (int i = 0; i < newPluginsSetArray.length; i++) {
+					newPluginsSet.add(newPluginsSetArray[i].toLowerCase());
+				}
+			}
+		} else {
+			URL[] newBundlePaths = runtimeConfig.getPluginPath();
+			newPluginsSet = new HashSet(newBundlePaths.length);
+			for (int i=0; i<newBundlePaths.length; i++) {
+				
+				String pluginLocation = newBundlePaths[i].getFile();
+				newPluginsSet.add(pluginLocation);
+				// On windows, we will be doing case insensitive search as well, so lower it now
+				if (isWindows)
+					newPluginsSet.add(pluginLocation.toLowerCase());
+			}
+		}
+		
+		
+		
+		Bundle[] oldBundles = UpdateCore.getPlugin().getBundleContext().getBundles();
+
+		int offset = ConfigurationActivator.UPDATE_PREFIX.length();
+		for (int i=0; i<oldBundles.length; i++) {
+			if (oldBundles[i].getBundleId() == 0)
+				continue; // skip the system bundle
+			String oldBundleLocation = oldBundles[i].getLocation();
+			// Don't worry about bundles we did not install
+			if (!oldBundleLocation.startsWith(ConfigurationActivator.UPDATE_PREFIX))
+				continue;
+			oldBundleLocation = oldBundleLocation.substring(offset);
+			
+			if (newPluginsSet.contains(oldBundleLocation))
+				continue;
+			if (isWindows && newPluginsSet.contains(oldBundleLocation.toLowerCase()))
+				continue;
+			
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+				UpdateCore.debug("Bundle " + oldBundleLocation + " has been removed"); //$NON-NLS-1$ //$NON-NLS-2$
+			return true;
+		}
+		
+		if (runtimeConfig instanceof PlatformConfiguration) {
+			return areThereNewVersionOfOldPlugins( ((PlatformConfiguration)runtimeConfig).getPlugins(), oldBundles);
+		}
+
+		return false;
+	}
+	
+	/**
+	 * The method should only be called if all old plug-ins exist in the new
+	 * configuration. Determines if a new version was added to any plugin ID, so
+	 * more versions are enabled for any given plug-in id then in old
+	 * configuration.
+	 * 
+	 * @param newConfigurationPlugins
+	 * @param oldConfigurationBundles
+	 * @return
+	 */
+	private boolean areThereNewVersionOfOldPlugins(PluginEntry[] newConfigurationPlugins, Bundle[] oldConfigurationBundles) {
+
+		
+		for ( int i = 0; i < oldConfigurationBundles.length; i++) {
+			if (oldConfigurationBundles[i].getBundleId() == 0)
+				continue; // skip the system bundle
+			if ( getNumberOfPlugins(oldConfigurationBundles[i].getSymbolicName(), oldConfigurationBundles) != getNumberOfPlugins(oldConfigurationBundles[i].getSymbolicName(), newConfigurationPlugins)) {
+				return true;
+			}
+		}
+		return false;				
+	}
+
+	private int getNumberOfPlugins(String symbolicName, PluginEntry[] newConfigurationPlugins) {
+		
+		int numberOfPlugins = 0;
+		
+		for ( int i = 0; i < newConfigurationPlugins.length; i++) {
+			if ( symbolicName.equals(newConfigurationPlugins[i].getPluginIdentifier())) {
+				numberOfPlugins++;
+			}
+		}
+		
+		return numberOfPlugins;
+	}
+
+	private int getNumberOfPlugins(String symbolicName, Bundle[] oldConfigurationBundles) {
+
+		int numberOfPlugins = 0;
+		
+		for ( int i = 0; i < oldConfigurationBundles.length; i++) {
+			if ( symbolicName.equals(oldConfigurationBundles[i].getSymbolicName())) {
+				numberOfPlugins++;
+			}
+		}
+		
+		return numberOfPlugins;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!(obj instanceof InstallConfiguration))
+			return false;
+		
+		InstallConfiguration config = (InstallConfiguration)obj;
+	
+		return getCreationDate().equals(config.getCreationDate()) && 
+				getLabel().equals(config.getLabel()) &&
+				getLocationURLString().equals(config.getLocationURLString());
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallHandlerProxy.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallHandlerProxy.java
new file mode 100644
index 0000000..0b47f34
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallHandlerProxy.java
@@ -0,0 +1,613 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.update.internal.core;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureContentConsumer;
+import org.eclipse.update.core.IInstallHandler;
+import org.eclipse.update.core.IInstallHandlerEntry;
+import org.eclipse.update.core.IInstallHandlerWithFilter;
+import org.eclipse.update.core.INonPluginEntry;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.IVerificationListener;
+import org.eclipse.update.core.InstallMonitor;
+import org.eclipse.update.core.Utilities;
+import org.osgi.framework.Bundle;
+
+public class InstallHandlerProxy implements IInstallHandlerWithFilter {
+
+	private IFeature feature = null;
+	private int type;
+	private IInstallHandler handler = null;
+	private IStatus savedStatus = null;
+	private boolean DEBUG = false;
+
+	private static final String EXT_PLUGIN = "org.eclipse.update.core"; //$NON-NLS-1$
+	private static final String UI_PLUGIN = "org.eclipse.ui"; //$NON-NLS-1$
+	private static final String EXT_POINT = "installHandlers"; //$NON-NLS-1$
+	private Method nonPluginDataAcceptor = null;
+
+	/**
+	 * A class loader that combines a the org.eclipse.update.core plugin class loader with the
+	 * org.eclipse.ui class loader (only when UI is active).
+	 */
+	private static class InstallHandlerClassLoader extends URLClassLoader {
+		private Bundle updateCore;
+		private Bundle eclipseUI;
+
+		public InstallHandlerClassLoader(URL[] classpath) {
+			super(classpath);
+			updateCore = Platform.getBundle(EXT_PLUGIN);
+			eclipseUI = Platform.getBundle(UI_PLUGIN);
+			if (eclipseUI != null && eclipseUI.getState() != Bundle.ACTIVE) 
+				eclipseUI = null;
+		}
+
+		public Class loadClass(String className) throws ClassNotFoundException {
+			// First check update core plugin loader, then the eclipse ui plugin loader
+			Class c = null;
+			try {
+				c = updateCore.loadClass(className);
+			} catch (ClassNotFoundException e) {
+				try {
+					if(eclipseUI != null)
+						c = eclipseUI.loadClass(className);
+				} catch (ClassNotFoundException e2) {
+				} finally {
+				}
+			} finally {
+			}
+			if (c != null)
+				return c;
+			else
+				return super.loadClass(className);
+		}
+
+		public URL getResource(String resName) {
+			// First check update core plugin loader, then the eclipse ui plugin loader
+			URL u = updateCore.getResource(resName);
+			if(u == null && eclipseUI != null)
+				u = eclipseUI.getResource(resName);
+				
+			if (u != null)
+				return u;
+			else
+				return super.getResource(resName);
+		}
+	}
+	
+	public InstallHandlerProxy(
+		int type,
+		IFeature feature,
+		IInstallHandlerEntry entry,
+		InstallMonitor monitor)
+		throws CoreException {
+
+		initialize(type, feature, entry, monitor);
+	}
+
+	/*
+	 * @see IInstallHandler#initialize
+	 */
+	public void initialize(
+		int type,
+		IFeature feature,
+		IInstallHandlerEntry entry,
+		InstallMonitor monitor)
+		throws CoreException {
+
+		DEBUG = UpdateCore.DEBUG_SHOW_IHANDLER;
+		// validate arguments
+		if (feature == null)
+			throw new IllegalArgumentException();
+		this.feature = feature;
+		this.type = type;
+
+		//  check if we have a handler entry specified in the feature.xml
+		if (entry == null) {
+			if (DEBUG)
+				debug("not specified"); //$NON-NLS-1$
+			return; // no handler entry
+		}
+
+		String library = entry.getLibrary();
+		String handlerName = entry.getHandlerName();
+		if (handlerName == null || handlerName.trim().equals("")) { //$NON-NLS-1$
+			if (DEBUG)
+				debug("not specified"); //$NON-NLS-1$
+			return; // no handler class spacified in entry
+		}
+		if (DEBUG) {
+			debug("handler=" + handlerName); //$NON-NLS-1$
+			debug("path=   " + library); //$NON-NLS-1$
+		}
+
+		// get handler instance
+		try {
+			if (library == null || library.trim().equals("")) //$NON-NLS-1$
+				this.handler = getGlobalHandler(handlerName);
+			else
+				this.handler = getLocalHandler(library, handlerName);
+			if (this.handler == null)
+				return;
+			handler.initialize(type, feature, entry, monitor);
+		} catch (ClassNotFoundException e) {
+			handleExceptionInInit(
+				NLS.bind(Messages.InstallHandler_notFound, (new String[] { feature.getLabel() })),
+				e);
+
+		} catch (ClassCastException e) {
+			handleExceptionInInit(
+				NLS.bind(Messages.InstallHandler_invalidHandler, (new String[] { feature.getLabel() })),
+				e);
+		} catch (CoreException e) {
+			handleExceptionInInit(null, e);
+		} catch (Exception e) {
+			handleExceptionInInit(
+				NLS.bind(Messages.InstallHandler_unableToCreateHandler, (new String[] { feature.getLabel() })),
+				e);
+		}
+
+	}
+
+	/*
+	 * @see IInstallHandler#installInitiated
+	 */
+	public void installInitiated() throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling installInitiated()"); //$NON-NLS-1$
+				handler.installInitiated();
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#allPluginsDownloaded
+	 */
+	public void pluginsDownloaded(IPluginEntry[] plugins) throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling pluginsDownloaded()"); //$NON-NLS-1$
+				handler.pluginsDownloaded(plugins);
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#allPluginsInstalled
+	 */
+	public void completeInstall(IFeatureContentConsumer consumer)
+		throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling completeInstall()"); //$NON-NLS-1$
+				handler.completeInstall(consumer);
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#allDataDownloaded
+	 */
+	public void nonPluginDataDownloaded(
+		INonPluginEntry[] nonPluginData,
+		IVerificationListener listener)
+		throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling nonPluginDataDownloaded()"); //$NON-NLS-1$
+				handler.nonPluginDataDownloaded(nonPluginData, listener);
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#installCompleted
+	 */
+	public void installCompleted(boolean success) throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling installCompleted()"); //$NON-NLS-1$
+				handler.installCompleted(success);
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#configureInitiated
+	 */
+	public void configureInitiated() throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling configureInitiated()"); //$NON-NLS-1$
+				handler.configureInitiated();
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#completeConfigure
+	 */
+	public void completeConfigure() throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling completeConfigure()"); //$NON-NLS-1$
+				handler.completeConfigure();
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#configureCompleted
+	 */
+	public void configureCompleted(boolean success) throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling configureCompleted()"); //$NON-NLS-1$
+				handler.configureCompleted(success);
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#unconfigureInitiated
+	 */
+	public void unconfigureInitiated() throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling unconfigureInitiated()"); //$NON-NLS-1$
+				handler.unconfigureInitiated();
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#completeUnconfigure
+	 */
+	public void completeUnconfigure() throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling completeUnconfigure()"); //$NON-NLS-1$
+				handler.completeUnconfigure();
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#unconfigureCompleted
+	 */
+	public void unconfigureCompleted(boolean success) throws CoreException {
+		if (handler == null) {
+			if (savedStatus == null)
+				return;
+			else
+				throw new CoreException(savedStatus); // delayed exception
+		} else {
+			try {
+				if (DEBUG)
+					debug("calling unconfigureCompleted()"); //$NON-NLS-1$
+				handler.unconfigureCompleted(success);
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+			if (savedStatus != null)
+				throw new CoreException(savedStatus); // delayed exception
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#uninstallInitiated
+	 */
+	public void uninstallInitiated() throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling uninstallInitiated()"); //$NON-NLS-1$
+				handler.uninstallInitiated();
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#completeUninstall
+	 */
+	public void completeUninstall() throws CoreException {
+		if (handler == null)
+			return;
+		else {
+			try {
+				if (DEBUG)
+					debug("calling completeUninstall()"); //$NON-NLS-1$
+				handler.completeUninstall();
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+		}
+	}
+
+	/*
+	 * @see IInstallHandler#uninstallCompleted
+	 */
+	public void uninstallCompleted(boolean success) throws CoreException {
+		if (handler == null) {
+			if (savedStatus == null)
+				return;
+			else
+				throw new CoreException(savedStatus); // delayed exception
+		} else {
+			try {
+				if (DEBUG)
+					debug("calling uninstallCompleted()"); //$NON-NLS-1$
+				handler.uninstallCompleted(success);
+			} catch (Throwable e) {
+				handleExceptionInCall(e, feature);
+			}
+			if (savedStatus != null)
+				throw new CoreException(savedStatus); // delayed exception
+		}
+	}
+
+	/*
+	 * common exception handling for initialization
+	 */
+	private void handleExceptionInInit(String s, Exception e)
+		throws CoreException {
+
+		CoreException ce;
+		if (e instanceof CoreException)
+			ce = (CoreException) e;
+		else
+			ce = Utilities.newCoreException(s, e);
+
+		if (isUndoAction()) {
+			// for "undo" operations, deactivate handler and log error
+			String id =
+				UpdateCore.getPlugin().getBundle().getSymbolicName();
+			IStatus status =
+				new Status(IStatus.ERROR, id, 0, "InstallHandler.deactivated", ce);	//$NON-NLS-1$
+			UpdateCore.getPlugin().getLog().log(status);
+			handler = null; // disable subsequent handler calls
+			savedStatus = status;
+		} else
+			// for "do" operations, hurl ...
+			throw ce;
+	}
+
+	/*
+	 * common exception handling for calls to install handler
+	 */
+	private void handleExceptionInCall(Throwable e, IFeature feature)
+		throws CoreException {
+
+		CoreException ce;
+		if (e instanceof CoreException)
+			ce = (CoreException) e;
+		else
+			ce =
+				Utilities.newCoreException(
+					NLS.bind(Messages.InstallHandler_callException, (new String[] { feature.getLabel() })),
+					e);
+		
+		if (isUndoAction()) {
+			// for "undo" operations, deactivate handler and log error
+			String id =
+				UpdateCore.getPlugin().getBundle().getSymbolicName();
+			IStatus status =
+				new Status(IStatus.ERROR, id, 0, "InstallHandler.deactivated", ce);	//$NON-NLS-1$
+			UpdateCore.getPlugin().getLog().log(status);
+			handler = null; // disable subsequent handler calls
+			savedStatus = status;
+		} else
+			// for "do" operations, hurl ...
+			throw ce;
+	}
+
+	/*
+	 * Indicates whether we are doing (install, configure) or 
+	 * undoing (uninstall, unconfigure)
+	 */
+	private boolean isUndoAction() {
+		if (this.type == IInstallHandler.HANDLER_ACTION_INSTALL
+			|| this.type == IInstallHandler.HANDLER_ACTION_CONFIGURE)
+			return false; // causes exception to be thrown and action aborted
+		else
+			return true; // causes exception to be logged and action continues
+	}
+
+	/*
+	 * get an instance of handler downloaded as part of the feature
+	 */
+	private IInstallHandler getLocalHandler(String libs, String name)
+		throws IOException, CoreException, ClassNotFoundException, InstantiationException, IllegalAccessException {
+
+		// Get baseline URL for handler (relative to feature.xml). For
+		// features being installed from a server (eg. http protocol)
+		// the URL will most likely be to a local file copy containing the
+		// unpacked feature jar.
+		ContentReference baseRef =
+			feature.getFeatureContentProvider().getFeatureManifestReference(null);
+		URL base = null;
+		if (baseRef != null)
+			base = baseRef.asURL();
+		if (base == null)
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.InstallHandler_unableToCreateHandler, (new String[] { this.feature.getLabel() })),
+				null);
+
+
+		// determine loader class path
+		StringTokenizer libraries = new StringTokenizer(libs, ","); //$NON-NLS-1$
+		URL[] cp = new URL[libraries.countTokens()];
+		for( int token = 0; token < cp.length; token++) {
+			cp[token] = new URL(base, libraries.nextToken());
+		}
+		if (this.type == IInstallHandler.HANDLER_ACTION_UNINSTALL) {
+			// check if we are doing uninstall
+			// ... need to make temp copy of library (being removed)
+			URL[] jars = new URL[cp.length];
+			for( int jar = 0; jar < cp.length; jar++) {
+				File tempLib = File.createTempFile("tmp" + jar, ".jar"); //$NON-NLS-1$ //$NON-NLS-2$
+				tempLib.deleteOnExit();
+				FileOutputStream fos = null;
+				InputStream is = null;
+				try {
+					fos = new FileOutputStream(tempLib);
+					is = new FileInputStream(cp[jar].getPath());
+					Utilities.copy(is, fos, null);
+				} finally {
+					if (fos != null)
+						try {
+							fos.close();
+						} catch (Exception e) {
+						}
+					if (is != null)
+						try {
+							is.close();
+						} catch (Exception e) {
+						}
+				}
+				jars[jar] = tempLib.toURL();
+			}
+			cp = jars;
+		}
+
+		// create class loader, load and instantiate handler
+		ClassLoader loader = new InstallHandlerClassLoader(cp);
+		Class clazz = loader.loadClass(name);
+		IInstallHandler handler = (IInstallHandler) clazz.newInstance();
+		return handler;
+	}
+
+	/*
+	 * get instance of global handler registered via extension point
+	 */
+	private IInstallHandler getGlobalHandler(String name) throws Exception {
+
+		IExtensionRegistry reg = Platform.getExtensionRegistry();
+		IConfigurationElement[] handlerExtension =
+			reg.getConfigurationElementsFor(EXT_PLUGIN, EXT_POINT, name);
+		if (handlerExtension == null || handlerExtension.length <= 0)
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.InstallHandler_unableToCreateHandler, (new String[] { this.feature.getLabel() })),
+				null);
+
+		return (IInstallHandler) handlerExtension[0].createExecutableExtension("class"); //$NON-NLS-1$
+	}
+	
+	private void debug(String s) {
+		String pfx = (feature==null) ? "" : feature.getVersionedIdentifier().toString(); //$NON-NLS-1$
+		System.out.println("InstallHandler["+pfx+"]: " + s); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	public boolean acceptNonPluginData(INonPluginEntry data) {
+		Boolean result = new Boolean(true);
+		if (handler != null){
+			if (DEBUG)
+				debug("calling acceptNonPluginData()"); //$NON-NLS-1$
+			if(handler instanceof IInstallHandlerWithFilter)
+				return ((IInstallHandlerWithFilter)handler).acceptNonPluginData(data);
+			else{ //support upgrade from legacy versions
+				if(getNonPluginDataAcceptor() != null){
+					try{
+						Object[] param = {data};
+						result = (Boolean)getNonPluginDataAcceptor().invoke(handler,param);
+					}catch(Exception e){
+						//todo
+					}
+				}
+			}
+		}
+		return result.booleanValue();
+	}
+	private Method getNonPluginDataAcceptor(){
+		if(nonPluginDataAcceptor == null){
+			try{
+				Class[] types = {INonPluginEntry.class};
+				nonPluginDataAcceptor = handler.getClass().getMethod("acceptNonPluginData",types); //$NON-NLS-1$
+			}catch(NoSuchMethodException nsme){
+			}
+		}
+		return nonPluginDataAcceptor;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallLogParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallLogParser.java
new file mode 100644
index 0000000..495d38c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallLogParser.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.*;
+import java.net.*;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.configurator.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.operations.*;
+
+
+/**
+ * Parses the installation log and creates installation configuration objects
+ */
+public class InstallLogParser {
+	private IPath logPath;
+	private BufferedReader buffRead;
+	private InstallConfiguration currentConfiguration;
+	private HashMap installConfigMap;
+	private Comparator comparator;
+	
+	private static final String FEATURE_INSTALL = "feature-install"; //$NON-NLS-1$
+	private static final String FEATURE_REMOVE = "feature-remove"; //$NON-NLS-1$
+	private static final String SITE_INSTALL = "site-install"; //$NON-NLS-1$
+	private static final String SITE_REMOVE = "site-remove"; //$NON-NLS-1$
+	private static final String UNCONFIGURE = "feature-disable"; //$NON-NLS-1$
+	private static final String CONFIGURE = "feature-enable"; //$NON-NLS-1$
+	private static final String REVERT = "revert"; //$NON-NLS-1$
+	private static final String RECONCILIATION = "reconciliation"; //$NON-NLS-1$
+	private static final String PRESERVED = "preserve-configuration"; //$NON-NLS-1$	
+	
+	private static final String ACTIVITY = "!ACTIVITY"; //$NON-NLS-1$
+	
+	public static final String SUCCESS = "success"; //$NON-NLS-1$
+	public static final String FAILURE = "failure"; //$NON-NLS-1$
+
+	
+	public InstallLogParser(){
+		String loc = ConfiguratorUtils.getCurrentPlatformConfiguration().getConfigurationLocation().getFile();
+		logPath = new Path(loc).removeLastSegments(1).append("install.log");  //$NON-NLS-1$
+		installConfigMap = new HashMap();
+		try {
+			InstallConfiguration[] configs = (InstallConfiguration[])SiteManager.getLocalSite().getConfigurationHistory();
+			for (int i=0;i<configs.length; i++){
+				if (!configs[i].isCurrent())
+					installConfigMap.put(new Long(configs[i].getCreationDate().getTime()), configs[i]);
+			}
+			// Need to make a copy of the current config instead
+			InstallConfiguration config = getConfigCopy((InstallConfiguration)SiteManager.getLocalSite().getCurrentConfiguration());
+			installConfigMap.put(new Long(config.getCreationDate().getTime()), config);
+			
+		} catch (CoreException e) {
+			UpdateCore.log(e);
+		} catch (MalformedURLException e){
+			UpdateCore.log(e);
+		}
+		comparator = new Comparator(){
+			public int compare(Object e1, Object e2) {
+				Date date1 = ((InstallConfiguration)e1).getCreationDate();
+				Date date2 = ((InstallConfiguration)e2).getCreationDate();
+				return date1.before(date2) ? 1 : -1;
+			}
+		};
+	}
+	private InstallConfiguration getConfigCopy(InstallConfiguration origConfig) throws CoreException, MalformedURLException{
+		InstallConfiguration config = new InstallConfiguration(origConfig, origConfig.getURL(), origConfig.getLabel() );
+		config.setCreationDate(origConfig.getCreationDate());
+		return config;
+	}
+	public void parseInstallationLog(){
+		try {
+			openLog();
+			parseLog();
+		} catch (CoreException e) {
+			UpdateUtils.logException(e);
+		} finally {
+			closeLog();
+		}
+	}
+	
+	private void openLog() throws CoreException {
+		try {
+		    // throws FileNotFoundException, IOException
+		    InputStream is = new FileInputStream(logPath.toOSString());
+		    // throws UnsupportedEncodingException
+		    InputStreamReader isr = new InputStreamReader(is,"UTF-8"); //$NON-NLS-1$
+		    buffRead = new BufferedReader(isr);
+		} catch (Exception e) {
+			throwCoreException(e);
+		}
+	}
+	
+	private void throwCoreException(Throwable e) throws CoreException {
+		throw new CoreException(
+			new Status(
+				IStatus.ERROR,
+				UpdateUtils.getPluginId(),
+				IStatus.ERROR,
+				Messages.InstallLogParser_errors, 
+				e));
+	}
+	
+	private void parseLog() throws CoreException {
+		// 		.install-log template
+		//		!CONFIGURATION <configuration-date>
+		//		!ACTIVITY <date> <target> <action> <status>
+
+		try {
+			String type, status, action;
+			StringTokenizer htmlCode;
+
+			while (buffRead.ready()) {
+
+				htmlCode = new StringTokenizer(buffRead.readLine());
+				while (!(htmlCode.hasMoreElements())) {
+					if (!buffRead.ready())
+						return;
+					htmlCode = new StringTokenizer(buffRead.readLine());
+				}
+
+				type = htmlCode.nextToken().trim();
+
+				if (type.equals(ACTIVITY)) {
+					String time = htmlCode.nextToken();
+					String date;
+					StringBuffer target = new StringBuffer();
+					date = htmlCode.nextToken(".");  //$NON-NLS-1$
+					htmlCode.nextToken(" ");  //$NON-NLS-1$
+					while (htmlCode.countTokens() > 2){
+						target.append(" "); //$NON-NLS-1$
+						target.append(htmlCode.nextToken());
+					}
+					
+					action = htmlCode.nextToken();
+					status = htmlCode.nextToken();
+					createActivity(action, time, date, status, target.toString(), currentConfiguration);
+				}  else {
+					String time = htmlCode.nextToken();
+					StringBuffer date;
+					date = new StringBuffer();
+					while (htmlCode.countTokens() > 0){
+						if (date.length() != 0)
+							date.append(" "); //$NON-NLS-1$
+						date.append(htmlCode.nextToken());
+					}
+					currentConfiguration = (InstallConfiguration)installConfigMap.get(new Long(time));
+				}
+			}
+		} catch (Exception e) {
+			throwCoreException(e);
+		}
+	}
+	
+	private void closeLog() {
+		try {
+			if (buffRead != null)
+				buffRead.close();
+		} catch (IOException e) {
+		} finally {
+			buffRead = null;
+		}
+	}
+	private IActivity createActivity(String action, String time, String date, String status, String target, InstallConfiguration config){
+		ConfigurationActivity a = new ConfigurationActivity();
+
+		int code = 0;
+		if (FEATURE_INSTALL.equals(action))
+			code = IActivity.ACTION_FEATURE_INSTALL;
+		else if (FEATURE_REMOVE.equals(action))
+			code = IActivity.ACTION_FEATURE_REMOVE;
+		else if (SITE_INSTALL.equals(action))
+			code = IActivity.ACTION_SITE_INSTALL;
+		else if (SITE_REMOVE.equals(action))
+			code = IActivity.ACTION_SITE_REMOVE;
+		else if (UNCONFIGURE.equals(action))
+			code = IActivity.ACTION_UNCONFIGURE;
+		else if (CONFIGURE.equals(action))
+			code = IActivity.ACTION_CONFIGURE;
+		else if (REVERT.equals(action))
+			code = IActivity.ACTION_REVERT;
+		else if (RECONCILIATION.equals(action))
+			code = IActivity.ACTION_RECONCILIATION;
+		else if (PRESERVED.equals(action))
+			code = IActivity.ACTION_ADD_PRESERVED;
+		
+		a.setAction(code);
+		try {
+			long activityTime = Long.parseLong(time);
+			a.setDate(new Date(activityTime));
+		} catch (NumberFormatException e) {
+			//PAL foundation
+			//a.setDate(new Date(date));
+			try {
+				a.setDate(new SimpleDateFormat().parse(date));
+			} catch (ParseException e1) {
+				//ignore
+			}
+		}
+		a.setStatus(SUCCESS.equals(status) ? IActivity.STATUS_OK : IActivity.STATUS_NOK);
+		a.setLabel(target);
+		a.setInstallConfigurationModel(config);
+		
+		if (config != null && !configContainsActivity(config, a)){
+			config.addActivity(a);
+		}
+		
+		return a;
+	}
+	
+	private boolean configContainsActivity(InstallConfiguration c, IActivity a){
+		IActivity[] activities = c.getActivities();
+		for (int i = 0 ; i<activities.length; i++){
+			if (a.equals(activities[i]))
+				return true;
+		}
+		return false;
+	}
+
+	public InstallConfiguration[] getConfigurations(){
+		Collection configSet = installConfigMap.values();
+		InstallConfiguration[] configs = (InstallConfiguration[]) configSet.toArray(new InstallConfiguration[configSet.size()]);
+		Arrays.sort(configs, comparator);
+		return configs;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallRegistry.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallRegistry.java
new file mode 100644
index 0000000..94e73e9
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InstallRegistry.java
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.eclipse.update.configurator.*;
+import org.eclipse.update.core.*;
+
+/**
+ * Keeps track of all the features and plugins installed by Update mgr
+ * so they can be uninstalled later.
+ * The info is persisted in the .config/registry file and each entry has a key=key where
+ * for feature this key is feature_<id>_<version> and for plugins
+ * key is plugin_<id>_<version>. Normally, getVersionedIdentifier() will
+ * return <id>_<version>. Eg: feature_org.eclipse.platform_3.0.0
+ * 
+ */
+public class InstallRegistry extends Properties {
+
+    private static final long serialVersionUID = 1L;
+    private File file = null;
+	private final static String REGISTRY = "registry"; //$NON-NLS-1$
+	private static InstallRegistry instance;
+	
+	// plugins installed in this eclipse session
+	private HashMap justInstalledPlugins = new HashMap();
+	
+	/**
+	 * Creates empty Properties.
+	 */
+	private InstallRegistry() {
+		super();
+		String configFile =
+			ConfiguratorUtils
+				.getCurrentPlatformConfiguration()
+				.getConfigurationLocation()
+				.getFile();
+		file = new File(configFile);
+		file = file.getParentFile();
+		file = new File(file, REGISTRY);
+		restore();
+	}
+
+	/**
+	 * Singleton
+	 */
+	public static InstallRegistry getInstance() {
+		if (instance == null)
+			instance = new InstallRegistry();
+		return instance;
+	}
+
+	/**
+	 * Restores contents of the Properties from a file.
+	 * @return true if persistant data was read in
+	 */
+	public boolean restore() {
+		InputStream in = null;
+		boolean loaded = false;
+		clear();
+		// Test if we have a contribution file to start with
+		// If this is a clean start, then we will not have a 
+		// contribution file. return false.
+		if (!file.exists())
+			return loaded;
+		try {
+			in = new FileInputStream(file);
+			super.load(in);
+			loaded = true;
+		} catch (IOException e) {
+			UpdateCore.log(e);
+		} finally {
+			if (in != null)
+				try {
+					in.close();
+				} catch (IOException e) {
+				}
+		}
+		return loaded;
+	}
+	/**
+	 * Saves contents of the table to a file.
+	 * @return true if operation was successful
+	 */
+	public synchronized boolean save() {
+		OutputStream out = null;
+		boolean ret = false;
+		try {
+			out = new FileOutputStream(file);
+			super.store(out, "This is a generated file; do not edit."); //$NON-NLS-1$
+			ret = true;
+		} catch (IOException e) {
+			UpdateCore.log(e);
+		} finally {
+			try {
+				if (out != null) {
+					out.close();
+				}
+			} catch (IOException e) {
+			}
+		}
+		return ret;
+	}
+	
+	/**
+	 * Registers an installed feature so it can be uninstalled later.
+	 * @param feature feature to register.
+	 */
+	public static synchronized void registerFeature(IFeature feature) {
+		String name = "feature_"+feature.getVersionedIdentifier(); //$NON-NLS-1$
+		if (InstallRegistry.getInstance().get(name) == null) {
+			InstallRegistry.getInstance().put(name, name);
+			// we save after each registration
+			InstallRegistry.getInstance().save();
+		}
+	}
+	
+	/**
+	 * Registers an installed feature so it can be uninstalled later.
+	 * @param pluginEntry plugin to register.
+	 */
+	public static synchronized void registerPlugin(IPluginEntry pluginEntry) {
+		String name = "plugin_"+pluginEntry.getVersionedIdentifier(); //$NON-NLS-1$
+		if (InstallRegistry.getInstance().get(name) == null) {
+			InstallRegistry.getInstance().put(name, name);
+			// we save after each registration
+			InstallRegistry.getInstance().save();
+		}
+		
+		// add plugin to the list of just installed plugins .
+		InstallRegistry.getInstance().justInstalledPlugins.put(name,name);
+	}
+	
+	/**
+	 * Removes specified feature from registry
+	 *
+	 */
+	public static synchronized void unregisterFeature(IFeature feature) {
+		String name = "feature_"+feature.getVersionedIdentifier(); //$NON-NLS-1$
+		InstallRegistry.getInstance().remove(name);
+	}
+	
+	/**
+	 * Removes specified plugin from registry
+	 *
+	 */
+	public static synchronized void unregisterPlugin(IPluginEntry pluginEntry) {
+		String name = "plugin_"+pluginEntry.getVersionedIdentifier(); //$NON-NLS-1$
+		InstallRegistry.getInstance().remove(name);
+		
+		// remove the plugin from the list of just installed plugins (if needed).
+		InstallRegistry.getInstance().justInstalledPlugins.remove(name);
+	}
+	
+	/**
+	 * Returns true if the plugin was installed during this eclipse session
+	 * @param pluginEntry
+	 * @return
+	 */
+	public boolean isPluginJustInstalled(IPluginEntry pluginEntry) {
+		String name = "plugin_"+pluginEntry.getVersionedIdentifier(); //$NON-NLS-1$
+		return InstallRegistry.getInstance().justInstalledPlugins.get(name) != null;
+	}
+	
+	/**
+	 * This method is only needed for the update JUnit tests.
+	 *
+	 */
+	public static void cleanup() {
+		InstallRegistry.getInstance().justInstalledPlugins.clear();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InternalFeatureParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InternalFeatureParser.java
new file mode 100644
index 0000000..dd71997
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InternalFeatureParser.java
@@ -0,0 +1,1281 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.update.internal.core;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Stack;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.IURLEntry;
+import org.eclipse.update.core.IUpdateConstants;
+import org.eclipse.update.core.model.ContentEntryModel;
+import org.eclipse.update.core.model.FeatureModel;
+import org.eclipse.update.core.model.FeatureModelFactory;
+import org.eclipse.update.core.model.ImportModel;
+import org.eclipse.update.core.model.IncludedFeatureReferenceModel;
+import org.eclipse.update.core.model.InstallHandlerEntryModel;
+import org.eclipse.update.core.model.NonPluginEntryModel;
+import org.eclipse.update.core.model.PluginEntryModel;
+import org.eclipse.update.core.model.URLEntryModel;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Default feature parser.
+ * Parses the feature manifest file as defined by the platform. Defers
+ * to a model factory to create the actual concrete model objects. The 
+ * update framework supplies two factory implementations:
+ * <ul>
+ * <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>
+ * <li>@see org.eclipse.update.core.model.FeatureModelFactory
+ * <li>@see org.eclipse.update.core.BaseFeatureFactory
+ * </ul>
+ * 
+ * @since 2.0
+ */
+public class InternalFeatureParser extends DefaultHandler {
+
+	private SAXParser parser;
+	private FeatureModelFactory factory;
+	private MultiStatus status;
+
+	private boolean URL_ALREADY_SEEN = false;
+
+	private static final int STATE_IGNORED_ELEMENT = -1;
+	private static final int STATE_INITIAL = 0;
+	private static final int STATE_INCLUDES = 1;
+	private static final int STATE_FEATURE = 2;
+	private static final int STATE_HANDLER = 3;
+	private static final int STATE_DESCRIPTION = 4;
+	private static final int STATE_COPYRIGHT = 5;
+	private static final int STATE_LICENSE = 6;
+	private static final int STATE_URL = 7;
+	private static final int STATE_UPDATE = 8;
+	private static final int STATE_DISCOVERY = 9;
+	private static final int STATE_REQUIRES = 10;
+	private static final int STATE_IMPORT = 11;
+	private static final int STATE_PLUGIN = 12;
+	private static final int STATE_DATA = 13;
+	private static final String PLUGIN_ID = UpdateCore.getPlugin().getBundle().getSymbolicName();
+
+	private static final String FEATURE = "feature"; //$NON-NLS-1$
+	private static final String INCLUDES = "includes"; //$NON-NLS-1$
+	private static final String HANDLER = "install-handler"; //$NON-NLS-1$
+	private static final String DESCRIPTION = "description"; //$NON-NLS-1$
+	private static final String COPYRIGHT = "copyright"; //$NON-NLS-1$
+	private static final String LICENSE = "license"; //$NON-NLS-1$
+	private static final String URL = "url"; //$NON-NLS-1$
+	private static final String UPDATE = "update"; //$NON-NLS-1$
+	private static final String DISCOVERY = "discovery"; //$NON-NLS-1$
+	private static final String REQUIRES = "requires"; //$NON-NLS-1$
+	private static final String IMPORT = "import"; //$NON-NLS-1$
+	private static final String PLUGIN = "plugin"; //$NON-NLS-1$
+	private static final String DATA = "data"; //$NON-NLS-1$
+	
+	//debug message
+	private static final String UNSUPPORTED_FILTER_MSG = "Unsupported \"filter\" attribute, ignoring {0}, with id {1}, version {2}";
+	
+	// Current State Information
+	Stack stateStack = new Stack();
+
+	// Current object stack (used to hold the current object we are
+	// populating in this plugin descriptor
+	Stack objectStack = new Stack();
+
+	private int currentState;
+    private String location;
+    private boolean ignoredElement = false;
+	
+	private final static SAXParserFactory parserFactory =
+		SAXParserFactory.newInstance();
+
+	/**
+	 * Constructs a feature parser.
+	 * 
+	 * @since 2.0
+	 */
+	public InternalFeatureParser() {
+		super();
+		try {
+			parserFactory.setNamespaceAware(true);
+			this.parser = parserFactory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+			UpdateCore.log(e);
+		} catch (SAXException e) {
+			UpdateCore.log(e);
+		}
+	}
+
+	public void init(FeatureModelFactory factory) {
+		init(factory, null);
+	}
+    
+    /**
+     * @param factory
+     * @param location
+     * @since 3.1
+     */
+    public void init(FeatureModelFactory factory, String location) {
+        // PERF: separate instance creation from parsing
+        this.factory = factory;
+        stateStack = new Stack();
+        objectStack = new Stack();
+        status = null;
+        URL_ALREADY_SEEN = false;
+        this.location = location;
+        //parser.reset();
+    }
+    
+    public void internalInit(FeatureModelFactory factory, String location) {
+    	init(factory, location);
+    	stateStack.push(new Integer(STATE_INITIAL));
+		currentState = ((Integer) stateStack.peek()).intValue();
+    }
+    
+    public FeatureModel getFeatureModel() throws SAXException {
+    	if (objectStack.isEmpty())
+			throw new SAXException(Messages.DefaultFeatureParser_NoFeatureTag); 
+		else {
+			if (objectStack.peek() instanceof FeatureModel) {
+				return (FeatureModel) objectStack.pop();
+			} else {
+				String stack = ""; //$NON-NLS-1$
+				Iterator iter = objectStack.iterator();
+				while (iter.hasNext()) {
+					stack = "\r\n" + iter.next().toString() + stack; //$NON-NLS-1$
+				}
+				throw new SAXException(NLS.bind(Messages.DefaultFeatureParser_WrongParsingStack, (new String[] { stack })));
+			}
+		}
+    }
+
+	/**
+	 * Parses the specified input steam and constructs a feature model.
+	 * The input stream is not closed as part of this operation.
+	 * 
+	 * @param in input stream
+	 * @return feature model
+	 * @exception SAXException
+	 * @exception IOException
+	 * @since 2.0
+	 */
+	public FeatureModel parse(InputStream in) throws SAXException, IOException {
+		stateStack.push(new Integer(STATE_INITIAL));
+		currentState = ((Integer) stateStack.peek()).intValue();
+		parser.parse(new InputSource(in), this);
+		return getFeatureModel();
+	}
+
+	/**
+	 * Returns all status objects accumulated by the parser.
+	 *
+	 * @return multi-status containing accumulated status, or <code>null</code>.
+	 * @since 2.0
+	 */
+	public MultiStatus getStatus() {
+		return status;
+	}
+
+	/**
+	 * Handle start of element tags
+	 * @see DefaultHandler#startElement(String, String, String, Attributes)
+	 * @since 2.0
+	 */
+	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("Start Element: uri:" + uri + " local Name:" + localName + " qName:" + qName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+		switch (currentState) {
+			case STATE_IGNORED_ELEMENT :
+				internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownElement, (new String[] { localName, getState(currentState) })));
+				break;
+
+			case STATE_INITIAL :
+				handleInitialState(localName, attributes);
+				break;
+
+			case STATE_FEATURE :
+			case STATE_INCLUDES :
+			case STATE_HANDLER :
+			case STATE_DESCRIPTION :
+			case STATE_COPYRIGHT :
+			case STATE_LICENSE :
+				handleFeatureState(localName, attributes);
+				break;
+
+			case STATE_URL :
+				if (URL_ALREADY_SEEN)
+					internalError(Messages.DefaultFeatureParser_TooManyURLtag); 
+				handleURLState(localName, attributes);
+				break;
+
+			case STATE_UPDATE :
+			case STATE_DISCOVERY :
+				handleUpdateDiscoveryState(localName, attributes);
+				break;
+
+			case STATE_REQUIRES :
+				handleRequiresState(localName, attributes);
+				break;
+
+			case STATE_IMPORT :
+				handleImportState(localName,attributes);
+				break;
+				
+			case STATE_PLUGIN :
+			case STATE_DATA :
+				handleFeatureState(localName, attributes);
+				break;
+
+			default :
+				internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownStartState, (new String[] { Integer.toString(currentState) })));
+				break;
+		}
+
+		int newState = ((Integer) stateStack.peek()).intValue();
+		if (newState != STATE_IGNORED_ELEMENT)
+			currentState = newState;
+
+	}
+
+	/**
+	 * Handle end of element tags
+	 * @see DefaultHandler#endElement(String, String, String)
+	 * @since 2.0
+	 */
+	public void endElement(String uri, String localName, String qName) {
+
+		// variables used
+		URLEntryModel info = null;
+		FeatureModel featureModel = null;
+		String text = null;
+		int innerState = 0;
+
+		int state = ((Integer) stateStack.peek()).intValue();
+		switch (state) {
+			case STATE_IGNORED_ELEMENT :
+				stateStack.pop();
+				break;
+
+			case STATE_INITIAL :
+				internalError(Messages.DefaultFeatureParser_ParsingStackBackToInitialState); 
+				break;
+
+			case STATE_FEATURE :
+				stateStack.pop();
+				if (objectStack.peek() instanceof String) {
+					text = (String) objectStack.pop();
+					FeatureModel feature = (FeatureModel) objectStack.peek();
+					feature.getDescriptionModel().setAnnotation(text);
+				}
+				//do not pop
+				break;
+
+			case STATE_INCLUDES :
+				stateStack.pop();
+				if (objectStack.peek() instanceof IncludedFeatureReferenceModel) {
+					IncludedFeatureReferenceModel includedFeatureRefModel = ((IncludedFeatureReferenceModel) objectStack.pop());
+					if (objectStack.peek() instanceof FeatureModel) {
+						featureModel = (FeatureModel) objectStack.peek();
+						featureModel.addIncludedFeatureReferenceModel(includedFeatureRefModel);
+					}
+				}
+				break;
+
+			case STATE_HANDLER :
+				stateStack.pop();
+				if (objectStack.peek() instanceof InstallHandlerEntryModel) {
+					InstallHandlerEntryModel handlerModel = (InstallHandlerEntryModel) objectStack.pop();
+					featureModel = (FeatureModel) objectStack.peek();
+					if (featureModel.getInstallHandlerModel() != null)
+						internalError(NLS.bind(Messages.DefaultFeatureParser_ElementAlreadySet, (new String[] { getState(state) })));
+					else
+						featureModel.setInstallHandlerModel(handlerModel);
+				}
+				break;
+
+			case STATE_DESCRIPTION :
+				stateStack.pop();
+
+				text = ""; //$NON-NLS-1$
+				while (objectStack.peek() instanceof String) {
+					text = (String) objectStack.pop() + text;
+				}
+				if (objectStack.peek() instanceof URLEntryModel) {
+					info = (URLEntryModel) objectStack.pop();
+					text = cleanupText(text);
+					if (text != null)
+						info.setAnnotation(text);
+
+					innerState = ((Integer) stateStack.peek()).intValue();
+					switch (innerState) {
+						case STATE_FEATURE :
+							if (objectStack.peek() instanceof FeatureModel) {
+								featureModel = (FeatureModel) objectStack.peek();
+								if (featureModel.getDescriptionModel() != null)
+									internalError(NLS.bind(Messages.DefaultFeatureParser_ElementAlreadySet, (new String[] { getState(state) })));
+								else
+									featureModel.setDescriptionModel(info);
+							}
+							break;
+
+						default :
+							internalError(NLS.bind(Messages.DefaultFeatureParser_StateIncludeWrongElement, (new String[] { getState(innerState), getState(state) })));
+							break;
+
+					}
+				}
+				break;
+
+			case STATE_COPYRIGHT :
+				stateStack.pop();
+				text = ""; //$NON-NLS-1$
+				while (objectStack.peek() instanceof String) {
+					text = (String) objectStack.pop() + text;
+				}
+				if (objectStack.peek() instanceof URLEntryModel) {
+					info = (URLEntryModel) objectStack.pop();
+					text = cleanupText(text);
+					if (text != null) {
+						info.setAnnotation(text);
+					}
+
+					innerState = ((Integer) stateStack.peek()).intValue();
+					switch (innerState) {
+						case STATE_FEATURE :
+							if (objectStack.peek() instanceof FeatureModel) {
+								featureModel = (FeatureModel) objectStack.peek();
+								if (featureModel.getCopyrightModel() != null)
+									internalError(NLS.bind(Messages.DefaultFeatureParser_ElementAlreadySet, (new String[] { getState(state) })));
+								else
+									featureModel.setCopyrightModel(info);
+							}
+							break;
+
+						default :
+							internalError(NLS.bind(Messages.DefaultFeatureParser_StateIncludeWrongElement, (new String[] { getState(innerState), getState(state) })));
+							break;
+
+					}
+				}
+				break;
+
+			case STATE_LICENSE :
+				stateStack.pop();
+
+				text = ""; //$NON-NLS-1$
+				while (objectStack.peek() instanceof String) {
+					text = (String) objectStack.pop() + text;
+				}
+				if (objectStack.peek() instanceof URLEntryModel) {
+					info = (URLEntryModel) objectStack.pop();
+					text = cleanupText(text);
+					if (text != null) {
+						info.setAnnotation(text);
+					}
+
+					innerState = ((Integer) stateStack.peek()).intValue();
+					switch (innerState) {
+						case STATE_FEATURE :
+							if (objectStack.peek() instanceof FeatureModel) {
+								featureModel = (FeatureModel) objectStack.peek();
+								if (featureModel.getLicenseModel() != null)
+									internalError(NLS.bind(Messages.DefaultFeatureParser_ElementAlreadySet, (new String[] { getState(state) })));
+								else
+									featureModel.setLicenseModel(info);
+							}
+							break;
+
+						default :
+							internalError(NLS.bind(Messages.DefaultFeatureParser_StateIncludeWrongElement, (new String[] { getState(innerState), getState(state) })));
+							break;
+
+					}
+				}
+				break;
+
+			case STATE_URL :
+				stateStack.pop();
+				URL_ALREADY_SEEN = true;
+				break;
+
+			case STATE_UPDATE :
+				stateStack.pop();
+				if (objectStack.peek() instanceof URLEntryModel) {
+					info = (URLEntryModel) objectStack.pop();
+					if (objectStack.peek() instanceof FeatureModel) {
+						featureModel = (FeatureModel) objectStack.peek();
+						if (featureModel.getUpdateSiteEntryModel() != null) {
+							internalError(NLS.bind(Messages.DefaultFeatureParser_ElementAlreadySet, (new String[] { getState(state) })));
+						} else {
+							featureModel.setUpdateSiteEntryModel(info);
+						}
+					}
+				}
+				break;
+
+			case STATE_DISCOVERY :
+				stateStack.pop();
+				if (objectStack.peek() instanceof URLEntryModel) {
+					info = (URLEntryModel) objectStack.pop();
+					if (objectStack.peek() instanceof FeatureModel) {
+						featureModel = (FeatureModel) objectStack.peek();
+						featureModel.addDiscoverySiteEntryModel(info);
+					}
+				}
+				break;
+
+			case STATE_REQUIRES :
+				stateStack.pop();
+				if (objectStack.peek() instanceof FeatureModel) {
+					boolean foundIgnored = ignoredElement;
+					ignoredElement = false;
+					featureModel = (FeatureModel) objectStack.peek();
+					ImportModel[] importModels = featureModel.getImportModels();
+					if (importModels.length == 0) {
+						if (!foundIgnored) //don't report error if we ignored something
+							internalError(Messages.DefaultFeatureParser_RequireStateWithoutImportElement);
+					} else {
+						boolean patchMode = false;
+						for (int i = 0; i < importModels.length; i++) {
+							ImportModel importModel = importModels[i];
+							if (importModel.isPatch()) {
+								if (patchMode == false)
+									patchMode = true;
+								else {
+									internalError(Messages.DefaultFeatureParser_MultiplePatchImports); 
+									break;
+								}
+							}
+						}
+					}
+				}
+				break;
+
+			case STATE_IMPORT :
+				stateStack.pop();
+				if (objectStack.peek() instanceof ImportModel) {
+					ImportModel importModel = (ImportModel) objectStack.pop();
+					if (objectStack.peek() instanceof FeatureModel) {
+						featureModel = (FeatureModel) objectStack.peek();
+						featureModel.addImportModel(importModel);
+					}
+				}
+				break;
+
+			case STATE_PLUGIN :
+				stateStack.pop();
+				if (objectStack.peek() instanceof PluginEntryModel) {
+					PluginEntryModel pluginEntry = (PluginEntryModel) objectStack.pop();
+					if (objectStack.peek() instanceof FeatureModel) {
+						featureModel = (FeatureModel) objectStack.peek();
+						featureModel.addPluginEntryModel(pluginEntry);
+					}
+				}
+				break;
+
+			case STATE_DATA :
+				stateStack.pop();
+				if (objectStack.peek() instanceof NonPluginEntryModel) {
+					NonPluginEntryModel nonPluginEntry = (NonPluginEntryModel) objectStack.pop();
+					if (objectStack.peek() instanceof FeatureModel) {
+						featureModel = (FeatureModel) objectStack.peek();
+						featureModel.addNonPluginEntryModel(nonPluginEntry);
+					}
+				}
+				break;
+
+			default :
+				internalErrorUnknownTag(Messages.DefaultFeatureParser_UnknownEndState + state);
+				break;
+		}
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("End Element:" + uri + ":" + localName + ":" + qName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+	
+	/*
+	 * Method cleanupText.
+	 * Removes pre white space and post white space
+	 * return null if the text only contains whitespaces (\t \r\n and spaces)
+	 * 
+	 * @param text or null
+	 * @return String
+	 */
+	private String cleanupText(String text) {
+		text = text.trim();
+		if ("".equals(text)) return null; //$NON-NLS-1$
+		return text;
+	}
+
+	/**
+	 * Handle character text
+	 * @see DefaultHandler#characters(char[], int, int)
+	 * @since 2.0
+	 */
+	public void characters(char[] ch, int start, int length) {
+		String text = ""; //$NON-NLS-1$
+		boolean valid = true;
+
+		if (valid) {
+			text = new String(ch, start, length);
+		}
+
+		//only push if not unknown state		
+		int state = ((Integer) stateStack.peek()).intValue();
+		if (state == STATE_DESCRIPTION || state == STATE_COPYRIGHT || state == STATE_LICENSE)
+			objectStack.push(text);
+
+	}
+
+	/**
+	 * Handle errors
+	 * @see DefaultHandler#error(SAXParseException)
+	 * @since 2.0
+	 */
+	public void error(SAXParseException ex) {
+		logStatus(ex);
+	}
+
+	/**
+	 * Handle fatal errors
+	 * @see DefaultHandler#fatalError(SAXParseException)
+	 * @exception SAXException
+	 * @since 2.0
+	 */
+	public void fatalError(SAXParseException ex) throws SAXException {
+		logStatus(ex);
+		throw ex;
+	}
+
+	private void handleInitialState(String elementName, Attributes attributes) throws SAXException {
+		if (elementName.equals(FEATURE)) {
+			stateStack.push(new Integer(STATE_FEATURE));
+			processFeature(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownElement, (new String[] { elementName, getState(currentState) })));
+	}
+
+	private void handleFeatureState(String elementName, Attributes attributes) throws SAXException {
+		if (elementName.equals(HANDLER)) {
+			stateStack.push(new Integer(STATE_HANDLER));
+			processHandler(attributes);
+		} else if (elementName.equals(DESCRIPTION)) {
+			stateStack.push(new Integer(STATE_DESCRIPTION));
+			processInfo(attributes);
+		} else if (elementName.equals(COPYRIGHT)) {
+			stateStack.push(new Integer(STATE_COPYRIGHT));
+			processInfo(attributes);
+		} else if (elementName.equals(LICENSE)) {
+			stateStack.push(new Integer(STATE_LICENSE));
+			processInfo(attributes);
+		} else if (elementName.equals(URL)) {
+			stateStack.push(new Integer(STATE_URL));
+			//No process as URL tag does not contain any element itself
+		} else if (elementName.equals(INCLUDES)) {
+			stateStack.push(new Integer(STATE_INCLUDES));
+			processIncludes(attributes);
+		} else if (elementName.equals(REQUIRES)) {
+			stateStack.push(new Integer(STATE_REQUIRES));
+			processRequire(attributes);
+		} else if (elementName.equals(PLUGIN)) {
+			stateStack.push(new Integer(STATE_PLUGIN));
+			processPlugin(attributes);
+		} else if (elementName.equals(DATA)) {
+			stateStack.push(new Integer(STATE_DATA));
+			processData(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownElement, (new String[] { elementName, getState(currentState) })));
+	}
+
+	private void handleURLState(String elementName, Attributes attributes) throws SAXException {
+		if (elementName.equals(UPDATE)) {
+			stateStack.push(new Integer(STATE_UPDATE));
+			processURLInfo(attributes);
+		} else if (elementName.equals(DISCOVERY)) {
+			stateStack.push(new Integer(STATE_DISCOVERY));
+			processURLInfo(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownElement, (new String[] { elementName, getState(currentState) })));
+	}
+
+	private void handleRequiresState(String elementName, Attributes attributes) throws SAXException {
+		if (elementName.equals(IMPORT)) {
+			stateStack.push(new Integer(STATE_IMPORT));
+			processImport(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownElement, (new String[] { elementName, getState(currentState) })));
+	}
+	private void handleUpdateDiscoveryState(String elementName, Attributes attributes) throws SAXException {
+		if (elementName.equals(HANDLER)) {
+			stateStack.push(new Integer(STATE_HANDLER));
+			processHandler(attributes);
+		} else if (elementName.equals(DESCRIPTION)) {
+			stateStack.push(new Integer(STATE_DESCRIPTION));
+			processInfo(attributes);
+		} else if (elementName.equals(COPYRIGHT)) {
+			stateStack.push(new Integer(STATE_COPYRIGHT));
+			processInfo(attributes);
+		} else if (elementName.equals(LICENSE)) {
+			stateStack.push(new Integer(STATE_LICENSE));
+			processInfo(attributes);
+		} else if (elementName.equals(URL)) {
+			stateStack.push(new Integer(STATE_URL));
+			//No process as URL tag does not contain any element itself
+		} else if (elementName.equals(INCLUDES)) {
+			stateStack.push(new Integer(STATE_INCLUDES));
+			processIncludes(attributes);
+		} else if (elementName.equals(REQUIRES)) {
+			stateStack.push(new Integer(STATE_REQUIRES));
+			processRequire(attributes);
+		} else if (elementName.equals(PLUGIN)) {
+			stateStack.push(new Integer(STATE_PLUGIN));
+			processPlugin(attributes);
+		} else if (elementName.equals(DATA)) {
+			stateStack.push(new Integer(STATE_DATA));
+			processData(attributes);
+		} else if (elementName.equals(UPDATE)) {
+			stateStack.push(new Integer(STATE_UPDATE));
+			processURLInfo(attributes);
+		} else if (elementName.equals(DISCOVERY)) {
+			stateStack.push(new Integer(STATE_DISCOVERY));
+			processURLInfo(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownElement, (new String[] { elementName, getState(currentState) })));
+        }
+
+
+
+	private void handleImportState(String elementName, Attributes attributes) throws SAXException {
+		if (elementName.equals(HANDLER)) {
+			stateStack.push(new Integer(STATE_HANDLER));
+			processHandler(attributes);
+		} else if (elementName.equals(DESCRIPTION)) {
+			stateStack.push(new Integer(STATE_DESCRIPTION));
+			processInfo(attributes);
+		} else if (elementName.equals(COPYRIGHT)) {
+			stateStack.push(new Integer(STATE_COPYRIGHT));
+			processInfo(attributes);
+		} else if (elementName.equals(LICENSE)) {
+			stateStack.push(new Integer(STATE_LICENSE));
+			processInfo(attributes);
+		} else if (elementName.equals(URL)) {
+			stateStack.push(new Integer(STATE_URL));
+			//No process as URL tag does not contain any element itself
+		} else if (elementName.equals(INCLUDES)) {
+			stateStack.push(new Integer(STATE_INCLUDES));
+			processIncludes(attributes);
+		} else if (elementName.equals(REQUIRES)) {
+			stateStack.push(new Integer(STATE_REQUIRES));
+			processRequire(attributes);
+		} else if (elementName.equals(PLUGIN)) {
+			stateStack.push(new Integer(STATE_PLUGIN));
+			processPlugin(attributes);
+		} else if (elementName.equals(DATA)) {
+			stateStack.push(new Integer(STATE_DATA));
+			processData(attributes);
+		} else if (elementName.equals(IMPORT)) {
+			stateStack.push(new Integer(STATE_IMPORT));
+			processImport(attributes);
+		} else
+			internalErrorUnknownTag(NLS.bind(Messages.DefaultFeatureParser_UnknownElement, (new String[] { elementName, getState(currentState) })));
+	}
+
+	/*
+	 * Process feature information
+	 */
+	private void processFeature(Attributes attributes) {
+
+		// identifier and version
+		String id = attributes.getValue("id"); //$NON-NLS-1$
+		String ver = attributes.getValue("version"); //$NON-NLS-1$
+
+		if (id == null || id.trim().equals("") //$NON-NLS-1$
+		|| ver == null || ver.trim().equals("")) { //$NON-NLS-1$
+			internalError(NLS.bind(Messages.DefaultFeatureParser_IdOrVersionInvalid, (new String[] { id, ver, getState(currentState)})));
+		} else {
+			// create feature model
+			FeatureModel feature = factory.createFeatureModel();
+
+			feature.setFeatureIdentifier(id);
+			feature.setFeatureVersion(ver);
+
+			// label
+			String label = attributes.getValue("label"); //$NON-NLS-1$
+			feature.setLabel(label);
+
+			// provider
+			String provider = attributes.getValue("provider-name"); //$NON-NLS-1$
+			feature.setProvider(provider);
+
+			//image
+			String imageURL = attributes.getValue("image"); //$NON-NLS-1$
+			feature.setImageURLString(imageURL);
+
+			// OS
+			String os = attributes.getValue("os"); //$NON-NLS-1$
+			feature.setOS(os);
+
+			// WS
+			String ws = attributes.getValue("ws"); //$NON-NLS-1$
+			feature.setWS(ws);
+
+			// NL
+			String nl = attributes.getValue("nl"); //$NON-NLS-1$
+			feature.setNL(nl);
+
+			// arch
+			String arch = attributes.getValue("arch"); //$NON-NLS-1$
+			feature.setArch(arch);
+
+			// primary
+			String primary = attributes.getValue("primary"); //$NON-NLS-1$
+			feature.setPrimary(primary != null && primary.trim().equalsIgnoreCase("true")); //$NON-NLS-1$
+
+			// exclusive
+			String exclusive = attributes.getValue("exclusive"); //$NON-NLS-1$
+			feature.setExclusive(exclusive != null && exclusive.trim().equalsIgnoreCase("true")); //$NON-NLS-1$
+
+			// application
+			String application = attributes.getValue("application"); //$NON-NLS-1$
+			feature.setApplication(application);
+
+			// affinity
+			String affinity = attributes.getValue("colocation-affinity"); //$NON-NLS-1$
+			feature.setAffinityFeature(affinity);
+
+			// primary plugin
+			String plugin = attributes.getValue("plugin"); //$NON-NLS-1$
+			feature.setPrimaryPluginID(plugin);
+
+			objectStack.push(feature);
+
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+				debug("End process DefaultFeature tag: id:" //$NON-NLS-1$
+				+id + " ver:" //$NON-NLS-1$
+				+ver + " label:" //$NON-NLS-1$
+				+label + " provider:" //$NON-NLS-1$
+				+provider);
+				debug("End process DefaultFeature tag: image:" + imageURL); //$NON-NLS-1$
+				debug("End process DefaultFeature tag: ws:" //$NON-NLS-1$
+				+ws + " os:" //$NON-NLS-1$
+				+os + " nl:" //$NON-NLS-1$
+				+nl + " application:" //$NON-NLS-1$
+				+application);
+			}
+		}
+	}
+
+	/* 
+	 * process URL info with element text
+	 */
+	private void processHandler(Attributes attributes) {
+		InstallHandlerEntryModel handler = factory.createInstallHandlerEntryModel();
+
+		String handlerURL = attributes.getValue("url"); //$NON-NLS-1$
+		handler.setURLString(handlerURL);
+
+		String library = attributes.getValue("library"); //$NON-NLS-1$
+		handler.setLibrary(library);
+
+		String clazz = attributes.getValue("handler"); //$NON-NLS-1$
+		handler.setHandlerName(clazz);
+
+		objectStack.push(handler);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("Processed Handler: url:" //$NON-NLS-1$
+			+handlerURL + " library:" //$NON-NLS-1$
+			+library + " class:" //$NON-NLS-1$
+			+clazz);
+	}
+
+	/* 
+	 * process URL info with element text
+	 */
+	private void processInfo(Attributes attributes) {
+		URLEntryModel inf = factory.createURLEntryModel();
+		String infoURL = attributes.getValue("url"); //$NON-NLS-1$
+		inf.setURLString(infoURL);
+
+		objectStack.push(inf);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("Processed Info: url:" + infoURL); //$NON-NLS-1$
+	}
+
+	/*
+	 * Process includes information
+	 */
+	private void processIncludes(Attributes attributes) {
+
+		// identifier and version
+		String id = attributes.getValue("id"); //$NON-NLS-1$
+		String ver = attributes.getValue("version"); //$NON-NLS-1$
+
+		if (id == null || id.trim().equals("") //$NON-NLS-1$
+		|| ver == null || ver.trim().equals("")) { //$NON-NLS-1$
+			internalError(NLS.bind(Messages.DefaultFeatureParser_IdOrVersionInvalid, (new String[] { id, ver, getState(currentState)})));
+		}
+
+		String filter = attributes.getValue("filter");
+		if (filter != null) {
+			//unsupported, ignore this element
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) 
+				debug(NLS.bind(UNSUPPORTED_FILTER_MSG, new String [] {INCLUDES, id, ver}));
+			return;
+		}
+		
+		IncludedFeatureReferenceModel includedFeature = factory.createIncludedFeatureReferenceModel();
+		includedFeature.setFeatureIdentifier(id);
+		includedFeature.setFeatureVersion(ver);
+
+		// name
+		String name = attributes.getValue("name");//$NON-NLS-1$
+		includedFeature.setLabel(name);
+
+		// optional
+		String optional = attributes.getValue("optional");//$NON-NLS-1$
+		boolean isOptional = "true".equalsIgnoreCase(optional);//$NON-NLS-1$
+		includedFeature.isOptional(isOptional);
+
+		// search location
+		String locationName = attributes.getValue("search-location");//$NON-NLS-1$
+		// bug 27030
+		if (locationName == null)
+			locationName = attributes.getValue("search_location");//$NON-NLS-1$
+		int searchLocation = IUpdateConstants.SEARCH_ROOT;
+		if ("both".equalsIgnoreCase(locationName))//$NON-NLS-1$
+			searchLocation = IUpdateConstants.SEARCH_ROOT & IUpdateConstants.SEARCH_SELF;
+		if ("self".equalsIgnoreCase(locationName))//$NON-NLS-1$
+			searchLocation = IUpdateConstants.SEARCH_SELF;				
+		includedFeature.setSearchLocation(searchLocation);
+
+		// os arch ws nl
+		String os = attributes.getValue("os");//$NON-NLS-1$
+		includedFeature.setOS(os);
+
+		String ws = attributes.getValue("ws");//$NON-NLS-1$
+		includedFeature.setWS(ws);
+
+		String arch = attributes.getValue("arch");//$NON-NLS-1$
+		includedFeature.setArch(arch);
+		
+		// NL
+		String nl = attributes.getValue("nl"); //$NON-NLS-1$
+		includedFeature.setNL(nl);
+
+		objectStack.push(includedFeature);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+			debug("End process Includes tag: id:" //$NON-NLS-1$
+			+id + " ver:" + ver); //$NON-NLS-1$
+			debug("name =" + name + " optional=" + optional + " search-location=" + locationName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			debug("os=" + os + " ws=" + ws + " arch=" + arch); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+	}
+
+	/* 
+	 * process URL info with label attribute
+	 */
+	private void processURLInfo(Attributes attributes) {
+		URLEntryModel inf = factory.createURLEntryModel();
+		String infoURL = attributes.getValue("url"); //$NON-NLS-1$
+		String label = attributes.getValue("label"); //$NON-NLS-1$
+		String type = attributes.getValue("type"); //$NON-NLS-1$
+		inf.setURLString(infoURL);
+		inf.setAnnotation(label);
+		
+		if ("web".equalsIgnoreCase(type)) //$NON-NLS-1$
+			inf.setType(IURLEntry.WEB_SITE);
+		else 
+			inf.setType(IURLEntry.UPDATE_SITE);
+
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			debug("Processed URLInfo: url:" + infoURL + " label:" + label+" type:"+type);//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+		objectStack.push(inf);
+	}
+
+	/* 
+	 * process import info
+	 */
+	private void processImport(Attributes attributes) {
+		String pluginID = attributes.getValue("plugin"); //$NON-NLS-1$
+		String featureID = attributes.getValue("feature"); //$NON-NLS-1$
+		String idMatch = attributes.getValue("id-match"); //$NON-NLS-1$
+
+		if (!(pluginID == null ^ featureID == null)) {
+			internalError(Messages.DefaultFeatureParser_PluginAndFeatureId); 
+			return;
+		}
+
+		// since 2.0.2 , manage feature and plugin import
+		String id = null;
+		if (pluginID == null) {
+			id = featureID;
+		} else {
+			id = pluginID;
+		}
+
+		if (id == null || id.trim().equals("")) //$NON-NLS-1$
+			internalError(NLS.bind(Messages.DefaultFeatureParser_MissingId, (new String[] { getState(currentState) })));
+		else {
+			String filter = attributes.getValue("filter");
+			String ver = attributes.getValue("version"); //$NON-NLS-1$
+			String match = attributes.getValue("match"); //$NON-NLS-1$
+			String patch = attributes.getValue("patch"); //$NON-NLS-1$
+			
+			if (filter != null) {
+				//unsupported, ignore this element
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) 
+					debug(NLS.bind(UNSUPPORTED_FILTER_MSG, new String [] {IMPORT + ' ' + (pluginID != null ? PLUGIN : FEATURE), id, ver}));
+				ignoredElement = true;
+				return;
+			}
+			ImportModel imp = factory.createImportModel();
+
+			imp.setPatch(patch != null && patch.equalsIgnoreCase("true")); //$NON-NLS-1$
+
+			if (ver == null) {
+				if (imp.isPatch()) {
+					internalError(Messages.DefaultFeatureParser_MissingPatchVersion); 
+				}
+				ver = "0.0.0"; //$NON-NLS-1$
+				match = "greaterOrEqual"; //$NON-NLS-1$
+			} else if (match == null) {
+				if (imp.isPatch())
+					match = "perfect"; //$NON-NLS-1$
+				else
+					match = "compatible"; //$NON-NLS-1$
+			}
+
+			imp.setIdentifier(id);
+			imp.setVersion(ver);
+			imp.setFeatureImport(featureID != null);
+			imp.setMatchingRuleName(match);
+			imp.setMatchingIdRuleName(idMatch);
+
+			if (imp.isPatch()) {
+				// patch reference must be perfect.
+				if (match != null && !match.equalsIgnoreCase("perfect")) { //$NON-NLS-1$
+					internalError(Messages.DefaultFeatureParser_wrongMatchForPatch); 
+				}
+				if (imp.isFeatureImport() == false) {
+					imp.setPatch(false);
+					internalError(Messages.DefaultFeatureParser_patchWithPlugin); 
+				}
+			}
+
+			// os arch ws
+			String os = attributes.getValue("os"); //$NON-NLS-1$
+			imp.setOS(os);
+
+			String ws = attributes.getValue("ws"); //$NON-NLS-1$
+			imp.setWS(ws);
+
+			String arch = attributes.getValue("arch"); //$NON-NLS-1$
+			imp.setOSArch(arch);
+
+			objectStack.push(imp);
+
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+				debug("Processed import: id:" + id + " ver:" + ver);//$NON-NLS-1$ //$NON-NLS-2$
+				debug("Processed import: match:" + match); //$NON-NLS-1$
+			}
+
+		}
+	}
+
+	/* 
+	 * process import info
+	 */
+	private void processRequire(Attributes attributes) {
+	}
+
+	/* 
+	 * process plugin entry info
+	 */
+	private void processPlugin(Attributes attributes) {
+		String id = attributes.getValue("id"); //$NON-NLS-1$
+		String ver = attributes.getValue("version"); //$NON-NLS-1$
+		if (id == null || id.trim().equals("") //$NON-NLS-1$
+		|| ver == null || ver.trim().equals("")) { //$NON-NLS-1$
+			internalError(NLS.bind(Messages.DefaultFeatureParser_IdOrVersionInvalid, (new String[] { id, ver, getState(currentState)})));
+		} else {
+			String filter = attributes.getValue("filter");
+			if (filter != null) {
+				//unsupported, ignore this element
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) 
+					debug(NLS.bind(UNSUPPORTED_FILTER_MSG, new String [] {PLUGIN, id, ver}));
+				return;
+			}
+			
+			PluginEntryModel pluginEntry = factory.createPluginEntryModel();
+			pluginEntry.setPluginIdentifier(id);
+			pluginEntry.setPluginVersion(ver);
+
+			String fragment = attributes.getValue("fragment"); //$NON-NLS-1$
+			pluginEntry.isFragment(fragment != null && fragment.trim().equalsIgnoreCase("true"));//$NON-NLS-1$			
+
+			//setOS
+			String os = attributes.getValue("os"); //$NON-NLS-1$
+			pluginEntry.setOS(os);
+
+			//setWS
+			String ws = attributes.getValue("ws"); //$NON-NLS-1$
+			pluginEntry.setWS(ws);
+
+			//setNL
+			String nl = attributes.getValue("nl"); //$NON-NLS-1$
+			pluginEntry.setNL(nl);
+
+			// setArch
+			String arch = attributes.getValue("arch"); //$NON-NLS-1$
+			pluginEntry.setArch(arch);
+
+			// setUnpack
+			String unpack = attributes.getValue("unpack"); //$NON-NLS-1$
+			pluginEntry.setUnpack(!"false".equalsIgnoreCase(unpack)); //$NON-NLS-1$
+
+			// download size
+			long download_size = ContentEntryModel.UNKNOWN_SIZE;
+			String download = attributes.getValue("download-size"); //$NON-NLS-1$
+			if (download != null && !download.trim().equals("")) { //$NON-NLS-1$
+				try {
+					download_size = Long.valueOf(download).longValue();
+				} catch (NumberFormatException e) {
+					// use UNKNOWN_SIZE
+				}
+			}
+			pluginEntry.setDownloadSize(download_size);
+
+			// install size	
+			long install_size = ContentEntryModel.UNKNOWN_SIZE;
+			String install = attributes.getValue("install-size"); //$NON-NLS-1$
+			if (install != null && !install.trim().equals("")) { //$NON-NLS-1$
+				try {
+					install_size = Long.valueOf(install).longValue();
+				} catch (NumberFormatException e) {
+					// use UNKNOWN_SIZE
+				}
+			}
+			pluginEntry.setInstallSize(install_size);
+
+			objectStack.push(pluginEntry);
+
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+				debug("Processed Plugin: id:" + id + " ver:" + ver + " fragment:" + fragment); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+				debug("Processed Plugin: os:" + os + " ws:" + ws + " nl:" + nl); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+				debug("Processed Plugin: download size:" //$NON-NLS-1$
+				+download_size + " install size:" //$NON-NLS-1$
+				+install_size);
+			}
+
+		}
+	}
+
+	/* 
+	 * process non-plug-in entry info
+	 */
+	private void processData(Attributes attributes) {
+		String id = attributes.getValue("id"); //$NON-NLS-1$
+		if (id == null || id.trim().equals("")) { //$NON-NLS-1$
+			internalError(NLS.bind(Messages.DefaultFeatureParser_MissingId, (new String[] { getState(currentState) })));
+		} else {
+			NonPluginEntryModel dataEntry = factory.createNonPluginEntryModel();
+			dataEntry.setIdentifier(id);
+
+			//setOS
+			String os = attributes.getValue("os"); //$NON-NLS-1$
+			dataEntry.setOS(os);
+
+			//setWS
+			String ws = attributes.getValue("ws"); //$NON-NLS-1$
+			dataEntry.setWS(ws);
+
+			//setNL
+			String nl = attributes.getValue("nl"); //$NON-NLS-1$
+			dataEntry.setNL(nl);
+
+			// setArch
+			String arch = attributes.getValue("arch"); //$NON-NLS-1$
+			dataEntry.setArch(arch);
+
+			// download size
+			long download_size = ContentEntryModel.UNKNOWN_SIZE;
+			String download = attributes.getValue("download-size"); //$NON-NLS-1$
+			if (download != null && !download.trim().equals("")) { //$NON-NLS-1$
+				try {
+					download_size = Long.valueOf(download).longValue();
+				} catch (NumberFormatException e) {
+					// use UNKNOWN_SIZE
+				}
+			}
+			dataEntry.setDownloadSize(download_size);
+
+			// install size	
+			long install_size = ContentEntryModel.UNKNOWN_SIZE;
+			String install = attributes.getValue("install-size"); //$NON-NLS-1$
+			if (install != null && !install.trim().equals("")) { //$NON-NLS-1$
+				try {
+					install_size = Long.valueOf(install).longValue();
+				} catch (NumberFormatException e) {
+					// use UNKNOWN_SIZE
+				}
+			}
+			dataEntry.setInstallSize(install_size);
+
+			objectStack.push(dataEntry);
+
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+				debug("Processed Data: id:" + id); //$NON-NLS-1$
+				debug("Processed Data: download size:" //$NON-NLS-1$
+				+download_size + " install size:" //$NON-NLS-1$
+				+install_size);
+			}
+
+		}
+	}
+
+	private void debug(String s) {
+		UpdateCore.debug("InternalFeatureParser: " + s); //$NON-NLS-1$
+	}
+
+	private void logStatus(SAXParseException ex) {
+		String name = ex.getSystemId();
+		if (name == null)
+			name = ""; //$NON-NLS-1$
+		else
+			name = name.substring(1 + name.lastIndexOf("/")); //$NON-NLS-1$
+
+		String msg;
+		if (name.equals("")) { //$NON-NLS-1$
+			msg = NLS.bind(Messages.DefaultFeatureParser_ErrorParsing, (new String[] { ex.getMessage() }));
+		} else {
+			String[] values = new String[] { name, Integer.toString(ex.getLineNumber()), Integer.toString(ex.getColumnNumber()), ex.getMessage()};
+			msg = NLS.bind(Messages.DefaultFeatureParser_ErrorlineColumnMessage, values);
+		}
+		error(new Status(IStatus.ERROR, PLUGIN_ID, Platform.PARSE_PROBLEM, msg, ex));
+	}
+
+	/*
+	 * Handles an error state specified by the status.  The collection of all logged status
+	 * objects can be accessed using <code>getStatus()</code>.
+	 *
+	 * @param error a status detailing the error condition
+	 */
+	private void error(IStatus error) {
+
+		if (status == null) {
+			status = new MultiStatus(PLUGIN_ID, Platform.PARSE_PROBLEM, Messages.DefaultFeatureParser_ErrorParsingFeature, null);	
+		}
+
+		status.add(error);
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING)
+			UpdateCore.log(error);
+	}
+
+	private void internalErrorUnknownTag(String msg) {
+		stateStack.push(new Integer(STATE_IGNORED_ELEMENT));
+		internalError(msg);
+	}
+
+	private void internalError(String message) {
+        if (location != null)
+            message += " " + NLS.bind(Messages.DefaultFeatureParser_location, (new String[] { location })); //$NON-NLS-1$
+		error(new Status(IStatus.ERROR, PLUGIN_ID, Platform.PARSE_PROBLEM, message, null));
+	}
+
+	/*
+	 * return the state as String
+	 */
+	private String getState(int state) {
+
+		switch (state) {
+			case STATE_IGNORED_ELEMENT :
+				return "Ignored"; //$NON-NLS-1$
+
+			case STATE_INITIAL :
+				return "Initial"; //$NON-NLS-1$
+
+			case STATE_FEATURE :
+				return "Feature"; //$NON-NLS-1$
+
+			case STATE_HANDLER :
+				return "Install Handler"; //$NON-NLS-1$
+
+			case STATE_DESCRIPTION :
+				return "description"; //$NON-NLS-1$
+
+			case STATE_INCLUDES :
+				return "includes"; //$NON-NLS-1$
+
+			case STATE_COPYRIGHT :
+				return "Copyright"; //$NON-NLS-1$
+
+			case STATE_LICENSE :
+				return "License"; //$NON-NLS-1$
+
+			case STATE_URL :
+				return "URL"; //$NON-NLS-1$
+
+			case STATE_UPDATE :
+				return "Update URL"; //$NON-NLS-1$
+
+			case STATE_DISCOVERY :
+				return "Discovery URL"; //$NON-NLS-1$
+
+			case STATE_REQUIRES :
+				return "Require"; //$NON-NLS-1$
+
+			case STATE_IMPORT :
+				return "Import"; //$NON-NLS-1$
+
+			case STATE_PLUGIN :
+				return "Plugin"; //$NON-NLS-1$
+
+			case STATE_DATA :
+				return "Data"; //$NON-NLS-1$
+
+			default :
+				return NLS.bind(Messages.DefaultFeatureParser_UnknownState, (new String[] { Integer.toString(state) }));
+		}
+
+	}
+
+	/**
+	 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+	 */
+	public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
+		super.ignorableWhitespace(arg0, arg1, arg2);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InternalSiteManager.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InternalSiteManager.java
new file mode 100644
index 0000000..f016501
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/InternalSiteManager.java
@@ -0,0 +1,396 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.ILocalSite;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFactory;
+import org.eclipse.update.core.ISiteFactoryExtension;
+import org.eclipse.update.core.JarContentReference;
+import org.eclipse.update.core.Site;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.model.InvalidSiteTypeException;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+import org.eclipse.update.internal.core.connection.IResponse;
+import org.eclipse.update.internal.model.ITimestamp;
+
+/**
+ * 
+ */
+public class InternalSiteManager {
+
+	public static ILocalSite localSite;
+
+	public static final String DEFAULT_SITE_TYPE = SiteURLContentProvider.SITE_TYPE;
+	private static final String DEFAULT_EXECUTABLE_SITE_TYPE = SiteFileContentProvider.SITE_TYPE;
+
+	private static Map estimates;
+
+	// cache found sites
+	private static Map sites = new HashMap();
+	// cache http updated url
+	private static Map httpSitesUpdatedUrls = new HashMap();
+	// cache timestamps
+	private static Map siteTimestamps = new HashMap();
+	public static boolean globalUseCache = true;
+
+	// true if an exception occured creating localSite
+	// so we cache it and don't attempt to create it again
+	private static CoreException exceptionOccured = null;
+
+	/*
+	 * @see SiteManager#getLocalSite()
+	 */
+	public static ILocalSite getLocalSite() throws CoreException {
+		return internalGetLocalSite();
+	}
+
+	/*
+	 * Internal call if optimistic reconciliation needed
+	 */
+	private static ILocalSite internalGetLocalSite() throws CoreException {
+
+		// if an exception occured while retrieving the Site
+		// rethrow it
+		if (exceptionOccured != null)
+			throw exceptionOccured;
+
+		if (localSite == null) {
+			try {
+				localSite = LocalSite.internalGetLocalSite();
+			} catch (CoreException e) {
+				exceptionOccured = e;
+				throw e;
+			}
+		}
+		return localSite;
+	}
+	
+	private static boolean isValidCachedSite(URL siteURL) {
+		if (!sites.containsKey(siteURL.toExternalForm()))
+			return false;
+			
+		Long timestamp = (Long)siteTimestamps.get(siteURL);
+		if (timestamp == null)
+			return false;
+		long localLastModified = timestamp.longValue();
+		
+		return UpdateManagerUtils.isSameTimestamp(siteURL, localLastModified);
+	}
+
+	/*
+	 * @see ILocalSite#getSite(URL)
+	 */
+	public static ISite getSite(URL siteURL, boolean useCache, IProgressMonitor monitor) throws CoreException {
+		ISite site = null;
+		if (monitor==null) monitor = new NullProgressMonitor();
+
+		if (siteURL == null)
+			return null;
+
+		// use cache if set up globally (globalUseCache=true)
+		// and passed as parameter (useCache=true)
+		if (httpSitesUpdatedUrls.containsKey(siteURL.toExternalForm())) {
+			siteURL = (URL)httpSitesUpdatedUrls.get(siteURL.toExternalForm());
+		}
+		String siteURLString = siteURL.toExternalForm();
+		if ((useCache && globalUseCache) && isValidCachedSite(siteURL)) {
+			site = (ISite) sites.get(siteURLString);
+			UpdateCore.getPlugin().getUpdateSession().markVisited(site.getURL());
+			return site;
+		}
+		
+		// try adding "eclipse" to the site url, in case this is an extension site
+		if ("file".equals(siteURL.getProtocol()) ) { //$NON-NLS-1$
+			File f = new File(siteURL.getFile());
+			if (f.isDirectory() && !"eclipse".equals(f.getName())) { //$NON-NLS-1$
+				f = new File(f, "eclipse"); //$NON-NLS-1$
+				try {
+					if ((useCache && globalUseCache) && isValidCachedSite(f.toURL())) {
+						site = (ISite) sites.get(f.toURL().toExternalForm());
+						return site;
+					}
+				} catch (MalformedURLException e) {
+				}	
+			}
+		}
+
+		// consider file protocol also if the URL points to a directory
+		// and no site.xml exist
+		// if the user points to a file, consider DEFAULT_SITE_TYPE
+		// site.xml will have to specify the type
+		boolean fileProtocol = "file".equalsIgnoreCase(siteURL.getProtocol()); //$NON-NLS-1$
+		boolean directoryExists = false;
+		if (fileProtocol) {
+			File dir;
+			dir = new File(siteURL.getFile());
+			if (dir != null && dir.isDirectory()) {
+				if (!(new File(dir, Site.SITE_XML).exists()))
+					directoryExists = true;
+			}
+		}
+
+		//PERF: if file: <path>/ and directory exists then consider executable
+		monitor.beginTask(Messages.InternalSiteManager_ConnectingToSite, 8); 
+		if (fileProtocol && directoryExists) {
+			site = attemptCreateSite(DEFAULT_EXECUTABLE_SITE_TYPE, siteURL, monitor);
+			monitor.worked(4); // only one attempt
+		} else {
+			try {
+				monitor.worked(3);
+				site = attemptCreateSite(DEFAULT_SITE_TYPE, siteURL, monitor);
+				monitor.worked(1);
+			} catch (CoreException preservedException) {
+				if (!monitor.isCanceled()) {
+					// attempt a retry is the protocol is file, with executbale type
+					if (!fileProtocol)
+						throw preservedException;
+
+					try {
+						site = attemptCreateSite(DEFAULT_EXECUTABLE_SITE_TYPE, siteURL, monitor);
+					} catch (CoreException retryException) {
+						IStatus firstStatus = preservedException.getStatus();
+						MultiStatus multi = new MultiStatus(firstStatus.getPlugin(), IStatus.OK, Messages.InternalSiteManager_FailedRetryAccessingSite, retryException); 
+						multi.addAll(firstStatus);
+						throw preservedException;
+					}
+				}
+			}
+		}
+
+		if (site != null) {
+			sites.put(site.getURL().toExternalForm(), site);
+			UpdateCore.getPlugin().getUpdateSession().markVisited(site.getURL());
+			if (site instanceof ITimestamp) {
+				siteTimestamps.put(site.getURL(), new Long(((ITimestamp)site).getTimestamp().getTime()));
+			} else {
+				try {
+					IResponse response = ConnectionFactory.get(URLEncoder.encode(siteURL));
+					siteTimestamps.put(siteURL, new Long(response.getLastModified()));
+				} catch (MalformedURLException e) {
+				} catch (IOException e) {
+				}
+			}
+		}
+
+		//flush the JarFile we may hold on to
+		// we keep the temp not to create them again
+		JarContentReference.shutdown(); // make sure we are not leaving jars open for this site
+
+		//flush mapping of downloaded JAR files
+		// FIXME : provide better cache flushing after 2.1
+		// FIXED: everything downloaded is cached and timestamped.
+		//        Timestamps are compared to lastModifed on the server
+		//        and we download only when there is a differenc
+		// Utilities.flushLocalFile();
+
+		return site;
+	}
+
+	/*
+	 * Attempt to create a site
+	 * if the site guessed is not the type found,
+	 * attempt to create a type with the type found in the site.xml
+	 */
+	private static ISite attemptCreateSite(String guessedTypeSite, URL siteURL, IProgressMonitor monitor) throws CoreException {
+		if (monitor == null) monitor = new NullProgressMonitor();
+		ISite site = null;
+
+		try {
+			monitor.worked(1);
+			site = createSite(guessedTypeSite, siteURL, monitor);
+			monitor.worked(1); // if no error, occurs the retry branch doesn't need to be executed
+		} catch (InvalidSiteTypeException e) {
+			if (monitor.isCanceled()) return null;
+
+			// the type in the site.xml is not the one expected	
+			// attempt to use this type instead	
+			//DEBUG:
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_TYPE) {
+				UpdateCore.debug("The Site :" + siteURL.toExternalForm() + " is a different type than the guessed type based on the protocol. new Type:" + e.getNewType());	//$NON-NLS-1$ //$NON-NLS-2$ 
+			}
+
+			try {
+				if (e.getNewType() == null)
+					throw e;
+				site = createSite(e.getNewType(), siteURL, monitor);
+			} catch (InvalidSiteTypeException e1) {
+				throw Utilities.newCoreException(NLS.bind(Messages.InternalSiteManager_UnableToCreateSiteWithType, (new String[] { e.getNewType(), siteURL.toExternalForm() })), e1); 
+			}
+		}
+
+		return site;
+	}
+
+	/*
+	 * create an instance of a class that implements ISite
+	 * 
+	 * the URL can be of the following form
+	 * 1 protocol://...../
+	 * 2 protocol://.....
+	 * 3 protocol://..../site.xml
+	 * 4 protocol://...#...
+	 * 
+	 * 1 If the file of the file of teh url ends with '/', attempt to open the stream.
+	 * if it fails, add site.xml and attempt to open the stream
+	 * 
+	 * 2 attempt to open the stream
+	 * 	fail
+	 * 		add '/site.xml' and attempt to open the stream
+	 * 	sucess
+	 * 		attempt to parse, if it fails, add '/site.xml' and attempt to open the stream
+	 * 
+	 * 3 open the stream
+	 * 
+	 * 4 open the stream	
+	 */
+	private static ISite createSite(String siteType, URL url, IProgressMonitor monitor) throws CoreException, InvalidSiteTypeException {
+		
+		if (monitor == null) monitor = new NullProgressMonitor();
+		//ISite site = null;
+		ISiteFactory factory = SiteTypeFactory.getInstance().getFactory(siteType);
+		URL fixedUrl;
+		
+		// see if we need to (and can) fix url by adding site.xml to it
+		try {
+			if ( (url.getRef() != null) || (url.getFile().endsWith(Site.SITE_XML) || (url.getProtocol().equalsIgnoreCase("file")))) { //$NON-NLS-1$
+			 	fixedUrl = url;
+			} else if (url.getFile().endsWith("/")) { //$NON-NLS-1$
+				fixedUrl = new URL(url, Site.SITE_XML);
+			} else {
+				fixedUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile() + "/" + Site.SITE_XML);	//$NON-NLS-1$
+			}
+		} catch (MalformedURLException mue) {
+			fixedUrl = url;
+		}
+		
+		try {
+			try { 
+				monitor.worked(1);
+				return createSite( factory, fixedUrl, url, monitor);
+			} catch (CoreException ce) {
+				if (monitor.isCanceled()) 
+					return null;
+			
+				if (!fixedUrl.equals(url)) {
+					// try with original url
+					 return createSite( factory, url, url, monitor);
+				} else if (url.getProtocol().equalsIgnoreCase("file") && ! url.getFile().endsWith(Site.SITE_XML)){ //$NON-NLS-1$
+					try {
+						if (url.getFile().endsWith("/")) { //$NON-NLS-1$
+							return createSite(factory, new URL(url,
+									Site.SITE_XML), url, monitor);
+						} else {
+							return createSite(factory, new URL(url
+									.getProtocol(), url.getHost(), url
+									.getPort(), url.getFile()
+									+ "/" + Site.SITE_XML), url, monitor); //$NON-NLS-1$							
+						}
+					} catch (MalformedURLException mue) {
+						throw ce;
+					}
+				} else {
+					throw ce;
+				}
+			}
+		} catch(CoreException ce) {
+			throw Utilities.newCoreException(NLS.bind(Messages.InternalSiteManager_UnableToAccessURL, (new String[] { url.toExternalForm() })), ce);
+		}
+	}
+	
+	private static ISite createSite(ISiteFactory factory, URL url, URL originalUrl, IProgressMonitor monitor) throws CoreException, InvalidSiteTypeException {
+		
+		ISite site;
+			
+		site = createSite(factory, url, monitor);
+		httpSitesUpdatedUrls.put(originalUrl.toExternalForm(), url);	
+		
+		return site;
+	}
+	
+	private static ISite createSite(ISiteFactory factory, URL url, IProgressMonitor monitor) throws CoreException, InvalidSiteTypeException {
+		if (factory instanceof ISiteFactoryExtension)
+			return ((ISiteFactoryExtension)factory).createSite(url, monitor);
+		else
+			return factory.createSite(url);
+	}
+
+	/*
+	 * Creates a new site on the file system
+	 * This is the only Site we can create.
+	 * 
+	 * @param siteLocation
+	 * @throws CoreException
+	 */
+	public static ISite createSite(File siteLocation) throws CoreException {
+		ISite site = null;
+		if (siteLocation != null) {
+			try {
+				URL siteURL = siteLocation.toURL();
+				site = getSite(siteURL, false, null);
+			} catch (MalformedURLException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.InternalSiteManager_UnableToCreateURL, (new String[] { siteLocation.getAbsolutePath() })), e);
+			}
+		}
+		return site;
+	}
+
+
+	/**
+	 * Method downloaded.
+	 * @param downloadSize size downloaded in bytes
+	 * @param time time in seconds
+	 * @param url
+	 */
+	public static void downloaded(long downloadSize, long time, URL url) {
+		if (downloadSize <= 0 || time < 0)
+			return;
+		String host = url.getHost();
+		long sizeByTime = (time == 0) ? 0 : downloadSize / time;
+		Long value = new Long(sizeByTime);
+		if (estimates == null) {
+			estimates = new HashMap();
+		} else {
+			Long previous = (Long) estimates.get(host);
+			if (previous != null) {
+				value = new Long((previous.longValue() + sizeByTime) / 2);
+			}
+		}
+		estimates.put(host, value);
+	}
+	/**
+	 * Method getEstimatedTransferRate rate bytes/seconds.
+	 * @param host
+	 * @return long
+	 */
+	public static long getEstimatedTransferRate(String host) {
+		if (estimates == null)
+			return 0;
+		Long value = (Long) estimates.get(host);
+		if (value == null)
+			return 0;
+		return value.longValue();
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/JarDeltaInstallHandler.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/JarDeltaInstallHandler.java
new file mode 100644
index 0000000..d1a7e34
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/JarDeltaInstallHandler.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.update.core.IFeatureContentConsumer;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.PluginEntry;
+import org.eclipse.update.core.Site;
+
+public class JarDeltaInstallHandler extends DeltaInstallHandler {
+
+	
+	protected void overlayPlugin(
+			IPluginEntry oldPlugin,
+			IPluginEntry newPlugin,
+			IFeatureContentConsumer consumer)
+			throws CoreException, IOException {
+		
+			if(newPlugin instanceof PluginEntry && ((PluginEntry)newPlugin).isUnpack()){
+				// partial plug-ins (in patches) must always be unpacked
+				super.overlayPlugin(oldPlugin, newPlugin, consumer);
+			}
+			
+			URL oldURI = null;
+			try {
+				oldURI = new URL(consumer.getFeature().getSite().getURL().getPath() + 
+									 Site.DEFAULT_PLUGIN_PATH + 
+									 oldPlugin.getVersionedIdentifier().toString());
+			} catch (MalformedURLException e) {
+				throw new IOException(e.getMessage());
+			}
+			File oldJarFile = new File(oldURI.toExternalForm());
+			JarFile oldJar = new JarFile(oldJarFile);
+			
+			URL newURI = null;
+			try {
+				newURI = new URL(consumer.getFeature().getSite().getURL().getPath() + 
+								 Site.DEFAULT_PLUGIN_PATH + 
+								 newPlugin.getVersionedIdentifier().toString());
+			} catch (MalformedURLException e) {
+				throw new IOException(e.getMessage());
+			}
+			File newJarFile = new File(newURI.toExternalForm());
+			JarFile newJar = new JarFile(newJarFile);
+
+			String tempFileName = oldURI + "-" + (new Date()).getTime(); //$NON-NLS-1$
+			File tempFile = new File(tempFileName);
+			FileOutputStream fos = new FileOutputStream(tempFile);
+			JarOutputStream jos = new JarOutputStream( fos);
+			
+			addToJar(jos, newJar);
+			addToJar(jos, oldJar);
+			
+			jos.closeEntry();
+			jos.finish();
+			fos.close();
+			newJar.close();
+			oldJar.close();
+			
+			newJarFile = new File(newURI.toExternalForm());
+			newJarFile.delete();
+			
+			newJarFile.createNewFile();
+
+			copyFile(tempFile, newJarFile);
+	}
+	
+	public static void copyFile(File src, File dst) throws IOException {
+		InputStream in=null;
+		OutputStream out=null;
+		try {
+			in = new BufferedInputStream(new FileInputStream(src));
+			out = new BufferedOutputStream(new FileOutputStream(dst));		
+			byte[] buffer = new byte[4096];
+			int len;
+			while ((len=in.read(buffer)) != -1) {
+				out.write(buffer, 0, len);
+			}
+		} finally {
+			if (in != null)
+				try {
+					in.close();
+				} catch (IOException e) {
+				}
+			if (out != null)
+				try {
+					out.close();
+				} catch (IOException e) {
+				}
+		}
+	}
+
+	public static void addToJar(JarOutputStream jos, JarFile jf) throws IOException {
+		Enumeration e = jf.entries();
+		
+		while(e.hasMoreElements()) {
+			
+			ZipEntry je = (ZipEntry)e.nextElement();
+			InputStream io = jf.getInputStream(je);
+			
+			byte b[] = new byte[4096];
+			int read = 0;
+			try {
+				jos.putNextEntry(je);
+				while( ( read = io.read(b, 0, 4096)) != -1) {
+					jos.write(b, 0, read);
+				}
+			} catch (ZipException ze) {
+				//ze.printStackTrace();
+				throw ze;
+			}		
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LiteFeature.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LiteFeature.java
new file mode 100644
index 0000000..25ac09f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LiteFeature.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import org.eclipse.update.core.Feature;
+
+public class LiteFeature extends Feature {
+	
+	private boolean fullFeature = true;
+
+	public boolean isFullFeature() {
+		return fullFeature;
+	}
+
+	public void setFullFeature(boolean fullFeature) {
+		this.fullFeature = fullFeature;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LiteFeatureFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LiteFeatureFactory.java
new file mode 100644
index 0000000..0f0a9ee
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LiteFeatureFactory.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.core.BaseFeatureFactory;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.model.FeatureModel;
+
+public class LiteFeatureFactory extends BaseFeatureFactory {
+
+	public LiteFeatureFactory() {
+		super();
+	}
+
+	public IFeature createFeature(URL url, ISite site, IProgressMonitor monitor)
+			throws CoreException {
+		
+		return null;
+	}
+
+	public FeatureModel createFeatureModel() {
+		return new LiteFeature();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LocalSite.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LocalSite.java
new file mode 100644
index 0000000..c41697d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LocalSite.java
@@ -0,0 +1,374 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import org.eclipse.core.runtime.ListenerList;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.IActivity;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.configuration.ILocalSite;
+import org.eclipse.update.configuration.ILocalSiteChangedListener;
+import org.eclipse.update.configuration.IProblemHandler;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.internal.model.InstallConfigurationModel;
+import org.eclipse.update.internal.model.SiteLocalModel;
+import org.eclipse.update.internal.model.SiteLocalParser;
+
+/**
+ * This class manages the configurations.
+ */
+
+public class LocalSite extends SiteLocalModel implements ILocalSite{
+
+	private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
+	private SiteStatusAnalyzer siteStatusAnalyzer;
+	private boolean isTransient = false;
+
+	/*
+	 * Have new features been found during reconciliation
+	 */
+	public static boolean newFeaturesFound = false;
+
+	/*
+	 * initialize the configurations from the persistent model.
+	 * Set the reconciliation as non optimistic
+	 */
+	public static ILocalSite getLocalSite() throws CoreException {
+		return internalGetLocalSite();
+	}
+
+	/*
+	 *Internal call is reconciliation needs to be optimistic
+	 */
+	public static ILocalSite internalGetLocalSite() throws CoreException {
+	
+		LocalSite localSite = new LocalSite();
+	
+		// obtain platform configuration
+		IPlatformConfiguration currentPlatformConfiguration = ConfiguratorUtils.getCurrentPlatformConfiguration();
+		localSite.isTransient(currentPlatformConfiguration.isTransient());
+	
+		try {
+			URL configXML = currentPlatformConfiguration.getConfigurationLocation();
+			localSite.setLocationURLString(configXML.toExternalForm());
+			localSite.resolve(configXML, null);
+	
+			// Attempt to read previous state
+			parseLocalSiteFile(currentPlatformConfiguration, localSite);
+
+		} catch (MalformedURLException exception) {
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteLocal_UnableToCreateURLFor, (new String[] { localSite.getLocationURLString() + " & " + CONFIG_FILE })), exception); //$NON-NLS-1$
+		}
+	
+		return localSite;
+	}
+
+	/**
+	 * Create the localSite object
+	 */
+	private static boolean parseLocalSiteFile(IPlatformConfiguration platformConfig, LocalSite localSite ) throws CoreException, MalformedURLException {
+
+		//attempt to parse the LocalSite.xml	
+//		URL resolvedURL = URLEncoder.encode(configXML);
+		try {
+//			InputStream in = UpdateCore.getPlugin().get(resolvedURL).getInputStream();
+			new SiteLocalParser(platformConfig, localSite);
+			return true;
+		} catch (Exception exception) {
+			return false;
+		}
+	}
+
+	/**
+	 * 
+	 */
+	protected LocalSite() {
+	}
+
+	/**
+	 * adds a new configuration to the LocalSite
+	 *  the newly added configuration is teh current one
+	 */
+	public void addConfiguration(IInstallConfiguration config) {
+		if (config != null) {
+			addConfigurationModel((InstallConfigurationModel) config);
+
+			trimHistoryToCapacity();
+
+			// set configuration as current		
+			if (getCurrentConfigurationModel() != null)
+				getCurrentConfigurationModel().setCurrent(false);
+			if (config instanceof InstallConfiguration)
+				 ((InstallConfiguration) config).setCurrent(true);
+
+			setCurrentConfigurationModel((InstallConfigurationModel) config);
+			((InstallConfigurationModel) config).markReadOnly();
+
+			// notify listeners
+			Object[] siteLocalListeners = listeners.getListeners();
+			for (int i = 0; i < siteLocalListeners.length; i++) {
+				((ILocalSiteChangedListener) siteLocalListeners[i]).currentInstallConfigurationChanged(config);
+			}
+		}
+
+	}
+
+	/*
+	 * 
+	 */
+	private void trimHistoryToCapacity() {
+		// check if we have to remove a configuration
+		// the first added is #0
+		while (getConfigurationHistory().length > getMaximumHistoryCount() &&
+				getConfigurationHistory().length > 1) {
+			// do not remove the first element in history, this is the original config
+			InstallConfigurationModel removedConfig = getConfigurationHistoryModel()[1];
+			if (removeConfigurationModel(removedConfig)) {
+
+				// DEBUG:
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION) {
+					UpdateCore.debug("Removed configuration :" + removedConfig.getLabel()); //$NON-NLS-1$
+				}
+
+				// notify listeners
+				Object[] siteLocalListeners = listeners.getListeners();
+				for (int i = 0; i < siteLocalListeners.length; i++) {
+					((ILocalSiteChangedListener) siteLocalListeners[i]).installConfigurationRemoved((IInstallConfiguration) removedConfig);
+				}
+
+				//remove files
+				URL url = removedConfig.getURL();
+				UpdateManagerUtils.removeFromFileSystem(new File(url.getFile()));
+			}
+		}
+	}
+	/*
+	 * @see ILocalSite#addLocalSiteChangedListener(ILocalSiteChangedListener)
+	 */
+	public void addLocalSiteChangedListener(ILocalSiteChangedListener listener) {
+		listeners.add(listener);
+	}
+
+	/*
+	 * @see ILocalSite#removeLocalSiteChangedListener(ILocalSiteChangedListener)
+	 */
+	public void removeLocalSiteChangedListener(ILocalSiteChangedListener listener) {
+		listeners.add(listener);
+	}
+
+	/**
+	 * Saves the site into the config file.
+	 * @return true if changes restart is needed
+	 */
+	public boolean save() throws CoreException {
+
+		// Save the current configuration as
+		// the other are already saved
+		// and set runtim info for next startup
+		return ((InstallConfiguration) getCurrentConfiguration()).save();
+	}
+	
+//	/**
+//	 * Method createNewInstallConfiguration.
+//	 * @return IInstallConfiguration
+//	 */
+//	private IInstallConfiguration createNewInstallConfiguration() throws CoreException {
+//		InstallConfiguration newInstallConfig = createConfigurationSite(null);
+//		newInstallConfig.setTimeline(newInstallConfig.getCreationDate().getTime());
+//		return newInstallConfig;
+//	}
+
+
+	/**
+	 * @since 2.0
+	 * @deprecated This method should not be used. The current install configuration is to be used.
+	 */
+	public IInstallConfiguration cloneCurrentConfiguration() throws CoreException {
+		try {
+			return new InstallConfiguration(getCurrentConfiguration());
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(Messages.SiteLocal_cloneConfig, e); 
+		}
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public void revertTo(IInstallConfiguration configuration, IProgressMonitor monitor, IProblemHandler handler) throws CoreException {
+
+		// create the activity 
+		//Start UOW ?
+		ConfigurationActivity activity = new ConfigurationActivity(IActivity.ACTION_REVERT);
+		activity.setLabel(configuration.getLabel());
+		activity.setDate(new Date());
+		IInstallConfiguration newConfiguration = null;
+
+		try {
+			// create a configuration
+			newConfiguration = cloneCurrentConfiguration();
+			newConfiguration.setLabel(configuration.getLabel());
+
+			// add to the stack which will set up as current
+			addConfiguration(newConfiguration);
+
+			// process delta
+			// the Configured featuresConfigured are the same as the old configuration
+			// the unconfigured featuresConfigured are the rest...
+			 ((InstallConfiguration) newConfiguration).revertTo(configuration, monitor, handler);
+
+			// everything done ok
+			activity.setStatus(IActivity.STATUS_OK);
+		} catch (CoreException e) {
+			// error
+			activity.setStatus(IActivity.STATUS_NOK);
+			throw e;
+		} catch (InterruptedException e) {
+			//user decided not to revert, do nothing
+			// because we didn't add the configuration to the history
+		} finally {
+			if (newConfiguration != null)
+				 ((InstallConfiguration) newConfiguration).addActivity(activity);
+		}
+
+	}
+
+	/**
+	 * @since 2.0
+	 * @deprecated
+	 */
+	public IInstallConfiguration addToPreservedConfigurations(IInstallConfiguration configuration) throws CoreException {
+		return null;
+	}
+
+	/*
+	 * @see ILocalSite#getPreservedConfigurationFor(IInstallConfiguration)
+	 */
+	public IInstallConfiguration findPreservedConfigurationFor(IInstallConfiguration configuration) {
+
+		// based on time stamp for now
+		InstallConfigurationModel preservedConfig = null;
+		if (configuration != null) {
+			InstallConfigurationModel[] preservedConfigurations = getPreservedConfigurationsModel();
+			if (preservedConfigurations != null) {
+				for (int indexPreserved = 0; indexPreserved < preservedConfigurations.length; indexPreserved++) {
+					if (configuration.getCreationDate().equals(preservedConfigurations[indexPreserved].getCreationDate())) {
+						preservedConfig = preservedConfigurations[indexPreserved];
+						break;
+					}
+				}
+			}
+		}
+
+		return (IInstallConfiguration) preservedConfig;
+	}
+
+	/*
+	 * @see ILocalSite#getCurrentConfiguration()
+	 * LocalSiteModel#getCurrentConfigurationModel() may return null if
+	 * we just parsed LocalSite.xml
+	 */
+	public IInstallConfiguration getCurrentConfiguration() {
+		if (getCurrentConfigurationModel() == null) {
+			int index = 0;
+			if ((index = getConfigurationHistoryModel().length) == 0) {
+				return null;
+			} else {
+				InstallConfigurationModel config = getConfigurationHistoryModel()[index - 1];
+				config.setCurrent(true);
+				setCurrentConfigurationModel(config);
+			}
+		}
+		return (IInstallConfiguration) getCurrentConfigurationModel();
+	}
+
+	/*
+	 * @see ILocalSite#getPreservedConfigurations()
+	 */
+	public IInstallConfiguration[] getPreservedConfigurations() {
+		if (getPreservedConfigurationsModel().length == 0)
+			return new IInstallConfiguration[0];
+		return (IInstallConfiguration[]) getPreservedConfigurationsModel();
+	}
+
+	/*
+	 * @see ILocalSite#removeFromPreservedConfigurations(IInstallConfiguration)
+	 */
+	public void removeFromPreservedConfigurations(IInstallConfiguration configuration) {
+		if (removePreservedConfigurationModel((InstallConfigurationModel) configuration))
+			 ((InstallConfiguration) configuration).remove();
+	}
+
+	/*
+	 * @see ILocalSite#getConfigurationHistory()
+	 */
+	public IInstallConfiguration[] getConfigurationHistory() {
+		if (getConfigurationHistoryModel().length == 0)
+			return new IInstallConfiguration[0];
+		return (IInstallConfiguration[]) getConfigurationHistoryModel();
+	}
+
+
+	/**
+	 * Gets the isTransient.
+	 * @return Returns a boolean
+	 */
+	public boolean isTransient() {
+		return isTransient;
+	}
+
+	/**
+	 * Sets the isTransient.
+	 * @param isTransient The isTransient to set
+	 */
+	private void isTransient(boolean isTransient) {
+		this.isTransient = isTransient;
+	}
+
+	/*
+	 * 
+	 */
+	private SiteStatusAnalyzer getSiteStatusAnalyzer() {
+		if (siteStatusAnalyzer == null)
+			siteStatusAnalyzer = new SiteStatusAnalyzer(this);
+		return siteStatusAnalyzer;
+	}
+
+	/*
+	 *  check if the Plugins of the feature are on the plugin path
+	 *  If all the plugins are on the plugin path, and the version match and there is no other version -> HAPPY
+	 *  If all the plugins are on the plugin path, and the version match and there is other version -> AMBIGUOUS
+	 *  If some of the plugins are on the plugin path, but not all -> UNHAPPY
+	 * 	Check on all ConfiguredSites
+	 */
+	public IStatus getFeatureStatus(IFeature feature) throws CoreException {
+		return getSiteStatusAnalyzer().getFeatureStatus(feature);
+	} 
+	/**
+	 * @see org.eclipse.update.internal.model.SiteLocalModel#setMaximumHistoryCount(int)
+	 */
+	public void setMaximumHistoryCount(int history) {
+		super.setMaximumHistoryCount(history);
+		trimHistoryToCapacity();
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LockManager.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LockManager.java
new file mode 100644
index 0000000..f1abe5d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/LockManager.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.update.internal.core;
+
+import java.util.Hashtable;
+
+/**
+ * @author aniefer
+ *
+ */
+public class LockManager {
+	// lock
+	private final static Object lock = new Object();
+
+	// hashtable of locks
+	private static Hashtable locks = new Hashtable();
+	
+	public static Object getLock(String key) {
+		synchronized (lock) {
+			if (locks.get(key) == null)
+				locks.put(key, key);
+			return locks.get(key);
+		}
+	}
+	
+	public static void returnLock(String key) {
+		synchronized (lock) {
+			locks.remove(key);
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Messages.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Messages.java
new file mode 100644
index 0000000..bee325b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Messages.java
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.update.internal.core;
+
+import org.eclipse.osgi.util.NLS;
+
+public final class Messages extends NLS {
+
+	private static final String BUNDLE_NAME = "org.eclipse.update.internal.core.messages";//$NON-NLS-1$
+
+	private Messages() {
+		// Do not instantiate
+	}
+
+	public static String BaseSiteFactory_CannotRetriveParentDirectory;
+	public static String ContentReference_UnableToCreateInputStream;
+	public static String ContentReference_UnableToReturnReferenceAsFile;
+	public static String ContentReference_UnableToReturnReferenceAsURL;
+	public static String ContentReference_HttpNok;
+	public static String ContentReference_FileDoesNotExist;
+	public static String Feature_SiteAlreadySet;
+	public static String Feature_TaskInstallFeatureFiles;
+	public static String Feature_TaskInstallPluginFiles;
+	public static String Feature_NoContentProvider;
+	public static String Feature_NoFeatureContentConsumer;
+	public static String Feature_NoURL;
+	public static String Feature_FeatureVersionToString;
+	public static String Feature_InstallationCancelled;
+	public static String Feature_UnableToInitializeFeatureReference;
+	public static String FeatureContentProvider_Downloading;
+	public static String FeatureContentProvider_UnableToRetrieve;
+	public static String FeatureContentProvider_UnableToCreate;
+	public static String FeatureContentProvider_ExceptionDownloading;
+	public static String InstallHandler_unableToCreateHandler;
+	public static String InstallHandler_notFound;
+	public static String InstallHandler_invalidHandler;
+	public static String InstallHandler_callException;
+	public static String InstallHandler_error;
+	public static String InstallMonitor_DownloadSize;
+	public static String InstallMonitor_DownloadSizeLong;
+	public static String ProductProvider;
+	public static String JarContentReference_Unpacking;
+	public static String Site_CannotFindCategory;
+	public static String Site_NoCategories;
+	public static String Site_NoContentProvider;
+	public static String VersionedIdentifier_IdOrVersionNull;
+	public static String SiteFile_CannotRemovePlugin;
+	public static String SiteFile_CannotRemoveFeature;
+	public static String SiteFile_UnableToCreateURL;
+	public static String SiteFile_Removing;
+	public static String SiteFileFactory_UnableToCreateURL;
+	public static String SiteFileFactory_UnableToAccessSite;
+	public static String SiteFileFactory_DirectoryDoesNotExist;
+	public static String DefaultFeatureParser_location;
+	public static String DefaultFeatureParser_NoFeatureTag;
+	public static String DefaultFeatureParser_WrongParsingStack;
+	public static String DefaultFeatureParser_UnknownElement;
+	public static String DefaultFeatureParser_TooManyURLtag;
+	public static String DefaultFeatureParser_UnknownStartState;
+	public static String DefaultFeatureParser_IdOrVersionInvalid;
+	public static String DefaultFeatureParser_MissingId;
+	public static String DefaultFeatureParser_ParsingStackBackToInitialState;
+	public static String DefaultFeatureParser_ElementAlreadySet;
+	public static String DefaultFeatureParser_StateIncludeWrongElement;
+	public static String DefaultFeatureParser_RequireStateWithoutImportElement;
+	public static String DefaultFeatureParser_MissingPatchVersion;
+	public static String DefaultFeatureParser_wrongMatchForPatch;
+	public static String DefaultFeatureParser_patchWithPlugin;
+	public static String DefaultFeatureParser_MultiplePatchImports;
+	public static String DefaultFeatureParser_UnknownEndState;
+	public static String DefaultFeatureParser_ErrorParsing;
+	public static String DefaultFeatureParser_ErrorlineColumnMessage;
+	public static String DefaultFeatureParser_ErrorParsingFeature;
+	public static String DefaultFeatureParser_UnknownState;
+	public static String DefaultFeatureParser_NoLicenseText;
+	public static String DefaultFeatureParser_PluginAndFeatureId;
+	public static String SiteContentProvider_ErrorCreatingURLForArchiveID;
+	public static String DefaultSiteParser_NoSiteTag;
+	public static String DefaultSiteParser_WrongParsingStack;
+	public static String DefaultSiteParser_UnknownElement;
+	public static String DefaultSiteParser_UnknownStartState;
+	public static String DefaultSiteParser_Missing;
+	public static String DefaultSiteParser_ParsingStackBackToInitialState;
+	public static String DefaultSiteParser_ElementAlreadySet;
+	public static String DefaultSiteParser_UnknownEndState;
+	public static String DefaultSiteParser_ErrorParsing;
+	public static String DefaultSiteParser_ErrorlineColumnMessage;
+	public static String DefaultSiteParser_ErrorParsingSite;
+	public static String DefaultSiteParser_UnknownState;
+	public static String DefaultSiteParser_InvalidXMLStream;
+	public static String ModelObject_ModelReadOnly;
+	public static String SiteModelObject_ErrorParsingSiteStream;
+	public static String SiteModelObject_ErrorAccessingSiteStream;
+	public static String InstallConfiguration_ErrorDuringFileAccess;
+	public static String InstallConfigurationParser_FeatureReferenceNoURL;
+	public static String FeatureExecutableContentProvider_FileDoesNotExist;
+	public static String FeatureExecutableContentProvider_InvalidDirectory;
+	public static String FeatureExecutableContentProvider_UnableToCreateURLFor;
+	public static String FeatureExecutableContentProvider_UnableToRetrieveNonPluginEntry;
+	public static String FeatureExecutableContentProvider_UnableToRetrieveFeatureEntry;
+	public static String FeatureExecutableContentProvider_UnableToRetrievePluginEntry;
+	public static String ConfiguredSite_NonInstallableSite;
+	public static String ConfiguredSite_NullFeatureToInstall;
+	public static String ConfiguredSite_NonUninstallableSite;
+	public static String ConfiguredSite_NoSite;
+	public static String ConfiguredSite_CannotFindFeatureToUnconfigure;
+	public static String ConfiguredSite_CannotFindFeatureToConfigure;
+	public static String ConfiguredSite_CannotFindPluginEntry;
+	public static String ConfiguredSite_MissingPluginsBrokenFeature;
+	public static String ConfiguredSite_UnableToRemoveConfiguredFeature;
+	public static String ConfiguredSite_UnableToFindFeature;
+	public static String ConfiguredSite_SiteURLNull;
+	public static String ConfiguredSite_NonLocalSite;
+	public static String ConfiguredSite_NotSameProductId;
+	public static String ConfiguredSite_ContainedInAnotherSite;
+	public static String ConfiguredSite_ReadOnlySite;
+	public static String ConfiguredSite_UnableResolveURL;
+	public static String ConfiguredSite_UnableToAccessSite;
+	public static String FeatureFactory_CreatingError;
+	public static String FeatureModelFactory_ErrorAccesingFeatureStream;
+	public static String FeatureExecutableFactory_NullURL;
+	public static String FeatureExecutableFactory_CannotCreateURL;
+	public static String FeaturePackagedContentProvider_NoManifestFile;
+	public static String FeaturePackagedContentProvider_InvalidDirectory;
+	public static String FeaturePackagedContentProvider_ErrorRetrieving;
+	public static String FeatureReference_UnableToResolveURL;
+	public static String FeatureTypeFactory_UnableToFindFeatureFactory;
+	public static String InstallConfiguration_UnableToCreateURL;
+	public static String InstallConfiguration_UnableToCast;
+	public static String InstallConfiguration_UnableToSavePlatformConfiguration;
+	public static String InstallConfiguration_AlreadyNativelyLinked;
+	public static String InstallConfiguration_AlreadyProductSite;
+	public static String InstallConfiguration_unableToFindSite;
+	public static String InternalSiteManager_UnableToCreateSiteWithType;
+	public static String InternalSiteManager_UnableToAccessURL;
+	public static String InternalSiteManager_UnableToCreateURL;
+	public static String InternalSiteManager_FailedRetryAccessingSite;
+	public static String InternalSiteManager_ConnectingToSite;
+	public static String GlobalConsumer_ErrorCreatingFile;
+	public static String SiteFileContentConsumer_UnableToCreateURL;
+	public static String SiteFileContentConsumer_UnableToCreateURLForFile;
+	public static String SiteFileContentConsumer_unableToDelete;
+	public static String ContentConsumer_UnableToRename;
+	public static String SiteFileFactory_UnableToObtainParentDirectory;
+	public static String SiteFileFactory_FileDoesNotExist;
+	public static String SiteFileFactory_UnableToCreateURLForFile;
+	public static String SiteFileFactory_ErrorParsingFile;
+	public static String SiteFileFactory_ErrorAccessing;
+	public static String SiteTypeFactory_UnableToFindSiteFactory;
+	public static String UpdateManagerUtils_UnableToRemoveFile;
+	public static String UpdateManagerUtils_FileAlreadyExists;
+	public static String SiteLocal_UnableToCreateURLFor;
+	public static String SiteLocal_UnableToDetermineFeatureStatusSiteNull;
+	public static String SiteLocal_TwoVersionSamePlugin1;
+	public static String SiteLocal_TwoVersionSamePlugin2;
+	public static String SiteLocal_FeatureUnHappy;
+	public static String SiteLocal_FeatureHappy;
+	public static String SiteLocal_FeatureAmbiguous;
+	public static String SiteLocal_NestedFeatureUnHappy;
+	public static String SiteLocal_NestedFeatureUnavailable;
+	public static String SiteLocal_NoPluginVersion;
+	public static String SiteLocal_UnableToDetermineFeatureStatusConfiguredSiteNull;
+	public static String SiteLocal_FeatureDisable;
+	public static String SiteLocal_FeatureStatusUnknown;
+	public static String SiteLocal_NestedFeatureDisable;
+	public static String SiteURLFactory_UnableToCreateURL;
+	public static String SiteURLFactory_UnableToAccessSiteStream;
+	public static String JarVerifier_Verify;
+	public static String JarVerifier_UnableToFindEncryption;
+	public static String JarVerifier_UnableToLoadCertificate;
+	public static String JarVerifier_UnableToFindProviderForKeystore;
+	public static String JarVerifier_KeyStoreNotLoaded;
+	public static String JarVerifier_UnableToAccessJar;
+	public static String JarVerifier_InvalidFile;
+	public static String JarVerifier_InvalidJar;
+	public static String JarVerificationResult_ValidBetween;
+	public static String JarVerificationResult_ExpiredCertificate;
+	public static String JarVerificationResult_CertificateNotYetValid;
+	public static String JarVerificationResult_CertificateValid;
+	public static String JarVerificationService_UnsucessfulVerification;
+	public static String JarVerificationService_CancelInstall;
+	public static String UpdateManagerUtils_UnableToLog;
+	public static String ConnectionThreadManager_tooManyConnections;
+	public static String ConnectionThreadManager_unresponsiveURL;
+	public static String IncludedFeatureReference_featureUninstalled;
+	public static String ActivityConstraints_warning;
+	public static String ActivityConstraints_rootMessage;
+	public static String ActivityConstraints_rootMessageInitial;
+	public static String ActivityConstraints_beforeMessage;
+	public static String ActivityConstraints_afterMessage;
+	public static String ActivityConstraints_platform;
+	public static String ActivityConstraints_primary;
+	public static String ActivityConstaints_prereq_plugin;
+	public static String ActivityConstaints_prereq_feature;
+	public static String ActivityConstraints_prereq;
+	public static String ActivityConstraints_prereqPerfect;
+	public static String ActivityConstraints_prereqEquivalent;
+	public static String ActivityConstraints_prereqCompatible;
+	public static String ActivityConstraints_prereqGreaterOrEqual;
+	public static String ActivityConstraints_os;
+	public static String ActivityConstraints_ws;
+	public static String ActivityConstraints_arch;
+	public static String ActivityConstraints_cycle;
+	public static String ActivityConstraints_childMessage;
+	public static String ActivityConstraints_optionalChild;
+	public static String ActivityConstraints_exclusive;
+	public static String ActivityConstraints_noLicense;
+	public static String ActivityConstraints_readOnly;
+	public static String ActivityConstraints_platformModified;
+	public static String DuplicateConflictsDialog_conflict;
+	public static String OperationsManager_error_old;
+	public static String OperationsManager_installing;
+	public static String OperationsManager_error_uninstall;
+	public static String Search_networkProblems;
+	public static String InstallConfiguration_location_exists;
+	public static String InstallLogParser_errors;
+	public static String SiteLocal_cloneConfig;
+	public static String UpdateManagerUtils_inputStreamEnded;
+	public static String UpdateSearchRequest_loadingPolicy;
+	public static String UpdateManagerUtils_copy;
+	public static String UpdatePolicy_parsePolicy;
+	public static String UpdatePolicy_policyExpected;
+	public static String UpdateSearchRequest_searching;
+	public static String UpdateSearchRequest_contacting;
+	public static String UpdateSearchRequest_checking;
+	public static String UpdatePolicy_invalidURL;
+	public static String UpdatePolicy_nameNoNull;
+	public static String UpdatePolicy_UpdatePolicy;
+	public static String SiteFile_featureNotRemoved;
+	public static String SiteFile_pluginNotRemoved;
+	public static String ErrorRecoveryLog_noFiletoRemove;
+	public static String UpdatesSearchCategory_errorSearchingForUpdates;
+    public static String UninstallCommand_featureNotInstalledByUM;
+	public static String Standalone_siteConfigured;
+	public static String Standalone_noSite;
+	public static String Standalone_noSite3;
+	public static String Standalone_noConfiguredSite;
+	public static String Standalone_installing;
+	public static String Standalone_notFoundOrNewer;
+	public static String Standalone_duplicate;
+	public static String Standalone_installed;
+	public static String Standalone_cannotInstall;
+	public static String Standalone_noFeatures1;
+	public static String Standalone_noFeatures2;
+	public static String Standalone_noFeatures3;
+	public static String Standalone_noFeatures4;
+	public static String Standalone_noConfigSiteForFeature;
+	public static String Standalone_invalidCmd;
+	public static String Standalone_connection;
+	public static String Standalone_searching;
+	public static String Standalone_cmdFailed;
+	public static String Standalone_cmdFailedNoLog;
+	public static String Standalone_cmdCompleteWithErrors;
+	public static String Standalone_cmdOK;
+	public static String Standalone_updating;
+	public static String Standalone_noUpdate;
+	public static String Standalone_updated;
+	
+	public static String SiteFilePluginContentConsumer_unableToDelete;
+	public static String SiteFilePackedPluginContentConsumer_unableToDelete;	
+	
+	public static String HttpResponse_rangeExpected;
+	public static String HttpResponse_wrongRange;
+	public static String DefaultSiteParser_mirrors;
+	public static String FeatureExecutableContentProvider_UnableToRetriveArchiveContentRef;
+	
+	public static String JarProcessor_unpackNotFound;
+	public static String JarProcessor_noPackUnpack;
+	public static String JarProcessor_packNotFound;
+	
+	public static String SiteOptimizer_inputNotSpecified;
+	public static String SiteOptimizer_inputFileNotFound;
+	public static String SiteCategory_other_label;
+	public static String SiteCategory_other_description;
+
+	static {
+		NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+	}
+
+	public static String InstallCommand_site;
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/NonPluginEntryContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/NonPluginEntryContentConsumer.java
new file mode 100644
index 0000000..e040188
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/NonPluginEntryContentConsumer.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+
+/**
+ * ContentConsumer for a non plugin entry of a feature
+ */
+
+public class NonPluginEntryContentConsumer extends ContentConsumer {
+
+	private boolean closed = false;
+
+	private IContentConsumer contentConsumer;
+		
+	/*
+	 * Constructor
+	 */
+	public NonPluginEntryContentConsumer(IContentConsumer contentConsumer){
+		this.contentConsumer = contentConsumer;
+	}
+
+	/*
+	 * @see ContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor) throws CoreException {
+		if (!closed){
+			contentConsumer.store( contentReference,monitor);
+		} else {
+			UpdateCore.warn("Attempt to store in a closed NonPluginEntryContentConsumer",new Exception()); //$NON-NLS-1$
+		}
+	}
+
+	/*
+	 * @see ContentConsumer#close()
+	 */
+	public void close() throws CoreException  {
+		if (!closed){
+			closed = true;
+			contentConsumer.close();
+		} else {
+			UpdateCore.warn("Attempt to close a closed NonPluginEntryContentConsumer",new Exception()); //$NON-NLS-1$
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/NullContentReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/NullContentReference.java
new file mode 100644
index 0000000..963fa1d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/NullContentReference.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.eclipse.update.core.ContentReference;
+
+/**
+ * NullContentReference implements a general access wrapper 
+ * to feature and site content -- but, for which, there is no 
+ * content actually found. This way, it can "keep" the ID that was 
+ * requested, and still hold a place in lists and arrays, without a 
+ * change to other program logic. It does, how ever require the internal
+ * algorithms to be more careful about assumptions made ... for example, 
+ * just because asFile is null, it does not follow that asURL will not be null. 
+ * <p>
+ * This class may not be instantiated or subclassed by clients. 
+ * </p>
+ * @see org.eclipse.update.core.ContentReference
+ * @see org.eclipse.update.core.JarContentReference
+ * @see org.eclipse.update.core.JarEntryContentReference
+ */
+public class NullContentReference extends ContentReference {
+
+
+	/**
+	 * Contructor for the "missing jar" case. 
+	 * 
+	 * @param id
+	 */
+	public NullContentReference(String id) {
+		super(id, (File) null);
+	}
+	/**
+	 * A factory method to create a content reference of
+	 * the same type.
+	 * 
+	 * @param id "symbolic" path identifier
+	 */
+	public ContentReference createContentReference(String id, File file) {
+		return new NullContentReference(id);
+	}
+
+	/**
+	 * Overrides super class implementation to avoid throwing a FileNotFound exception.
+	 * 
+	 * @return null
+	 */
+	public InputStream getInputStream() throws IOException {
+		return null;
+	}
+	/**
+	 * Overrides super class implementation to avoid throwing a FileNotFound exception.
+	 * 
+	 * @return null
+	 */
+	public File asFile() throws IOException {
+		return null; 
+	}
+
+	/**
+	 * Overrides super class implementation to avoid throwing URL exceptions.
+	 * 
+	 * @return null
+	 */
+	public URL asURL() throws IOException {
+		return null;
+	}
+
+	/**
+	 * Return string representation of this reference.
+	 * 
+	 * @return string representation
+	 */
+	public String toString() {
+			return "Missing archive file: " + '(' + getIdentifier() + ')'; //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/PatchedFeature.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/PatchedFeature.java
new file mode 100644
index 0000000..bc9dae9
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/PatchedFeature.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+/**
+ * Feature and corresponding patch features
+ */
+public class PatchedFeature {
+	private IFeatureReference feature;
+	private Collection patches = new HashSet();
+	/**
+	 *  
+	 */
+	public PatchedFeature(IFeatureReference feature) {
+		super();
+		this.feature = feature;
+	}
+	public void addPatch(IFeatureReference patch) {
+		patches.add(patch);
+	}
+	/**
+	 * @return Returns the feature.
+	 */
+	public IFeatureReference getFeature() {
+		return feature;
+	}
+	/**
+	 * @return Returns the patches.
+	 */
+	public IFeatureReference[] getPatches() {
+		return (IFeatureReference[]) patches.toArray(new IFeatureReference[patches.size()]);
+	}
+	/**
+	 * @return Returns the feature and the patches.
+	 */
+	public IFeatureReference[] getFeatureAndPatches() {
+		IFeatureReference[] features = new IFeatureReference[patches.size() + 1];
+		features[0] = feature;
+		System.arraycopy(getPatches(), 0, features, 1, patches.size());
+		return features;
+	}
+	/**
+	 * Obtains all plugins from the feature and its patches. Each plugin will
+	 * have unique ID.
+	 * If there are multiple version of plugin with same ID among the feature
+	 * and its patches, highest version plugins are chosen.
+	 * 
+	 * @return FeaturePlugin[]
+	 */
+	public FeaturePlugin[] getPlugins() {
+		// Use a map of PatchedPluigns by plugin ID
+		// to collect one version of each plugin
+		Map plugins = new HashMap();
+		IFeatureReference[] featureRefs = getFeatureAndPatches();
+		// for each (feature or any patch)
+		for (int i = 0; i < featureRefs.length; i++) {
+			try {
+				IFeature feature = featureRefs[i].getFeature(null);
+				if (feature == null) {
+					UpdateCore.warn("Null Feature", new Exception()); //$NON-NLS-1$
+					continue;
+				}
+				// get plugin entries
+				IPluginEntry[] entries = feature.getPluginEntries();
+				for (int entr = 0; entr < entries.length; entr++) {
+					String pluginId = entries[entr].getVersionedIdentifier().getIdentifier();
+					PluginVersionIdentifier pluginVersion = entries[entr].getVersionedIdentifier().getVersion();
+					// check if map contains >= version of same plugin
+					FeaturePlugin existingPlugin = (FeaturePlugin) plugins.get(pluginId);
+					if (existingPlugin != null && existingPlugin.getEntry().getVersionedIdentifier().getVersion().isGreaterOrEqualTo(pluginVersion)) {
+						// same or newer plugin already collected
+						continue;
+					} else {
+						plugins.put(pluginId, new FeaturePlugin(entries[entr], feature));
+					}
+				}
+			} catch (CoreException e) {
+				UpdateCore.warn(null, e);
+			}
+		}
+		return (FeaturePlugin[]) plugins.values().toArray(new FeaturePlugin[plugins.size()]);
+	}
+	public String toString() {
+		StringBuffer str = new StringBuffer(feature.toString());
+		IFeatureReference[] patches = getFeatureAndPatches();
+		for (int i = 0; i < patches.length; i++) {
+			str.append(" +patch=" + patches[i].toString() + " "); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		return str.toString();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/PluginEntryContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/PluginEntryContentConsumer.java
new file mode 100644
index 0000000..a549ee5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/PluginEntryContentConsumer.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+
+/**
+ * ContentConsumer for a plugin entry of a feature
+ */
+
+public class PluginEntryContentConsumer extends ContentConsumer {
+
+	private boolean closed = false;
+
+	private IContentConsumer contentConsumer;
+		
+	/*
+	 * Constructor
+	 */
+	public PluginEntryContentConsumer(IContentConsumer contentConsumer){
+		this.contentConsumer = contentConsumer;
+	}
+
+	/*
+	 * @see ContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor) throws CoreException {
+		if (!closed){
+			contentConsumer.store(contentReference,monitor);
+		} else {
+			UpdateCore.warn("Attempt to store in a closed PluginEntryContentConsumer",new Exception()); //$NON-NLS-1$
+		}
+	}
+
+	/*
+	 * @see ContentConsumer#close()
+	 */
+	public void close() throws CoreException {
+		if (!closed){
+			closed = true;
+			contentConsumer.close();
+		} else {
+			UpdateCore.warn("Attempt to close a closed PluginEntryContentConsumer",new Exception()); //$NON-NLS-1$
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ProductProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ProductProvider.java
new file mode 100644
index 0000000..da84d80
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/ProductProvider.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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.update.internal.core;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configurator.*;
+import org.eclipse.update.internal.configurator.FeatureEntry;
+
+/**
+ * Maps primary features to IProduct
+ */
+public class ProductProvider implements IProductProvider {
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProductProvider#getProducts()
+	 */
+	public IProduct[] getProducts() {
+		IPlatformConfiguration configuration = ConfiguratorUtils.getCurrentPlatformConfiguration();
+		if (configuration == null)
+			return new IProduct[0];
+		IPlatformConfiguration.IFeatureEntry[] features = configuration.getConfiguredFeatureEntries();
+		ArrayList primaryFeatures = new ArrayList();
+		for (int i = 0; i < features.length; i++)
+			if (features[i].canBePrimary() && (features[i] instanceof FeatureEntry))
+				primaryFeatures.add(new FeatureEntryWrapper((FeatureEntry) features[i]));
+		// TODO handle unmanaged plugins later
+		return (IProduct[]) primaryFeatures.toArray(new IProduct[primaryFeatures.size()]);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IProductProvider#getName()
+	 */
+	public String getName() {
+		return Messages.ProductProvider;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteContentConsumer.java
new file mode 100644
index 0000000..3c121cd
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteContentConsumer.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+
+import org.eclipse.update.core.*;
+
+/**
+ * A default implementation for IFeatureContentConsumer
+ * </p>
+ * @since 2.0
+ */
+
+public abstract class SiteContentConsumer implements ISiteContentConsumer {
+	
+	private ISite site;
+	
+	/*
+	 * @see ISiteContentConsumer#setSite(ISite)
+	 */
+	/**
+	 * Sets the site.
+	 * @param site The site to set
+	 */
+	public void setSite(ISite site) {
+		this.site = site;
+	}
+
+	/**
+	 * Gets the site.
+	 * @return Returns a ISite
+	 */
+	public ISite getSite() {
+		return site;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFile.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFile.java
new file mode 100644
index 0000000..29c6f31
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFile.java
@@ -0,0 +1,424 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2008 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.update.internal.core;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureContentConsumer;
+import org.eclipse.update.core.IFeatureFactory;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.IInstallHandler;
+import org.eclipse.update.core.INonPluginEntry;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFeatureReference;
+import org.eclipse.update.core.IVerificationListener;
+import org.eclipse.update.core.IVerifier;
+import org.eclipse.update.core.InstallMonitor;
+import org.eclipse.update.core.Site;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.model.ContentEntryModel;
+import org.eclipse.update.core.model.FeatureModel;
+import org.eclipse.update.core.model.FeatureReferenceModel;
+import org.eclipse.update.core.model.InstallAbortedException;
+import org.eclipse.update.internal.operations.UpdateUtils;
+
+
+/**
+ * Site on the File System
+ */
+public class SiteFile extends Site {
+
+	/**
+	 * plugin entries 
+	 */
+	private List pluginEntries = new ArrayList(0);
+
+	/**
+	 * 
+	 */
+	public ISiteContentConsumer createSiteContentConsumer(IFeature targetFeature) throws CoreException {
+		SiteFileContentConsumer consumer = new SiteFileContentConsumer(targetFeature);
+		consumer.setSite(this);
+		return consumer;
+	}
+
+	/**
+	 */
+	public String getDefaultPackagedFeatureType() {
+		return DEFAULT_INSTALLED_FEATURE_TYPE;
+	}
+
+	/*
+	 * @see ISite#install(IFeature, IVerifier, IProgressMonitor)
+	 */
+	public IFeatureReference install(IFeature sourceFeature, IVerificationListener verificationListener, IProgressMonitor progress) throws CoreException {
+		return install(sourceFeature,null,verificationListener,progress);
+	}
+
+	/*
+	 * @see ISite#install(IFeature, IVerifier, IProgressMonitor)
+	 */
+	public IFeatureReference install(IFeature sourceFeature, IFeatureReference[] optionalfeatures, IVerificationListener verificationListener, IProgressMonitor progress) throws CoreException {
+
+		if (sourceFeature == null)
+			return null;
+
+		// make sure we have an InstallMonitor		
+		InstallMonitor monitor;
+		if (progress == null)
+			monitor = null;
+		else if (progress instanceof InstallMonitor)
+			monitor = (InstallMonitor) progress;
+		else
+			monitor = new InstallMonitor(progress);
+
+		// create new executable feature and install source content into it
+		IFeature localFeature = createExecutableFeature(sourceFeature);
+
+		IFeatureReference localFeatureReference = null;
+		localFeatureReference = sourceFeature.install(localFeature, optionalfeatures, verificationListener, monitor);
+
+		return localFeatureReference;
+	}
+
+	/*
+	 * @see ISite#install(IFeature,IFeatureContentConsumer, IVerifier,IVerificationLIstener, IProgressMonitor)
+	 */
+	public IFeatureReference install(IFeature sourceFeature, IFeatureReference[] optionalfeatures, IFeatureContentConsumer parentContentConsumer, IVerifier parentVerifier, IVerificationListener verificationListener, IProgressMonitor progress)
+		throws InstallAbortedException, CoreException {
+
+		if (sourceFeature == null)
+			return null;
+
+		// make sure we have an InstallMonitor		
+		InstallMonitor monitor;
+		if (progress == null)
+			monitor = null;
+		else if (progress instanceof InstallMonitor)
+			monitor = (InstallMonitor) progress;
+		else
+			monitor = new InstallMonitor(progress);
+
+		// create new executable feature and install source content into it
+		IFeature localFeature = createExecutableFeature(sourceFeature);
+		parentContentConsumer.addChild(localFeature);
+
+		// set the verifier
+		IVerifier vr = sourceFeature.getFeatureContentProvider().getVerifier();
+		if (vr != null)
+			vr.setParent(parentVerifier);
+
+		IFeatureReference localFeatureReference = null;
+		localFeatureReference = sourceFeature.install(localFeature, optionalfeatures, verificationListener, monitor);
+
+		return localFeatureReference;
+	}
+
+	/*
+	 * @see ISite#remove(IFeature, IProgressMonitor)
+	 */
+	public void remove(IFeature feature, IProgressMonitor progress) throws CoreException {
+
+		if (feature == null) {
+			UpdateCore.warn("Feature to remove is null"); //$NON-NLS-1$
+			return;
+		}
+		
+		ErrorRecoveryLog recoveryLog = ErrorRecoveryLog.getLog();
+
+		// make sure we have an InstallMonitor		
+		InstallMonitor monitor;
+		if (progress == null)
+			monitor = null;
+		else if (progress instanceof InstallMonitor)
+			monitor = (InstallMonitor) progress;
+		else
+			monitor = new InstallMonitor(progress);
+
+		// Setup optional install handler
+		InstallHandlerProxy handler = new InstallHandlerProxy(IInstallHandler.HANDLER_ACTION_UNINSTALL, feature, feature.getInstallHandlerEntry(), monitor);
+		boolean success = false;
+		Throwable originalException = null;
+
+		try {
+
+			// start log
+			recoveryLog.open(ErrorRecoveryLog.START_REMOVE_LOG);
+
+			// log files have been downloaded
+			recoveryLog.append(ErrorRecoveryLog.END_ABOUT_REMOVE);
+
+			handler.uninstallInitiated();
+
+			// remove the feature and the plugins if they are not used and not activated
+			// get the plugins from the feature
+			IPluginEntry[] pluginsToRemove = getPluginEntriesOnlyReferencedBy(feature);
+
+			if (monitor != null) {
+				monitor.beginTask(Messages.SiteFile_Removing + feature.getLabel(), pluginsToRemove.length + 1);	
+			}
+
+			// remove feature reference from the site
+			ISiteFeatureReference[] featureReferences = getFeatureReferences();
+			if (featureReferences != null) {
+				for (int indexRef = 0; indexRef < featureReferences.length; indexRef++) {
+					IFeatureReference element = featureReferences[indexRef];
+					if (element.getVersionedIdentifier().equals(feature.getVersionedIdentifier())) {
+						removeFeatureReferenceModel((FeatureReferenceModel) element);
+						break;
+					}
+				}
+			}
+
+			if (InstallRegistry.getInstance().get("feature_"+feature.getVersionedIdentifier()) == null) { //$NON-NLS-1$
+				UpdateCore.log(NLS.bind(Messages.SiteFile_featureNotRemoved, (new String[] { feature.getVersionedIdentifier().toString() })), null);
+			} else {
+				// remove the feature content
+				ContentReference[] references = feature.getFeatureContentProvider().getFeatureEntryArchiveReferences(monitor);
+				for (int i = 0; i < references.length; i++) {
+					try {
+						UpdateManagerUtils.removeFromFileSystem(references[i].asFile());
+						if (monitor != null)
+							monitor.worked(1);
+					} catch (IOException e) {
+						throw Utilities.newCoreException(NLS.bind(Messages.SiteFile_CannotRemoveFeature, (new String[] { feature.getVersionedIdentifier().getIdentifier(), getURL().toExternalForm() })), e);
+					}
+				}
+				InstallRegistry.unregisterFeature(feature);
+			}
+
+			//finds the contentReferences for an IPluginEntry
+			// and remove it
+			for (int i = 0; i < pluginsToRemove.length; i++) {
+				remove(feature, pluginsToRemove[i], monitor);
+			}
+
+			// remove any children feature
+			IFeatureReference[] childrenRef = feature.getIncludedFeatureReferences();
+			for (int i = 0; i < childrenRef.length; i++) {
+				IFeature childFeature = null;
+				try {
+					childFeature = childrenRef[i].getFeature(null);
+				} catch (CoreException e) {
+					UpdateCore.warn("Unable to retrieve feature to remove for:" + childrenRef[i]); //$NON-NLS-1$
+				}
+                // do not remove nested feature if configured (i.e. used by another configured feature)
+				if (childFeature != null && !getCurrentConfiguredSite().isConfigured(childFeature))
+					remove(childrenRef[i].getFeature(null), monitor);
+			}
+
+			// remove the feature from the site cache
+			removeFeatureFromCache(feature.getURL());
+			
+			handler.completeUninstall();
+
+			success = true;
+		} catch (Throwable t) {
+			originalException = t;
+		} finally {
+			Throwable newException = null;
+			try {
+				if (success) {
+					// close the log
+					recoveryLog.close(ErrorRecoveryLog.END_REMOVE_LOG);
+					recoveryLog.delete();
+				} else {
+					recoveryLog.close(ErrorRecoveryLog.END_REMOVE_LOG);
+				}
+				handler.uninstallCompleted(success);
+			} catch (Throwable t) {
+				newException = t;
+			}
+			if (originalException != null) // original exception wins
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallHandler_error, (new String[] { feature.getLabel() })), originalException);
+			if (newException != null)
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallHandler_error, (new String[] { feature.getLabel() })), newException);
+		}
+	}
+
+	/**
+	 * returns the download size
+	 * of the feature to be installed on the site.
+	 * If the site is <code>null</code> returns the maximum size
+	 * 
+	 * If one plug-in entry has an unknown size.
+	 * then the download size is unknown.
+	 * 
+	 */
+	public long getDownloadSizeFor(IFeature feature) {
+		long result = 0;
+		//[132029]
+		//IPluginEntry[] entriesToInstall = feature.getPluginEntries();
+		//IPluginEntry[] siteEntries = this.getPluginEntries();
+		//entriesToInstall = UpdateManagerUtils.diff(entriesToInstall, siteEntries);
+		//[18355]
+		//INonPluginEntry[] nonPluginEntriesToInstall = feature.getNonPluginEntries();
+
+		try {
+			//[132029]
+			//result = feature.getFeatureContentProvider().getDownloadSizeFor(entriesToInstall, nonPluginEntriesToInstall);
+			IFeatureReference[] children = feature.getIncludedFeatureReferences();
+			IFeature currentFeature = null;
+			for (int i = 0; i < children.length; i++) {
+				currentFeature = UpdateUtils.getIncludedFeature(feature, children[i]);
+				if (currentFeature != null) {
+					result += getDownloadSizeFor(currentFeature);
+					if(result == ContentEntryModel.UNKNOWN_SIZE)
+						return result;
+				}
+			}
+
+			IPluginEntry[] entriesToInstall = feature.getPluginEntries();
+			IPluginEntry[] siteEntries = this.getPluginEntries();
+			entriesToInstall = UpdateManagerUtils.diff(entriesToInstall, siteEntries);
+			//[18355]
+			   INonPluginEntry[] nonPluginEntriesToInstall = feature.getNonPluginEntries();
+
+			 result += feature.getFeatureContentProvider().getDownloadSizeFor(entriesToInstall, nonPluginEntriesToInstall);
+		} catch (CoreException e) {
+			UpdateCore.warn(null, e);
+			result = ContentEntryModel.UNKNOWN_SIZE;
+		}
+		return result;
+	}
+
+	/**
+	 * returns the download size
+	 * of the feature to be installed on the site.
+	 * If the site is <code>null</code> returns the maximum size
+	 * 
+	 * If one plug-in entry has an unknown size.
+	 * then the download size is unknown.
+	 * 
+	 * @see ISite#getDownloadSizeFor(IFeature)
+	 * 
+	 */
+	public long getInstallSizeFor(IFeature feature) {
+		long result = 0;
+
+		try {
+			List pluginsToInstall = new ArrayList();
+
+			// get all the plugins [17304]
+			pluginsToInstall.addAll(Arrays.asList(feature.getPluginEntries()));
+			IFeatureReference[] children = feature.getIncludedFeatureReferences();
+			IFeature currentFeature = null;
+			for (int i = 0; i < children.length; i++) {
+				currentFeature = UpdateUtils.getIncludedFeature(feature, children[i]);
+				if (currentFeature != null) {
+					//[132029]
+					//pluginsToInstall.addAll(Arrays.asList(currentFeature.getPluginEntries()));
+					result += getInstallSizeFor(currentFeature);
+					if (result == ContentEntryModel.UNKNOWN_SIZE)
+						return result;
+				}
+			}
+
+			IPluginEntry[] entriesToInstall = new IPluginEntry[0];
+			if (pluginsToInstall.size() > 0) {
+				entriesToInstall = new IPluginEntry[pluginsToInstall.size()];
+				pluginsToInstall.toArray(entriesToInstall);
+			}
+
+			IPluginEntry[] siteEntries = this.getPluginEntries();
+			entriesToInstall = UpdateManagerUtils.diff(entriesToInstall, siteEntries);
+
+			//[18355]
+			INonPluginEntry[] nonPluginEntriesToInstall = feature.getNonPluginEntries();
+			
+			//[132029]
+			//result = feature.getFeatureContentProvider().getInstallSizeFor(entriesToInstall, nonPluginEntriesToInstall);
+			result += feature.getFeatureContentProvider().getInstallSizeFor(entriesToInstall, nonPluginEntriesToInstall);
+		} catch (CoreException e) {
+			UpdateCore.warn(null, e);
+			result = ContentEntryModel.UNKNOWN_SIZE;
+		}
+
+		return result;
+	}
+
+	/**
+	 * Adds a plugin entry 
+	 * Either from parsing the file system or 
+	 * installing a feature
+	 * 
+	 * We cannot figure out the list of plugins by reading the Site.xml as
+	 * the archives tag are optionals
+	 */
+	public void addPluginEntry(IPluginEntry pluginEntry) {
+		pluginEntries.add(pluginEntry);
+	}
+
+	public IPluginEntry[] getPluginEntries() {
+		IPluginEntry[] result = new IPluginEntry[0];
+		if (!(pluginEntries == null || pluginEntries.isEmpty())) {
+			result = new IPluginEntry[pluginEntries.size()];
+			pluginEntries.toArray(result);
+		}
+		return result;
+	}
+
+
+	public int getPluginEntryCount() {
+		return getPluginEntries().length;
+	}
+
+	/**
+	 * 
+	 */
+	private IFeature createExecutableFeature(IFeature sourceFeature) throws CoreException {
+		IFeature result = null;
+		IFeatureFactory factory = FeatureTypeFactory.getInstance().getFactory(DEFAULT_INSTALLED_FEATURE_TYPE);
+		result = factory.createFeature(/*URL*/null, this, null);
+
+		// at least set the version identifier to be the same
+		 ((FeatureModel) result).setFeatureIdentifier(sourceFeature.getVersionedIdentifier().getIdentifier());
+		((FeatureModel) result).setFeatureVersion(sourceFeature.getVersionedIdentifier().getVersion().toString());
+		return result;
+	}
+
+	/**
+	 * 
+	 */
+	private void remove(IFeature feature, IPluginEntry pluginEntry, InstallMonitor monitor) throws CoreException {
+
+		if (pluginEntry == null)
+			return;
+			
+		if (InstallRegistry.getInstance().get("plugin_"+pluginEntry.getVersionedIdentifier()) == null) { //$NON-NLS-1$
+			UpdateCore.log(NLS.bind(Messages.SiteFile_pluginNotRemoved, (new String[] { pluginEntry.getVersionedIdentifier().toString() })), null);
+			return; 
+		}
+
+		ContentReference[] references = feature.getFeatureContentProvider().getPluginEntryArchiveReferences(pluginEntry, monitor);
+		for (int i = 0; i < references.length; i++) {
+			try {
+				UpdateManagerUtils.removeFromFileSystem(references[i].asFile());
+				if (monitor != null)
+					monitor.worked(1);
+			} catch (IOException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.SiteFile_CannotRemovePlugin, (new String[] { pluginEntry.getVersionedIdentifier().toString(), getURL().toExternalForm() })), e);
+			}
+		}
+		pluginEntries.remove(pluginEntry);
+		InstallRegistry.unregisterPlugin(pluginEntry);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileContentConsumer.java
new file mode 100644
index 0000000..d1e4139
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileContentConsumer.java
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import org.eclipse.update.core.FeatureContentProvider;
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+
+/**
+ * ContentConsummer for a SiteFile
+ */
+public class SiteFileContentConsumer extends SiteContentConsumer {
+
+	private IFeature feature;
+	private boolean closed = false;
+
+	// recovery
+	private String oldPath;
+	private String newPath;
+
+	//  for abort
+	private List /* of SiteFilePluginContentConsumer */
+	contentConsumers;
+	private List /*of path as String */
+	installedFiles;
+	
+	// PERF: new instance variable
+	private SiteFileFactory archiveFactory = new SiteFileFactory();
+
+	/*
+	 * Constructor 
+	 */
+	public SiteFileContentConsumer(IFeature feature) {
+		this.feature = feature;
+		installedFiles = new ArrayList();
+	}
+
+	/*
+	 * Returns the path in which the Feature will be installed
+	 */
+	private String getFeaturePath() throws CoreException {
+		String featurePath = null;
+		try {
+			VersionedIdentifier featureIdentifier = feature.getVersionedIdentifier();
+			String path = Site.DEFAULT_INSTALLED_FEATURE_PATH + featureIdentifier.toString() + File.separator;
+			URL newURL = new URL(getSite().getURL(), path);
+			featurePath = newURL.getFile();
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(Messages.SiteFileContentConsumer_UnableToCreateURL + e.getMessage(), e);	
+		}
+		return featurePath;
+	}
+
+	/*
+	 * @see ISiteContentConsumer#open(INonPluginEntry)
+	 */
+	public IContentConsumer open(INonPluginEntry nonPluginEntry) throws CoreException {
+		return new SiteFileNonPluginContentConsumer(getFeaturePath());
+	}
+
+	/*
+	 * @see ISiteContentConsumer#open(IPluginEntry)
+	 */
+	public IContentConsumer open(IPluginEntry pluginEntry) throws CoreException {
+		ContentConsumer cons;
+		if(pluginEntry instanceof PluginEntryModel && !((PluginEntryModel)pluginEntry).isUnpack()){
+			// plugin can run from a jar
+			 cons = new SiteFilePackedPluginContentConsumer(pluginEntry, getSite());
+		} else{
+			// plugin must be unpacked
+			cons = new SiteFilePluginContentConsumer(pluginEntry, getSite());
+		}
+		addContentConsumers(cons);
+		return cons;
+	}
+
+	/*
+	 * @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor) throws CoreException {
+
+		if (closed) {
+			UpdateCore.warn("Attempt to store in a closed SiteFileContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		InputStream inStream = null;
+		String featurePath = getFeaturePath();
+		String contentKey = contentReference.getIdentifier();
+		featurePath += contentKey;
+
+		// error recovery
+		if (featurePath.endsWith("\\"+Feature.FEATURE_XML) || featurePath.endsWith("/"+Feature.FEATURE_XML)) { //$NON-NLS-1$ //$NON-NLS-2$
+			oldPath = featurePath.replace(File.separatorChar, '/');
+			File localFile = new File(oldPath);
+			if (localFile.exists()) {
+				throw Utilities.newCoreException(NLS.bind(Messages.UpdateManagerUtils_FileAlreadyExists, (new Object[] { localFile })), null);
+			}
+			featurePath = ErrorRecoveryLog.getLocalRandomIdentifier(featurePath);
+			newPath = featurePath;
+			ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.FEATURE_ENTRY, featurePath);
+		}
+
+		try {
+			inStream = contentReference.getInputStream();
+			UpdateManagerUtils.copyToLocal(inStream, featurePath, null);
+			UpdateManagerUtils.checkPermissions(contentReference, featurePath); // 20305
+			installedFiles.add(featurePath);
+		} catch (IOException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.GlobalConsumer_ErrorCreatingFile, (new String[] { featurePath })), e);
+		} finally {
+			if (inStream != null) {
+				try {
+					// close stream
+					inStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+
+	}
+
+	/*
+	 * @see ISiteContentConsumer#close()
+	 */
+	public IFeatureReference close() throws CoreException {
+
+		if (closed)
+			UpdateCore.warn("Attempt to close a closed SiteFileContentConsumer", new Exception()); //$NON-NLS-1$
+
+		// create a new Feature reference to be added to the site
+		SiteFeatureReference ref = new SiteFeatureReference();
+		ref.setSite(getSite());
+		File file = null;
+
+		try {
+			file = new File(getFeaturePath());
+			ref.setURL(file.toURL());
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteFileContentConsumer_UnableToCreateURLForFile, (new String[] { file.getAbsolutePath() })), e);
+		}
+
+		//rename file back 
+		if (newPath != null) {
+			ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.RENAME_ENTRY, newPath);
+			boolean sucess = false;
+			File fileToRename = new File(newPath);
+			if (fileToRename.exists()) {
+				File renamedFile = new File(oldPath);
+				if (renamedFile.exists()) {
+					UpdateManagerUtils.removeFromFileSystem(renamedFile);
+					UpdateCore.warn("Removing already existing file:" + oldPath); //$NON-NLS-1$
+				}
+				sucess = fileToRename.renameTo(renamedFile);
+			}
+			if (!sucess) {
+				String msg = NLS.bind(Messages.ContentConsumer_UnableToRename, (new String[] { newPath, oldPath }));
+				throw Utilities.newCoreException(msg, new Exception(msg));
+			}
+		}
+
+		// close plugin and non plugin content consumer
+		if (contentConsumers != null) {
+			Iterator iter = contentConsumers.iterator();
+			while (iter.hasNext()) {
+				ContentConsumer element = (ContentConsumer) iter.next();
+				element.close();
+			}
+		}
+		contentConsumers = null;
+
+		if (ref != null) {
+			// the feature MUST have renamed the plugins at that point
+			// (by closing the PluginContentConsumer)
+			commitPlugins(ref);
+			ref.markReadOnly();
+		}
+
+		closed = true;
+		return ref;
+	}
+
+	/*
+	 * @see ISiteContentConsumer#abort()
+	 */
+	public void abort() throws CoreException {
+
+		if (closed) {
+			UpdateCore.warn("Attempt to abort a closed SiteFileContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		//abort all plugins content consumer opened
+		if (contentConsumers != null) {
+			Iterator iter = contentConsumers.iterator();
+			while (iter.hasNext()) {
+				Object element = iter.next();
+				if (element instanceof  SiteFilePluginContentConsumer) {
+					((SiteFilePluginContentConsumer)element).abort();
+				} else if (element instanceof  SiteFilePackedPluginContentConsumer){
+					((SiteFilePackedPluginContentConsumer)element).abort();
+				}
+				
+			}
+		}
+		contentConsumers = null;
+		boolean sucess = true;
+
+		//Remove feature.xml first if it exists
+		if (oldPath != null) {
+			ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.DELETE_ENTRY, oldPath);
+			File fileToDelete = new File(oldPath);
+			if (fileToDelete.exists()) {
+				sucess = fileToDelete.delete();
+			}
+		}
+
+		if (!sucess) {
+			String msg = NLS.bind(Messages.SiteFileContentConsumer_unableToDelete, (new String[] { oldPath })); 
+			UpdateCore.log(msg, null);
+		} else {
+			// remove the feature files;
+			Iterator iter = installedFiles.iterator();
+			File featureFile = null;
+			while (iter.hasNext()) {
+				String path = (String) iter.next();
+				featureFile = new File(path);
+				UpdateManagerUtils.removeFromFileSystem(featureFile);
+			}
+
+			// remove the feature directory if empty
+			String featurePath = getFeaturePath();
+			UpdateManagerUtils.removeEmptyDirectoriesFromFileSystem(new File(featurePath));
+		}
+		closed = true;
+		return;
+	}
+
+	/*
+	 * commit the plugins installed as archive on the site
+	 * (creates the map between the plugin id and the location of the plugin)
+	 */
+	private void commitPlugins(IFeatureReference localFeatureReference) throws CoreException {
+	
+		// get the feature
+		 ((SiteFile) getSite()).addFeatureReferenceModel((SiteFeatureReferenceModel) localFeatureReference);
+		IFeature localFeature = null;
+		try {
+			localFeature = localFeatureReference.getFeature(null);
+		} catch (CoreException e) {
+			UpdateCore.warn(null, e);
+			return;
+		}
+	
+		if (localFeature == null)
+			return;
+	
+		// add the installed plugins directories as archives entry
+		ArchiveReferenceModel archive = null;
+		IPluginEntry[] pluginEntries = localFeature.getPluginEntries();
+		for (int i = 0; i < pluginEntries.length; i++) {
+			String versionId = pluginEntries[i].getVersionedIdentifier().toString();
+			String pluginID = Site.DEFAULT_PLUGIN_PATH + versionId + FeatureContentProvider.JAR_EXTENSION;
+			archive = archiveFactory.createArchiveReferenceModel();
+			archive.setPath(pluginID);
+			try {
+				URL url = null;
+				if (pluginEntries[i] instanceof PluginEntryModel
+						&& !((PluginEntryModel) pluginEntries[i]).isUnpack()) {
+					url = new URL(getSite().getURL(), Site.DEFAULT_PLUGIN_PATH	+ versionId + ".jar"); //$NON-NLS-1$
+				} else {
+					url = new URL(getSite().getURL(), Site.DEFAULT_PLUGIN_PATH	+ versionId + File.separator);
+				}
+				archive.setURLString(url.toExternalForm());
+				archive.resolve(url, null);
+				((SiteFile) getSite()).addArchiveReferenceModel(archive);
+			} catch (MalformedURLException e) {
+	
+				String urlString = (getSite().getURL() != null) ? getSite().getURL().toExternalForm() : "";	//$NON-NLS-1$
+				urlString += Site.DEFAULT_PLUGIN_PATH + pluginEntries[i].toString();
+				throw Utilities.newCoreException(NLS.bind(Messages.SiteFile_UnableToCreateURL, (new String[] { urlString })), e);
+			}
+		}
+		return;
+	}
+
+	/*
+	 * Adds a SiteFilePluginContentConsumer to the list
+	 */
+	private void addContentConsumers(ContentConsumer cons) {
+		if (contentConsumers == null)
+			contentConsumers = new ArrayList();
+		contentConsumers.add(cons);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileContentProvider.java
new file mode 100644
index 0000000..09ce8c2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileContentProvider.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.net.URL;
+
+import org.eclipse.update.core.SiteContentProvider;
+
+/**
+ * Site on the File System
+ */
+public class SiteFileContentProvider extends SiteContentProvider {
+	
+	public static final String SITE_TYPE = "org.eclipse.update.core.file";	 //$NON-NLS-1$
+
+	/**
+	 * Constructor for FileSite
+	 */
+	public SiteFileContentProvider(URL url) {
+		super(url);
+	}
+}
+
+
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileFactory.java
new file mode 100644
index 0000000..24dc5ca
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileFactory.java
@@ -0,0 +1,446 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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.update.internal.core;
+
+import org.eclipse.update.core.FeatureContentProvider;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.BaseSiteFactory;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.Feature;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.JarContentReference;
+import org.eclipse.update.core.PluginEntry;
+import org.eclipse.update.core.Site;
+import org.eclipse.update.core.SiteContentProvider;
+import org.eclipse.update.core.SiteFeatureReferenceModel;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.model.ArchiveReferenceModel;
+import org.eclipse.update.core.model.InvalidSiteTypeException;
+import org.eclipse.update.core.model.SiteModel;
+import org.eclipse.update.core.model.SiteModelFactory;
+import org.eclipse.update.internal.model.BundleManifest;
+import org.eclipse.update.internal.model.DefaultPluginParser;
+import org.xml.sax.SAXException;
+
+public class SiteFileFactory extends BaseSiteFactory {
+
+	// private when parsing file system
+	private SiteFile site;
+
+	/*
+	 * @see ISiteFactory#createSite(URL,boolean)
+	 */
+	public ISite createSite(URL url) throws CoreException, InvalidSiteTypeException {
+
+		Site site = null;
+		InputStream siteStream = null;
+		SiteModelFactory factory = this;
+
+		try {
+			// if url points to a directory
+			// attempt to parse site.xml
+			String path = url.getFile();
+			File siteLocation = new File(path);
+			if (siteLocation.isDirectory()) {
+				url = siteLocation.toURL();
+				File siteXMLFile = new File(siteLocation, Site.SITE_XML);
+				if (siteXMLFile.exists()) {
+					siteStream = new FileInputStream(siteXMLFile);
+					site = (Site) factory.parseSite(siteStream);
+				} else {
+					// parse siteLocation
+					site = parseSite(siteLocation);
+				}
+			} else {
+				// we are not pointing to a directory
+				// attempt to parse the file
+				try {
+					URL resolvedURL = URLEncoder.encode(url);
+					siteStream = openStream(resolvedURL);
+					site = (Site) factory.parseSite(siteStream);
+				} catch (IOException e) {
+
+					// attempt to parse parent directory
+					File file = new File(url.getFile());
+					File parentDirectory = file.getParentFile();
+
+					// do not create directory if it doesn't exist	[18318]
+					// instead hrow error					
+					if (parentDirectory != null && !parentDirectory.exists())
+						throw Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_DirectoryDoesNotExist, (new String[] {file.getAbsolutePath()})), null);
+
+					if (parentDirectory == null || !parentDirectory.isDirectory())
+						throw Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_UnableToObtainParentDirectory, (new String[] {file.getAbsolutePath()})), null);
+
+					site = parseSite(parentDirectory);
+
+				}
+			}
+
+			SiteContentProvider contentProvider = new SiteFileContentProvider(url);
+			site.setSiteContentProvider(contentProvider);
+			contentProvider.setSite(site);
+			site.resolve(url, url);
+
+			// Do not set read only as may install in it
+			//site.markReadOnly();
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_UnableToCreateURL, (new String[] {url == null ? "" : url.toExternalForm()})), e); //$NON-NLS-1$
+		} catch (IOException e) {
+			throw Utilities.newCoreException(Messages.SiteFileFactory_UnableToAccessSite, ISite.SITE_ACCESS_EXCEPTION, e);
+		} finally {
+			try {
+				if (siteStream != null)
+					siteStream.close();
+			} catch (IOException e) {
+			}
+		}
+		return site;
+	}
+
+	/**
+	 * Method parseSite.
+	 */
+	private Site parseSite(File directory) throws CoreException {
+
+		this.site = (SiteFile) createSiteMapModel();
+
+		if (!directory.exists())
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_FileDoesNotExist, (new String[] {directory.getAbsolutePath()})), null);
+
+		File pluginPath = new File(directory, Site.DEFAULT_PLUGIN_PATH);
+
+		//PACKAGED
+		try {
+			parsePackagedFeature(directory); // in case it contains JAR files
+		} catch (EmptyDirectoryException ede) {
+			UpdateCore.log(ede.getStatus());
+		}
+
+		try {
+			parsePackagedPlugins(pluginPath);
+		} catch (EmptyDirectoryException ede) {
+			UpdateCore.log(ede.getStatus());
+		}
+
+		// INSTALLED
+		try {
+			parseInstalledFeature(directory);
+		} catch (EmptyDirectoryException ede) {
+			UpdateCore.log(ede.getStatus());
+		}
+
+		try {
+			parseInstalledPlugins(pluginPath);
+		} catch (EmptyDirectoryException ede) {
+			UpdateCore.log(ede.getStatus());
+		}
+
+		return site;
+
+	}
+
+	/**
+	 * Method parseFeature.
+	 * @throws CoreException
+	 */
+	private void parseInstalledFeature(File directory) throws CoreException {
+
+		File featureDir = new File(directory, Site.DEFAULT_INSTALLED_FEATURE_PATH);
+		if (featureDir.exists()) {
+			String[] dir;
+			SiteFeatureReferenceModel featureRef;
+			URL featureURL;
+			File currentFeatureDir;
+			String newFilePath = null;
+
+			try {
+				// handle the installed featuresConfigured under featuresConfigured subdirectory
+				dir = featureDir.list();
+				if (dir == null) {
+					throw new EmptyDirectoryException(new Status(IStatus.WARNING, UpdateCore.getPlugin().getBundle().getSymbolicName(), IStatus.OK, directory.getName() + File.separator + directory.getName() + "directory is empty", null)); //$NON-NLS-1$
+				}
+				for (int index = 0; index < dir.length; index++) {
+
+					// the URL must ends with '/' for the bundle to be resolved
+					newFilePath = dir[index] + (dir[index].endsWith("/") ? "/" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					currentFeatureDir = new File(featureDir, newFilePath);
+					// check if feature.xml exists
+					File featureXMLFile = new File(currentFeatureDir, Feature.FEATURE_XML);
+					if (!featureXMLFile.exists()) {
+						UpdateCore.warn("Unable to find feature.xml in directory:" + currentFeatureDir); //$NON-NLS-1$
+					} else {
+						// PERF: remove code
+						//SiteFileFactory archiveFactory = new SiteFileFactory();
+						featureURL = currentFeatureDir.toURL();
+						featureRef = createFeatureReferenceModel();
+						featureRef.setSiteModel(site);
+						featureRef.setURLString(featureURL.toExternalForm());
+						featureRef.setType(ISite.DEFAULT_INSTALLED_FEATURE_TYPE);
+						((Site) site).addFeatureReferenceModel(featureRef);
+					}
+				}
+			} catch (MalformedURLException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_UnableToCreateURLForFile, (new String[] {newFilePath})), e);
+			}
+		}
+	}
+
+	/**
+	* Method parseFeature.
+	* @throws CoreException
+	*/
+	private void parsePackagedFeature(File directory) throws CoreException {
+
+		// FEATURES
+		File featureDir = new File(directory, Site.DEFAULT_FEATURE_PATH);
+		if (featureDir.exists()) {
+			String[] dir;
+			SiteFeatureReferenceModel featureRef;
+			URL featureURL;
+			File currentFeatureFile;
+			String newFilePath = null;
+
+			try {
+				// only list JAR files
+				dir = featureDir.list(FeaturePackagedContentProvider.filter);
+				if (dir == null) {
+					throw new EmptyDirectoryException(new Status(IStatus.WARNING, UpdateCore.getPlugin().getBundle().getSymbolicName(), IStatus.OK, directory.getName() + File.separator + directory.getName() + "directory is empty", null)); //$NON-NLS-1$
+				}
+
+				for (int index = 0; index < dir.length; index++) {
+
+					// check if the JAR file contains a feature.xml
+					currentFeatureFile = new File(featureDir, dir[index]);
+					JarContentReference ref = new JarContentReference("", currentFeatureFile); //$NON-NLS-1$
+					ContentReference result = null;
+					try {
+						result = ref.peek(Feature.FEATURE_XML, null, null);
+					} catch (IOException e) {
+						UpdateCore.warn("Exception retrieving feature.xml in file:" + currentFeatureFile, e); //$NON-NLS-1$
+					}
+					if (result == null) {
+						UpdateCore.warn("Unable to find feature.xml in file:" + currentFeatureFile); //$NON-NLS-1$
+					} else {
+						featureURL = currentFeatureFile.toURL();
+						// PERF: remove code
+						//SiteFileFactory archiveFactory = new SiteFileFactory();
+						featureRef = createFeatureReferenceModel();
+						featureRef.setSiteModel(site);
+						featureRef.setURLString(featureURL.toExternalForm());
+						featureRef.setType(ISite.DEFAULT_PACKAGED_FEATURE_TYPE);
+						site.addFeatureReferenceModel(featureRef);
+					}
+				}
+			} catch (MalformedURLException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_UnableToCreateURLForFile, (new String[] {newFilePath})), e);
+			}
+		}
+	}
+
+	/**
+	 * Method parsePlugins.
+	 * 
+	 * look into each plugin/fragment directory, crack the plugin.xml open (or
+	 * fragment.xml ???) get id and version, calculate URL...
+	 * 
+	 * @throws CoreException
+	 */
+	private void parseInstalledPlugins(File pluginsDir) throws CoreException {
+		if (!pluginsDir.exists() || !pluginsDir.isDirectory()) {
+			return;
+		}
+		File[] dirs = pluginsDir.listFiles(new FileFilter() {
+			public boolean accept(File f) {
+				return f.isDirectory();
+			}
+		});
+		DefaultPluginParser parser = new DefaultPluginParser();
+
+		if (dirs == null) {
+			throw new EmptyDirectoryException(new Status(IStatus.WARNING, UpdateCore.getPlugin().getBundle().getSymbolicName(), IStatus.OK, pluginsDir.getName() + File.separator + pluginsDir.getName() + "directory is empty", null)); //$NON-NLS-1$
+		}
+		for (int i = 0; i < dirs.length; i++) {
+			File pluginFile = new File(dirs[i], "META-INF/MANIFEST.MF"); //$NON-NLS-1$
+			InputStream in = null;
+			try {
+				BundleManifest bundleManifest = new BundleManifest(pluginFile);
+				if (bundleManifest.exists()) {
+					PluginEntry entry = bundleManifest.getPluginEntry();
+					addParsedPlugin(entry, dirs[i]);
+				} else {
+					if (!(pluginFile = new File(dirs[i], "plugin.xml")) //$NON-NLS-1$
+							.exists()) {
+						pluginFile = new File(dirs[i], "fragment.xml"); //$NON-NLS-1$
+					}
+					if (pluginFile != null && pluginFile.exists() && !pluginFile.isDirectory()) {
+						in = new FileInputStream(pluginFile);
+						PluginEntry entry = parser.parse(in);
+						addParsedPlugin(entry, dirs[i]);
+					}
+				}
+			} catch (IOException e) {
+				String pluginFileString = (pluginFile == null) ? null : pluginFile.getAbsolutePath();
+				UpdateCore.log(Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_ErrorAccessing, (new String[] {pluginFileString})), e));
+			} catch (SAXException e) {
+				String pluginFileString = (pluginFile == null) ? null : pluginFile.getAbsolutePath();
+				UpdateCore.log(Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_ErrorParsingFile, (new String[] {pluginFileString})), e));
+			} finally {
+				if (in != null) {
+					try {
+						in.close();
+					} catch (IOException e) {
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * transform each Plugin and Fragment into an ArchiveReferenceModel
+	 * and a PluginEntry for the Site	 
+	 */
+	// PERF: removed intermediate Plugin object
+	private void addParsedPlugin(PluginEntry entry, File file) throws CoreException {
+
+		String location = null;
+		try {
+			if (entry != null) {
+
+				// create the plugin Entry
+				((Site) site).addPluginEntry(entry);
+
+				// Create the Site mapping ArchiveRef->PluginEntry
+				// the id of the archiveRef is plugins\<pluginid>_<ver>.jar as per the specs
+				// PERF: remove code
+				//SiteFileFactory archiveFactory = new SiteFileFactory();				
+				ArchiveReferenceModel archive = createArchiveReferenceModel();
+				String id = (entry.getVersionedIdentifier().toString());
+				String pluginID = Site.DEFAULT_PLUGIN_PATH + id + FeatureContentProvider.JAR_EXTENSION;
+				archive.setPath(pluginID);
+				location = file.toURL().toExternalForm();
+				archive.setURLString(location);
+				((Site) site).addArchiveReferenceModel(archive);
+
+				// TRACE				
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+					UpdateCore.debug("Added archive to site:" + pluginID + " pointing to: " + location); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+			}
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteFileFactory_UnableToCreateURLForFile, (new String[] {location})), e);
+		}
+	}
+
+	/**
+	 *  
+	 */
+	private void parsePackagedPlugins(File pluginDir) throws CoreException {
+		if (!pluginDir.exists()) {
+			return;
+		}
+		String[] dir = pluginDir.list(FeaturePackagedContentProvider.filter);
+
+		if (dir == null) {
+			throw new EmptyDirectoryException(new Status(IStatus.WARNING, UpdateCore.getPlugin().getBundle().getSymbolicName(), IStatus.OK, pluginDir.getName() + File.separator + pluginDir.getName() + "directory is empty", null)); //$NON-NLS-1$
+		}
+		for (int i = 0; i < dir.length; i++) {
+			ContentReference ref = null;
+			String refString = null;
+			InputStream in = null;
+			JarContentReference jarReference = null;
+			try {
+				File file = new File(pluginDir, dir[i]);
+				jarReference = new JarContentReference(null, file);
+				ref = jarReference.peek("META-INF/MANIFEST.MF", null, null); //$NON-NLS-1$
+				if (ref != null) {
+					in = ref.getInputStream();
+					BundleManifest manifest = new BundleManifest(in);
+					if (manifest.exists()) {
+
+						addParsedPlugin(manifest.getPluginEntry(), file);
+						continue;
+					}
+				}
+				ref = jarReference.peek("plugin.xml", null, null);//$NON-NLS-1$
+				if (ref == null) {
+					ref = jarReference.peek("fragment.xml", null, null); //$NON-NLS-1$
+				}
+				if (ref != null) {
+					in = ref.getInputStream();
+					PluginEntry entry = new DefaultPluginParser().parse(in);
+					addParsedPlugin(entry, file);
+				}
+			} catch (Exception e) {
+				try {
+					refString = (ref == null) ? null : ref.asURL().toExternalForm();
+				} catch (IOException ioe) {
+				}
+
+				String message;
+
+				if (e instanceof IOException) {
+					message = NLS.bind(Messages.SiteFileFactory_ErrorAccessing, (new String[] {refString}));
+				} else if (e instanceof SAXException) {
+					message = NLS.bind(Messages.SiteFileFactory_ErrorParsingFile, (new String[] {refString}));
+				} else {
+					message = NLS.bind(Messages.SiteFileFactory_ErrorAccessing, (new String[] {refString}));
+				}// end if
+
+				// Log exception, but do not throw to caller.
+				// Continue with processing remaining plug-ins
+				UpdateCore.log(message, e);
+			} finally {
+				if (in != null) {
+					try {
+						in.close();
+					} catch (IOException ce) {
+					}
+				}
+				if (jarReference != null) {
+					try {
+						jarReference.closeArchive();
+					} catch (IOException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
+
+	/*
+	 * @see SiteModelFactory#createSiteMapModel()
+	 */
+	public SiteModel createSiteMapModel() {
+		return new SiteFile();
+	}
+
+	/*
+	 * @see SiteModelFactory#canParseSiteType(String)
+	 */
+	public boolean canParseSiteType(String type) {
+		return (super.canParseSiteType(type) || SiteFileContentProvider.SITE_TYPE.equalsIgnoreCase(type));
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileNonPluginContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileNonPluginContentConsumer.java
new file mode 100644
index 0000000..7dbfd58
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFileNonPluginContentConsumer.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+import java.io.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+
+/**
+ * Plugin Content Consumer on a Site
+ */
+public class SiteFileNonPluginContentConsumer extends ContentConsumer {
+
+	private String path;
+	private boolean closed = false;
+
+	/*
+	 * Constructor 
+	 */
+	public SiteFileNonPluginContentConsumer(String featurePath) {
+		this.path = featurePath;
+	}
+
+	/*
+	 * @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor) throws CoreException {
+
+		if (closed) {
+			UpdateCore.warn("Attempt to store in a closed SiteFileNonPluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		InputStream inStream = null;
+		String featurePath = path;
+		String contentKey = contentReference.getIdentifier();
+		featurePath += contentKey;
+		try {
+			inStream = contentReference.getInputStream();
+			UpdateManagerUtils.copyToLocal(inStream, featurePath, null);
+			UpdateManagerUtils.checkPermissions(contentReference, featurePath); // 20305
+		} catch (IOException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.GlobalConsumer_ErrorCreatingFile, (new String[] { featurePath })), e);
+		} finally {
+			if (inStream != null) {
+				try {
+					// close stream
+					inStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+
+	}
+
+	/*
+	 * @see ISiteContentConsumer#close()
+	 */
+	public void close() {
+		if (closed) {
+			UpdateCore.warn("Attempt to close a closed SiteFileNonPluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+		closed = true;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFilePackedPluginContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFilePackedPluginContentConsumer.java
new file mode 100644
index 0000000..bf65225
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFilePackedPluginContentConsumer.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+import java.io.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+
+/**
+ * Plugin Content Consumer on a Site
+ * for a plugin that will run from a jar
+ */
+public class SiteFilePackedPluginContentConsumer extends ContentConsumer {
+
+	private IPluginEntry pluginEntry;
+	private ISite site;
+	private boolean closed = false;
+	private String jarPath;
+	private String tempPath;
+
+	/*
+	 * Constructor
+	 */
+	public SiteFilePackedPluginContentConsumer(IPluginEntry pluginEntry, ISite site) {
+		this.pluginEntry = pluginEntry;
+		this.site = site;
+	}
+
+	/*
+	 * @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor) throws CoreException {
+		InputStream inStream = null;
+
+		if (closed) {
+			UpdateCore.warn("Attempt to store in a closed SiteFilePluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		try {
+			URL newURL = new URL(site.getURL(), Site.DEFAULT_PLUGIN_PATH + pluginEntry.getVersionedIdentifier().toString() + ".jar"); //$NON-NLS-1$
+			inStream = contentReference.getInputStream();
+			jarPath = newURL.getFile().replace(File.separatorChar, '/');
+			File jarFile = new File(jarPath);
+			if (jarFile.exists()) {
+				throw Utilities.newCoreException(NLS.bind(Messages.UpdateManagerUtils_FileAlreadyExists, (new Object[] { jarFile })), null);
+			}
+			// error recovery
+			tempPath= ErrorRecoveryLog.getLocalRandomIdentifier(jarPath+".tmp"); //$NON-NLS-1$
+				ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.BUNDLE_JAR_ENTRY, tempPath);
+			//
+			UpdateManagerUtils.copyToLocal(inStream, tempPath, null);
+		} catch (IOException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.GlobalConsumer_ErrorCreatingFile, (new String[] { tempPath })), e);
+		} finally {
+			if (inStream != null) {
+				try {
+					// close stream
+					inStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+	}
+
+	/*
+	 * @see ISiteContentConsumer#close() 
+	 */
+	public void close() throws CoreException {
+
+		if (closed) {
+			UpdateCore.warn("Attempt to close a closed SiteFilePluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		if (tempPath != null) {
+			// rename file 
+			ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.RENAME_ENTRY, tempPath);
+			File fileToRename = new File(tempPath);
+			boolean sucess = false;
+			if (fileToRename.exists()) {
+				File renamedFile = new File(jarPath);
+				sucess = fileToRename.renameTo(renamedFile);
+			}
+			if (!sucess) {
+				String msg = NLS.bind(Messages.ContentConsumer_UnableToRename, (new String[] { tempPath, jarPath }));
+				throw Utilities.newCoreException(msg, new Exception(msg));
+			}
+		}
+
+		if (site instanceof SiteFile)
+			 ((SiteFile) site).addPluginEntry(pluginEntry);
+		closed = true;
+	}
+
+	/*
+	 * 
+	 */
+	public void abort() throws CoreException {
+
+		if (closed) {
+			UpdateCore.warn("Attempt to abort a closed SiteFilePluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		boolean sucess = true;
+
+		// delete plugin.jar
+		if (jarPath != null) {
+			ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.DELETE_ENTRY, jarPath);
+			File fileToRemove = new File(jarPath);
+
+			if (fileToRemove.exists()) {
+				sucess = fileToRemove.delete();
+			}
+		}
+
+		if (!sucess) {
+			String msg = NLS.bind(Messages.SiteFilePackedPluginContentConsumer_unableToDelete, (new String[] { jarPath })); 
+			UpdateCore.log(msg, null);
+		}
+		closed = true;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFilePluginContentConsumer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFilePluginContentConsumer.java
new file mode 100644
index 0000000..3b3d047
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteFilePluginContentConsumer.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+
+/**
+ * Plugin Content Consumer on a Site
+ */
+public class SiteFilePluginContentConsumer extends ContentConsumer {
+
+	private IPluginEntry pluginEntry;
+	private ISite site;
+	private boolean closed = false;
+
+	// recovery
+	// temporary name to original name map
+	private Map renames = new HashMap(2);
+
+	// for abort
+	private List /*of path as String */
+	installedFiles;
+
+	/*
+	 * Constructor
+	 */
+	public SiteFilePluginContentConsumer(IPluginEntry pluginEntry, ISite site) {
+		this.pluginEntry = pluginEntry;
+		this.site = site;
+		installedFiles = new ArrayList();
+	}
+
+	/*
+	 * @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	public void store(ContentReference contentReference, IProgressMonitor monitor) throws CoreException {
+		InputStream inStream = null;
+		String pluginPath = null;
+
+		if (closed) {
+			UpdateCore.warn("Attempt to store in a closed SiteFilePluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		try {
+			URL newURL = new URL(site.getURL(), Site.DEFAULT_PLUGIN_PATH + pluginEntry.getVersionedIdentifier().toString());
+			pluginPath = newURL.getFile(); 
+			String contentKey = contentReference.getIdentifier();
+			inStream = contentReference.getInputStream();
+			pluginPath += pluginPath.endsWith(File.separator) ? contentKey : File.separator + contentKey;
+
+			// error recovery
+			String logEntry=null;
+			if ("plugin.xml".equals(contentKey)) { //$NON-NLS-1$
+				logEntry=ErrorRecoveryLog.PLUGIN_ENTRY;
+			} else if ("fragment.xml".equals(contentKey)) { //$NON-NLS-1$
+				logEntry=ErrorRecoveryLog.FRAGMENT_ENTRY;
+			} else if ("META-INF/MANIFEST.MF".equals(contentKey)) { //$NON-NLS-1$
+				logEntry=ErrorRecoveryLog.BUNDLE_MANIFEST_ENTRY;
+			}
+			if (logEntry!=null) {
+				String originalName = pluginPath.replace(File.separatorChar, '/');
+				File localFile = new File(originalName);
+				if (localFile.exists()) {
+					throw Utilities.newCoreException(NLS.bind(Messages.UpdateManagerUtils_FileAlreadyExists, (new Object[] { localFile })), null);
+				}
+				pluginPath = ErrorRecoveryLog.getLocalRandomIdentifier(pluginPath);
+				renames.put(pluginPath, originalName);
+				ErrorRecoveryLog.getLog().appendPath(logEntry, pluginPath);
+			}
+			//
+			UpdateManagerUtils.copyToLocal(inStream, pluginPath, null);
+			UpdateManagerUtils.checkPermissions(contentReference, pluginPath); // 20305
+			installedFiles.add(pluginPath);
+		} catch (IOException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.GlobalConsumer_ErrorCreatingFile, (new String[] { pluginPath })), e);
+		} finally {
+			if (inStream != null) {
+				try {
+					// close stream
+					inStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+	}
+
+	/*
+	 * @see ISiteContentConsumer#close() 
+	 */
+	public void close() throws CoreException {
+
+		if (closed) {
+			UpdateCore.warn("Attempt to close a closed SiteFilePluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		for(Iterator it = renames.entrySet().iterator(); it.hasNext();){
+			// rename file 
+			Map.Entry entry = (Map.Entry)it.next();
+			String temporary = (String) entry.getKey();
+			String original = (String) entry.getValue();
+			ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.RENAME_ENTRY, temporary);
+			File fileToRename = new File(temporary);
+			boolean sucess = false;
+			if (fileToRename.exists()) {
+				File renamedFile = new File(original);
+				sucess = fileToRename.renameTo(renamedFile);
+			}
+			if (!sucess) {
+				String msg = NLS.bind(Messages.ContentConsumer_UnableToRename, (new String[] { temporary, original }));
+				throw Utilities.newCoreException(msg, new Exception(msg));
+			}
+		}
+
+		if (site instanceof SiteFile)
+			 ((SiteFile) site).addPluginEntry(pluginEntry);
+		closed = true;
+	}
+
+	/*
+	 * 
+	 */
+	public void abort() throws CoreException {
+
+		if (closed) {
+			UpdateCore.warn("Attempt to abort a closed SiteFilePluginContentConsumer", new Exception()); //$NON-NLS-1$
+			return;
+		}
+
+		boolean success = true;
+		InstallRegistry.unregisterPlugin(pluginEntry);
+
+		// delete plugin manifests first
+		for(Iterator it = renames.values().iterator(); it.hasNext();){
+			String originalName = (String) it.next();
+
+			ErrorRecoveryLog.getLog().appendPath(ErrorRecoveryLog.DELETE_ENTRY, originalName);
+			File fileToRemove = new File(originalName);
+			if (fileToRemove.exists()) {
+				if(!fileToRemove.delete()){
+					String msg = NLS.bind(Messages.SiteFilePluginContentConsumer_unableToDelete, (new String[] { originalName })); 
+					UpdateCore.log(msg, null);	
+					success = false;
+				}
+			}
+		}
+
+		if (success) {
+			// remove the plugin files;
+			Iterator iter = installedFiles.iterator();
+			File featureFile = null;
+			while (iter.hasNext()) {
+				String path = (String) iter.next();
+				featureFile = new File(path);
+				UpdateManagerUtils.removeFromFileSystem(featureFile);
+			}
+
+			// remove the plugin directory if empty
+			try {
+				URL newURL = new URL(site.getURL(), Site.DEFAULT_PLUGIN_PATH + pluginEntry.getVersionedIdentifier().toString());
+				String pluginPath = newURL.getFile();
+				UpdateManagerUtils.removeEmptyDirectoriesFromFileSystem(new File(pluginPath));
+			} catch (MalformedURLException e) {
+				throw Utilities.newCoreException(e.getMessage(), e);
+			}
+		}
+		closed = true;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteReconciler.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteReconciler.java
new file mode 100644
index 0000000..7fdb7ff
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteReconciler.java
@@ -0,0 +1,347 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import java.util.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.ModelObject;
+
+/**
+ * This class manages the reconciliation.
+ */
+
+public class SiteReconciler extends ModelObject {
+
+	private SiteReconciler(LocalSite siteLocal) {
+		//never instantiated
+	}
+
+	/**
+	 * Validate the list of configured features eliminating extra
+	 * entries (if possible). Make sure we do not leave configured
+	 * nested features with "holes" (ie. unconfigured children)
+	 */
+	public static void checkConfiguredFeatures(IConfiguredSite configuredSite) {
+
+		// Note: if we hit errors in the various computation
+		// methods and throw a CoreException, we will not catch it
+		// in this method. Consequently we will not attempt to
+		// unconfigure any "extra" features because we would 
+		// likely get it wrong. The platform will run with extra features
+		// configured. The runtime will eliminate extra plugins based
+		// on runtime binding rules.
+
+		// determine "proposed" list of configured features
+		ConfiguredSite cSite = (ConfiguredSite) configuredSite;
+		// debug
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+			UpdateCore.debug("Validate configuration of site " + cSite.getSite().getURL()); //$NON-NLS-1$
+		}
+		IFeatureReference[] configuredRefs = cSite.getConfiguredFeatures();
+		ArrayList allPossibleConfiguredFeatures = new ArrayList();
+		for (int i = 0; i < configuredRefs.length; i++) {
+			try {
+				IFeature feature = configuredRefs[i].getFeature(null);
+				allPossibleConfiguredFeatures.add(feature);
+				// debug
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+					UpdateCore.debug("   configured feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$
+				}
+			} catch (CoreException e) {
+				UpdateCore.warn("", e); //$NON-NLS-1$
+			}
+		}
+
+		// find top level features
+		ArrayList topFeatures = computeTopFeatures(allPossibleConfiguredFeatures);
+
+		// find non efix top level features
+		ArrayList topNonEfixFeatures = getNonEfixFeatures(topFeatures);
+
+		// expand non efix top level features (compute full nesting structures).
+		ArrayList configuredFeatures = expandFeatures(topNonEfixFeatures, configuredSite);
+
+		// retrieve efixes that patch enable feature
+		// they must be kept enabled
+		if (topFeatures.size() != topNonEfixFeatures.size()) {
+			Map patches = getPatchesAsFeature(allPossibleConfiguredFeatures);
+			if (!patches.isEmpty()) {
+				// calculate efixes to enable
+				List efixesToEnable = getPatchesToEnable(patches, configuredFeatures);
+				// add efies to keep enable
+				//add them to the enable list
+				for (Iterator iter = efixesToEnable.iterator(); iter.hasNext();) {
+					IFeature element = (IFeature) iter.next();
+					ArrayList expandedEfix = new ArrayList();
+					expandEfixFeature(element, expandedEfix, configuredSite);
+					configuredFeatures.addAll(expandedEfix);
+				}
+			}
+		}
+
+		// compute extra features
+		ArrayList extras = diff(allPossibleConfiguredFeatures, configuredFeatures);
+
+		// unconfigure extra features
+		ConfigurationPolicy cPolicy = cSite.getConfigurationPolicy();
+		for (int i = 0; i < extras.size(); i++) {
+			IFeature feature = (IFeature) extras.get(i);
+			IFeatureReference ref = cSite.getSite().getFeatureReference(feature);
+			try {
+				cPolicy.unconfigure(ref, true, false);
+				// debug
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+					UpdateCore.debug("Unconfiguring \"extra\" feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$
+				}
+			} catch (CoreException e) {
+				UpdateCore.warn("", e); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/*
+	 *  
+	 */
+	private static ArrayList computeTopFeatures(ArrayList features) {
+		/* map of Feature by VersionedIdentifier */
+		Map topFeatures = new HashMap(features.size());
+		// start with the features passed in
+		for (Iterator it = features.iterator(); it.hasNext();) {
+			IFeature f = ((IFeature) it.next());
+			topFeatures.put(f.getVersionedIdentifier(), f);
+		}
+		// remove all features that nest in some other feature
+		for (Iterator it = features.iterator(); it.hasNext();) {
+			try {
+				IIncludedFeatureReference[] children = ((IFeature) it.next()).getIncludedFeatureReferences();
+				for (int j = 0; j < children.length; j++) {
+					try {
+						topFeatures.remove(children[j].getVersionedIdentifier());
+					} catch (CoreException e1) {
+						if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS)
+							UpdateCore.warn("", e1); //$NON-NLS-1$
+					}
+				}
+			} catch (CoreException e) {
+				UpdateCore.warn("", e); //$NON-NLS-1$
+			}
+		}
+		ArrayList list = new ArrayList();
+		list.addAll(topFeatures.values());
+		// debug
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+			UpdateCore.debug("Computed top-level features"); //$NON-NLS-1$
+			for (int i = 0; i < topFeatures.size(); i++) {
+				UpdateCore.debug("   " + ((IFeature) list.get(i)).getVersionedIdentifier().toString()); //$NON-NLS-1$
+			}
+		}
+		return list;
+	}
+
+	/*
+	 * 
+	 */
+	private static ArrayList expandFeatures(ArrayList features, IConfiguredSite configuredSite) {
+		ArrayList result = new ArrayList();
+
+		// expand all top level features
+		for (int i = 0; i < features.size(); i++) {
+			expandFeature((IFeature) features.get(i), result, configuredSite);
+		}
+
+		return result;
+	}
+
+	/*
+	 * 
+	 */
+	private static void expandFeature(IFeature feature, ArrayList features, IConfiguredSite configuredSite) {
+
+		// add feature
+		if (!features.contains(feature)) {
+			features.add(feature);
+			// debug
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+				UpdateCore.debug("Retaining configured feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$
+			}
+		}
+
+		// add nested children to the list
+		IIncludedFeatureReference[] children = null;
+		try {
+			children = feature.getIncludedFeatureReferences();
+		} catch (CoreException e) {
+			UpdateCore.warn("", e); //$NON-NLS-1$
+			return;
+		}
+
+		for (int j = 0; j < children.length; j++) {
+			IFeature child = null;
+			try {
+				child = children[j].getFeature(null);
+			} catch (CoreException e) {
+				if (!UpdateManagerUtils.isOptional(children[j]))
+					UpdateCore.warn("", e); //$NON-NLS-1$
+				// 25202 do not return right now, the peer children may be ok
+			}
+			if (child != null)
+				expandFeature(child, features, configuredSite);
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private static ArrayList diff(ArrayList left, ArrayList right) {
+		ArrayList result = new ArrayList();
+
+		// determine difference (left "minus" right)
+		for (int i = 0; i < left.size(); i++) {
+			IFeature feature = (IFeature) left.get(i);
+			if (!right.contains(feature))
+				result.add(feature);
+		}
+		return result;
+	}
+
+	/*
+	 * get the list of enabled patches
+	 */
+	private static Map getPatchesAsFeature(ArrayList allConfiguredFeatures) {
+		// get all efixes and the associated patched features
+		Map patches = new HashMap();
+		if (allConfiguredFeatures != null) {
+			Iterator iter = allConfiguredFeatures.iterator();
+			while (iter.hasNext()) {
+				List patchedFeaturesID = new ArrayList();
+				IFeature element = (IFeature) iter.next();
+				// add the patched feature identifiers
+				for (int i = 0; i < element.getImports().length; i++) {
+					if (element.getImports()[i].isPatch()) {
+						VersionedIdentifier id = element.getImports()[i].getVersionedIdentifier();
+						if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER)
+							UpdateCore.debug("Found patch " + element + " for feature identifier " + id); //$NON-NLS-1$ //$NON-NLS-2$
+						patchedFeaturesID.add(id);
+					}
+				}
+
+				if (!patchedFeaturesID.isEmpty()) {
+					patches.put(element, patchedFeaturesID);
+				}
+			}
+		}
+
+		return patches;
+	}
+
+	/*
+	 * retruns the list of pathes-feature who patch enabled features
+	 */
+	private static List getPatchesToEnable(Map efixes, ArrayList configuredFeatures) {
+
+		ArrayList enabledVersionedIdentifier = new ArrayList();
+		Iterator iter = configuredFeatures.iterator();
+		while (iter.hasNext()) {
+			IFeature element = (IFeature) iter.next();
+			enabledVersionedIdentifier.add(element.getVersionedIdentifier());
+		}
+
+		// loop through the patches
+		List result = new ArrayList();
+		iter = efixes.keySet().iterator();
+		while (iter.hasNext()) {
+			boolean toEnable = false;
+			IFeature efixFeature = (IFeature) iter.next();
+			List patchedFeatures = (List) efixes.get(efixFeature);
+			// loop through the 'patched features identifier' the for this patch
+			// see if it the patch patches at least one enable feature
+			Iterator patchedFeaturesIter = patchedFeatures.iterator();
+			while (patchedFeaturesIter.hasNext() && !toEnable) {
+				VersionedIdentifier patchedFeatureID = (VersionedIdentifier) patchedFeaturesIter.next();
+				if (enabledVersionedIdentifier.contains(patchedFeatureID)) {
+					toEnable = true;
+				}
+			}
+
+			if (!toEnable) {
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER)
+					UpdateCore.debug("The Patch " + efixFeature + " does not patch any enabled features: it will be disabled"); //$NON-NLS-1$ //$NON-NLS-2$
+			} else {
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER)
+					UpdateCore.debug("The patch " + efixFeature + " will be enabled."); //$NON-NLS-1$ //$NON-NLS-2$
+				result.add(efixFeature);
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * returns the feature that are not patches
+	 */
+	private static ArrayList getNonEfixFeatures(ArrayList topFeatures) {
+		Map efixFeatures = getPatchesAsFeature(topFeatures);
+		Set keySet = efixFeatures.keySet();
+		if (keySet == null || keySet.isEmpty())
+			return topFeatures;
+
+		Iterator iter = topFeatures.iterator();
+		ArrayList result = new ArrayList();
+		while (iter.hasNext()) {
+			IFeature element = (IFeature) iter.next();
+			if (!keySet.contains(element)) {
+				result.add(element);
+			}
+		}
+		return result;
+	}
+
+	/*
+	 * only enable non-efix children recursively
+	 */
+	private static void expandEfixFeature(IFeature feature, ArrayList features, IConfiguredSite configuredSite) {
+
+		// add feature
+		if (!features.contains(feature)) {
+			features.add(feature);
+			// debug
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_RECONCILER) {
+				UpdateCore.debug("Retaining configured feature " + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$
+			}
+		}
+
+		// add nested children to the list
+		IIncludedFeatureReference[] children = null;
+		try {
+			children = feature.getIncludedFeatureReferences();
+		} catch (CoreException e) {
+			UpdateCore.warn("", e); //$NON-NLS-1$
+			return;
+		}
+
+		for (int j = 0; j < children.length; j++) {
+			IFeature child = null;
+			try {
+				child = children[j].getFeature(null);
+			} catch (CoreException e) {
+				if (!children[j].isOptional())
+					UpdateCore.warn("", e); //$NON-NLS-1$
+				// 25202 do not return right now, the peer children may be ok
+			}
+			if (child != null) {
+				if (!UpdateCore.isPatch(child))
+					expandEfixFeature(child, features, configuredSite);
+			}
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteStatusAnalyzer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteStatusAnalyzer.java
new file mode 100644
index 0000000..fcdbb1d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteStatusAnalyzer.java
@@ -0,0 +1,463 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 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.update.internal.core;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.IImport;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * This class manages the configurations.
+ */
+
+public class SiteStatusAnalyzer {
+
+	private static final String SOURCE_BUNDLES_PATH = "org.eclipse.equinox.source/source.info"; //$NON-NLS-1$
+	private static final String ID = "org.eclipse.update.core"; //$NON-NLS-1$
+	private static List allConfiguredFeatures; /*VersionedIdentifier */
+	private LocalSite siteLocal;
+
+	// A list of versionedIdentifiers for source bundles; initialized on demand.
+	private List sourceBundles = null;
+
+	/**
+	 * 
+	 */
+	public SiteStatusAnalyzer(LocalSite siteLocal) {
+		this.siteLocal = siteLocal;
+	}
+
+	/*
+	 *  check if the Plugins of the feature are on the plugin path
+	 *  If all the plugins are on the plugin path, and the version match and there is no other version -> HAPPY
+	 *  If all the plugins are on the plugin path, and the version match and there is other version -> AMBIGUOUS
+	 *  If some of the plugins are on the plugin path, but not all -> UNHAPPY
+	 * 	Check on all ConfiguredSites
+	 */
+	private IStatus getStatus(IFeature feature) {
+
+		// validate site
+		ISite featureSite = feature.getSite();
+		if (featureSite == null) {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+				UpdateCore.debug("Cannot determine status of feature:" + feature.getLabel() + ". Site is NULL."); //$NON-NLS-1$ //$NON-NLS-2$
+			String msg = NLS.bind(Messages.SiteLocal_UnableToDetermineFeatureStatusSiteNull, (new Object[] {feature.getURL()}));
+			return createStatus(IStatus.ERROR, IFeature.STATUS_AMBIGUOUS, msg, null);
+		}
+
+		// validate configured site		
+		ConfiguredSite cSite = (ConfiguredSite) featureSite.getCurrentConfiguredSite();
+		if (cSite == null) {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+				UpdateCore.warn("Cannot determine status of feature: " + feature.getLabel() + ". Configured Site is NULL."); //$NON-NLS-1$ //$NON-NLS-2$
+			String msg = NLS.bind(Messages.SiteLocal_UnableToDetermineFeatureStatusConfiguredSiteNull, (new Object[] {feature.getURL()}));
+			return createStatus(IStatus.ERROR, IFeature.STATUS_AMBIGUOUS, msg, null);
+		}
+
+		// check if disable, if so return
+		IFeatureReference ref = cSite.getSite().getFeatureReference(feature);
+		if (ref != null) {
+			if (!cSite.getConfigurationPolicy().isConfigured(ref))
+				return createStatus(IStatus.OK, IFeature.STATUS_DISABLED, "", null); //$NON-NLS-1$
+		} else {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+				UpdateCore.warn("Unable to find reference for feature " + feature + " in site " + cSite.getSite().getURL()); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		// check if broken
+		IStatus status = cSite.getBrokenStatus(feature);
+		if (status.getSeverity() != IStatus.OK) {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
+				UpdateCore.debug("Feature broken:" + feature.getLabel() + ".Site:" + cSite.toString()); //$NON-NLS-1$ //$NON-NLS-2$
+			return status;
+		}
+
+		// check ambiguous against registry [17015]
+		IPluginEntry[] featuresEntries = feature.getPluginEntries();
+		return status(feature, featuresEntries);
+	}
+
+	/*
+	 *  check if the Plugins of the feature are on the plugin path
+	 *  If all the plugins are on the plugin path, and the version match and there is no other version -> HAPPY
+	 *  If all the plugins are on the plugin path, and the version match and there is other version -> AMBIGUOUS
+	 *  If some of the plugins are on the plugin path, but not all -> UNHAPPY
+	 * 	Check on all ConfiguredSites
+	 */
+	public IStatus getFeatureStatus(IFeature feature) throws CoreException {
+
+		IFeature childFeature = null;
+		IStatus childStatus;
+
+		IFeatureReference[] children = feature.getIncludedFeatureReferences();
+
+		// consider disable
+		// check the current feature
+		String msg = Messages.SiteLocal_FeatureDisable;
+		int code = IFeature.STATUS_DISABLED;
+		IStatus featureStatus = getStatus(feature);
+		MultiStatus multiTemp = new MultiStatus(featureStatus.getPlugin(), code, msg, null);
+		if (featureStatus.getSeverity() == IStatus.ERROR) {
+			if (featureStatus.isMultiStatus()) {
+				multiTemp.addAll(featureStatus);
+			} else {
+				multiTemp.add(featureStatus);
+			}
+		}
+		// preserve the worse code through the method (self assesment + children assessment)
+		if (featureStatus.getCode() > code)
+			code = featureStatus.getCode();
+
+		// do not check children if feature is disable
+		if (!(code == IFeature.STATUS_DISABLED)) {
+			for (int i = 0; i < children.length; i++) {
+				if (!UpdateManagerUtils.isOptional(children[i])) {
+					try {
+						childFeature = children[i].getFeature(null);
+					} catch (CoreException e) {
+						childFeature = null;
+						if (!UpdateManagerUtils.isOptional(children[i]))
+							UpdateCore.warn("Error retrieving feature:" + children[i]); //$NON-NLS-1$
+					}
+
+					if (childFeature == null) {
+						UpdateCore.warn("getFeatureStatus: Feature is null for:" + children[i]); //$NON-NLS-1$
+						// Unable to find children feature, broken
+						Object featureAsPrintableObject = children[i].getURL();
+						featureAsPrintableObject = children[i].getVersionedIdentifier();
+						String msg1 = NLS.bind(Messages.SiteLocal_NestedFeatureUnavailable, (new Object[] {featureAsPrintableObject}));
+						multiTemp.add(createStatus(IStatus.ERROR, IFeature.STATUS_UNHAPPY, msg1, null));
+						if (IFeature.STATUS_UNHAPPY > code)
+							code = IFeature.STATUS_UNHAPPY;
+					} else {
+						childStatus = getFeatureStatus(childFeature);
+						// do not add the status, add the children status as getFeatureStatus
+						// returns a multiStatus 
+						if (childStatus.getCode() == IFeature.STATUS_DISABLED) {
+							VersionedIdentifier versionID = childFeature.getVersionedIdentifier();
+							String featureVer = (versionID == null) ? "" : versionID.getVersion().toString(); //$NON-NLS-1$
+							String msg1 = NLS.bind(Messages.SiteLocal_NestedFeatureDisable, (new String[] {childFeature.getLabel(), featureVer}));
+							multiTemp.add(createStatus(IStatus.ERROR, childStatus.getCode(), msg1, null));
+							if (IFeature.STATUS_UNHAPPY > code)
+								code = IFeature.STATUS_UNHAPPY;
+						}
+						if (childStatus.getSeverity() != IStatus.OK) {
+							VersionedIdentifier versionID = childFeature.getVersionedIdentifier();
+							String featureVer = (versionID == null) ? "" : versionID.getVersion().toString(); //$NON-NLS-1$
+							String msg1 = NLS.bind(Messages.SiteLocal_NestedFeatureUnHappy, (new String[] {childFeature.getLabel(), featureVer}));
+							multiTemp.add(createStatus(IStatus.ERROR, childStatus.getCode(), msg1, null));
+							if (childStatus.getCode() > code)
+								code = childStatus.getCode();
+						}
+					}
+				}
+			}
+		}
+
+		// set message
+		switch (code) {
+			case IFeature.STATUS_HAPPY :
+				msg = Messages.SiteLocal_FeatureHappy;
+				break;
+			case IFeature.STATUS_UNHAPPY :
+				msg = Messages.SiteLocal_FeatureUnHappy;
+				break;
+			case IFeature.STATUS_AMBIGUOUS :
+				msg = Messages.SiteLocal_FeatureAmbiguous;
+				break;
+			case IFeature.STATUS_DISABLED :
+				msg = Messages.SiteLocal_FeatureDisable;
+				break;
+			default :
+				msg = Messages.SiteLocal_FeatureStatusUnknown;
+				break;
+		}
+		MultiStatus multi = new MultiStatus(featureStatus.getPlugin(), code, msg, null);
+		multi.addAll(multiTemp);
+		return multi;
+	}
+
+	/*
+	 * compute the status based on getStatus() rules 
+	 */
+	private IStatus status(IFeature pluginsOriginatorFeature, IPluginEntry[] featurePlugins) {
+		VersionedIdentifier featurePluginID;
+
+		String happyMSG = Messages.SiteLocal_FeatureHappy;
+		String ambiguousMSG = Messages.SiteLocal_FeatureAmbiguous;
+		IStatus featureStatus = createStatus(IStatus.OK, IFeature.STATUS_HAPPY, "", null); //$NON-NLS-1$
+		MultiStatus multi = new MultiStatus(featureStatus.getPlugin(), IFeature.STATUS_AMBIGUOUS, ambiguousMSG, null);
+		PackageAdmin pkgAdmin = UpdateCore.getPlugin().getPackageAdmin();
+
+		// is Ambigous if we find a plugin from the feature
+		// with a different version and not the one we are looking
+		for (int i = 0; i < featurePlugins.length; i++) {
+			MultiStatus tempmulti = new MultiStatus(featureStatus.getPlugin(), IFeature.STATUS_AMBIGUOUS, ambiguousMSG, null);
+			featurePluginID = featurePlugins[i].getVersionedIdentifier();
+			boolean found = false;
+
+			String singleVersionRange = '[' + featurePluginID.getVersion().toString() + ',' + featurePluginID.getVersion().toString() + ']';
+			Bundle[] bundles = pkgAdmin.getBundles(featurePluginID.getIdentifier(), singleVersionRange);
+			if (bundles != null && bundles.length == 1) {
+				found = true;
+				continue;
+			}
+
+			// Check if there is another feature with this plugin (but different version)
+			// log it
+			bundles = pkgAdmin.getBundles(featurePluginID.getIdentifier(), null);
+			for (int j = 0; bundles != null && j < bundles.length && !found; j++) {
+				String bundleVersion = (String) bundles[j].getHeaders().get(Constants.BUNDLE_VERSION);
+				IFeature feature = getFeatureForId(new VersionedIdentifier(bundles[j].getSymbolicName(), bundleVersion));
+				if ((feature != null) && (!isFeaturePatchOfThisFeature(pluginsOriginatorFeature, feature))) {
+					String msg = null;
+					String label = feature.getLabel();
+					String featureVersion = feature.getVersionedIdentifier().getVersion().toString();
+					Object[] values = new Object[] {bundles[j].getSymbolicName(), featurePluginID.getVersion(), bundleVersion, label, featureVersion};
+					msg = NLS.bind(Messages.SiteLocal_TwoVersionSamePlugin2, values);
+					UpdateCore.warn("Found another version of the same plugin on the path:" + bundles[j].getSymbolicName() + " " + bundleVersion); //$NON-NLS-1$ //$NON-NLS-2$
+					tempmulti.add(createStatus(IStatus.ERROR, IFeature.STATUS_AMBIGUOUS, msg, null));
+				} else {
+					found = true;
+				}
+
+			}
+
+			// check whether the plugin is a source bundle
+			// that has not been configured into the runtime
+			if (!found) {
+				loadSourceBundlesList();
+				for (Iterator iter = sourceBundles.iterator(); iter.hasNext();) {
+					VersionedIdentifier nextId = (VersionedIdentifier) iter.next();
+					if (featurePluginID.equals(nextId)) {
+						found = true;
+						break;
+					}
+				}
+			}
+
+			// if we haven't found the exact plugin, add the children
+			// of tempMulti (i,e the other we found) 
+			// if we have no children, we have a problem as a required plugin is not there at all
+			if (!found) {
+				if (tempmulti.getChildren().length > 0) {
+					multi.addAll(tempmulti);
+				} else {
+					if (multi.getCode() != IFeature.STATUS_UNHAPPY) {
+						String unhappyMSG = Messages.SiteLocal_FeatureUnHappy;
+						MultiStatus newMulti = new MultiStatus(featureStatus.getPlugin(), IFeature.STATUS_UNHAPPY, unhappyMSG, null);
+						newMulti.addAll(multi);
+						multi = newMulti;
+					}
+					String msg = NLS.bind(Messages.SiteLocal_NoPluginVersion, (new String[] {featurePluginID.getIdentifier()}));
+					multi.add(createStatus(IStatus.ERROR, IFeature.STATUS_UNHAPPY, msg, null));
+				}
+			}
+		}
+
+		if (!multi.isOK())
+			return multi;
+
+		// we return happy as we consider the isBroken verification has been done
+		return createStatus(IStatus.OK, IFeature.STATUS_HAPPY, happyMSG, null);
+	}
+
+	public static File toFile(URL url) {
+		try {
+			if (!"file".equalsIgnoreCase(url.getProtocol())) //$NON-NLS-1$
+				return null;
+			//assume all illegal characters have been properly encoded, so use URI class to unencode
+			return new File(new URI(url.toExternalForm()));
+		} catch (Exception e) {
+			//URL contains unencoded characters
+			return new File(url.getFile());
+		}
+	}
+
+	/**
+	 * 	Get the contents of the source bundles text file.
+	 */
+	private void loadSourceBundlesList() {
+		if (sourceBundles != null)
+			return;
+
+		sourceBundles = new ArrayList(32);
+		IPlatformConfiguration config = ConfiguratorUtils.getCurrentPlatformConfiguration();
+		URL configLocation = config.getConfigurationLocation();
+		if (configLocation == null)
+			return;
+		// Drop off /org.eclipse.update/platform.xml
+		File configDir = toFile(configLocation);
+		configDir = configDir.getParentFile();
+		if (configDir == null)
+			return;
+		configDir = configDir.getParentFile();
+		if (configDir == null)
+			return;
+		File sourceBundlesFile = new File(configDir, SOURCE_BUNDLES_PATH);
+
+		try {
+			BufferedReader reader = new BufferedReader(new FileReader(sourceBundlesFile));
+			String line;
+			try {
+				while ((line = reader.readLine()) != null) {
+					if (line.startsWith("#"))
+						continue;
+					line = line.trim();// symbolicName,version,other ignored stuff
+					if (line.length() == 0)
+						continue;
+
+					StringTokenizer tok = new StringTokenizer(line, ",", true);
+					String symbolicName = tok.nextToken();
+					if (symbolicName.equals(","))
+						continue;
+					else
+						tok.nextToken(); // ,
+
+					String version = tok.nextToken();
+					if (version.equals(","))
+						continue;
+					else
+						tok.nextToken(); // ,
+
+					VersionedIdentifier sourceId = new VersionedIdentifier(symbolicName, version);
+					sourceBundles.add(sourceId);
+				}
+			} finally {
+				try {
+					reader.close();
+				} catch (IOException ex) {
+					// ignore
+				}
+			}
+		} catch (MalformedURLException e) {
+			UpdateCore.log(new Status(IStatus.ERROR, ID, "Error occurred while reading source bundle list.", e)); //$NON-NLS-1$
+		} catch (IOException e) {
+			UpdateCore.log(new Status(IStatus.ERROR, ID, "Error occurred while reading source bundle list.", e)); //$NON-NLS-1$
+		}
+	}
+
+	private boolean isFeaturePatchOfThisFeature(IFeature pluginsOriginatorFeature, IFeature feature) {
+
+		if (!feature.isPatch())
+			return false;
+
+		IImport[] featureImports = feature.getImports();
+
+		if (featureImports == null) {
+			return false;
+		}
+
+		for (int i = 0; i < featureImports.length; i++) {
+			if (featureImports[i].isPatch() && featureImports[i].getVersionedIdentifier().equals(pluginsOriginatorFeature.getVersionedIdentifier())) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/*
+	 * creates a Status
+	 */
+	private IStatus createStatus(int statusSeverity, int statusCode, String msg, Exception e) {
+		String id = UpdateCore.getPlugin().getBundle().getSymbolicName();
+
+		StringBuffer completeString = new StringBuffer(""); //$NON-NLS-1$
+		if (msg != null)
+			completeString.append(msg);
+		if (e != null) {
+			completeString.append("\r\n["); //$NON-NLS-1$
+			completeString.append(e.toString());
+			completeString.append("]\r\n"); //$NON-NLS-1$
+		}
+		return new Status(statusSeverity, id, statusCode, completeString.toString(), e);
+	}
+
+	/*
+	 * returns all the configured fetaures
+	 */
+	private IFeature[] getAllConfiguredFeatures() {
+		if (allConfiguredFeatures == null) {
+
+			allConfiguredFeatures = new ArrayList();
+			IConfiguredSite[] allConfiguredSites = siteLocal.getCurrentConfiguration().getConfiguredSites();
+
+			for (int i = 0; i < allConfiguredSites.length; i++) {
+				IFeatureReference[] refs = allConfiguredSites[i].getConfiguredFeatures();
+				IFeature feature = null;
+				for (int j = 0; j < refs.length; j++) {
+					feature = null;
+					try {
+						feature = refs[j].getFeature(null);
+					} catch (CoreException e) {
+					}
+					if (feature != null) {
+						allConfiguredFeatures.add(feature);
+					}
+				}
+			}
+		}
+
+		IFeature[] features = new IFeature[allConfiguredFeatures.size()];
+		if (allConfiguredFeatures.size() > 0) {
+			allConfiguredFeatures.toArray(features);
+		}
+		return features;
+	}
+
+	/*
+	 * returns the Feature that declares this versionedIdentifier or null if none found
+	 */
+	private IFeature getFeatureForId(VersionedIdentifier id) {
+
+		if (id == null)
+			return null;
+
+		IFeature[] allFeatures = getAllConfiguredFeatures();
+		IFeature currentFeature = null;
+		IPluginEntry[] allPlugins = null;
+		IPluginEntry currentPlugin = null;
+		for (int i = 0; i < allFeatures.length; i++) {
+			currentFeature = allFeatures[i];
+			allPlugins = currentFeature.getPluginEntries();
+			for (int j = 0; j < allPlugins.length; j++) {
+				currentPlugin = allPlugins[j];
+				if (id.equals(currentPlugin.getVersionedIdentifier()))
+					return currentFeature;
+			}
+		}
+		return null;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteTypeFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteTypeFactory.java
new file mode 100644
index 0000000..5526f76
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteTypeFactory.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+
+/**
+ * 
+ */
+public final class SiteTypeFactory {
+	
+
+	/**
+	 * extension point ID
+	 */
+	public static final String SIMPLE_EXTENSION_ID = "siteTypes";	 //$NON-NLS-1$
+	
+
+	private static SiteTypeFactory inst;
+	
+	private Map factories;
+
+	/**
+	 * hide ctr 
+	 */
+	private SiteTypeFactory() {
+	}
+
+	public static SiteTypeFactory getInstance() {
+		if (inst == null)
+			inst = new SiteTypeFactory();
+		return inst;
+	}
+
+
+	/**
+	 * return the factory for the type
+	 */
+	public ISiteFactory getFactory(String type) throws CoreException {
+			//
+			Object instance = getFactories().get(type);
+			if (instance==null) {
+				instance = createFactoryFor(type);
+				getFactories().put(type,instance);
+			}
+			return (ISiteFactory) instance;
+	}
+
+	/**
+	 * 
+	 */
+	private ISiteFactory createFactoryFor(String type) throws CoreException {
+		ISiteFactory result = null;
+		
+		String pluginID = UpdateCore.getPlugin().getBundle().getSymbolicName();
+		IExtensionRegistry registry = Platform.getExtensionRegistry();
+		IConfigurationElement[] elements = registry.getConfigurationElementsFor(pluginID,SIMPLE_EXTENSION_ID,type);
+		if (elements==null || elements.length==0){
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteTypeFactory_UnableToFindSiteFactory, (new String[] { type })),null);
+		} else {
+			IConfigurationElement element = elements[0];
+			result = (ISiteFactory)element.createExecutableExtension("class"); //$NON-NLS-1$
+		}
+		return result;
+	}
+
+	/**
+	 * Gets the actories.
+	 * @return Returns a Map
+	 */
+	private Map getFactories() {
+		if (factories==null) factories = new HashMap();
+			return factories;
+	}
+
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteURLContentProvider.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteURLContentProvider.java
new file mode 100644
index 0000000..a6070c9
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteURLContentProvider.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.net.URL;
+
+import org.eclipse.update.core.SiteContentProvider;
+
+/**
+ * 
+ */
+public class SiteURLContentProvider extends SiteContentProvider {
+	
+	public static final String SITE_TYPE = "org.eclipse.update.core.http"; //$NON-NLS-1$
+
+	/**
+	 * Constructor for HTTPSite
+	 */
+	public SiteURLContentProvider(URL url) {
+		super(url);
+	}
+}
+
+
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteURLFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteURLFactory.java
new file mode 100644
index 0000000..0540a85
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/SiteURLFactory.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.BaseSiteFactory;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFactoryExtension;
+import org.eclipse.update.core.Site;
+import org.eclipse.update.core.SiteFeatureReferenceModel;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.model.InvalidSiteTypeException;
+import org.eclipse.update.core.model.SiteModelFactory;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+import org.eclipse.update.internal.core.connection.IResponse;
+import org.eclipse.update.internal.model.SiteWithTimestamp;
+
+/**
+ * An update site factory.
+ *
+ */
+public class SiteURLFactory extends BaseSiteFactory implements ISiteFactoryExtension {
+	
+	/*
+	 * For backward compatibility.
+	 */
+	public ISite createSite(URL url) throws CoreException, InvalidSiteTypeException {
+		return createSite(url, null);
+	}
+	/*
+	 * @see ISiteFactory#createSite(URL, boolean)
+	 * 
+	 * the URL can be of the following form
+	 * 1 protocol://...../
+	 * 2 protocol://.....
+	 * 3 protocol://..../site.xml
+	 * 
+	 * 1 If the file of the file of teh url ends with '/', attempt to open the stream.
+	 * if it fails, add site.xml and attempt to open the stream
+	 * 
+	 * 2 attempt to open the stream
+	 * 	fail
+	 * 		add '/site.xml' and attempt to open the stream
+	 * 	sucess
+	 * 		attempt to parse, if it fails, add '/site.xml' and attempt to open teh stream
+	 * 
+	 * 3 open the stream	 
+	 */
+	public ISite createSite(URL url, IProgressMonitor monitor) throws CoreException, InvalidSiteTypeException {
+		Site site = null;
+		InputStream siteStream = null;
+	
+		try {
+			SiteURLContentProvider contentProvider = new SiteURLContentProvider(url);
+	
+			URL resolvedURL = URLEncoder.encode(url);
+			IResponse response = ConnectionFactory.get(resolvedURL);
+			UpdateManagerUtils.checkConnectionResult(response, resolvedURL);
+			siteStream = response.getInputStream(monitor);
+			// the stream can be null if the user cancels the connection
+			if (siteStream==null) return null;
+
+			SiteModelFactory factory = this;
+			site = (Site) factory.parseSite(siteStream);
+			//System.out.println(site.getClass().getCanonicalName());
+			site.setSiteContentProvider(contentProvider);
+			contentProvider.setSite(site);
+			site.resolve(url, url);
+			site.markReadOnly();
+			/*SiteWithTimestamp siteWithTimestamp = new SiteWithTimestamp(site);
+			siteWithTimestamp.setTimestamp( new Date(response.getLastModified()));
+			site = siteWithTimestamp;*/
+			((SiteWithTimestamp)site).setTimestamp( new Date(response.getLastModified()));
+		} catch (MalformedURLException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteURLFactory_UnableToCreateURL, (new String[] { url == null ? "" : url.toExternalForm() })), e); //$NON-NLS-1$
+		} catch (IOException e) {
+			throw Utilities.newCoreException(NLS.bind(Messages.SiteURLFactory_UnableToAccessSiteStream, (new String[] { url == null ? "" : url.toExternalForm() })), ISite.SITE_ACCESS_EXCEPTION, e);	//$NON-NLS-1$
+		} finally {
+			if (siteStream != null) {
+				try {
+					siteStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+		return site;
+	}
+
+	/*
+	 * @see SiteModelFactory#canParseSiteType(String)
+	 */
+	public boolean canParseSiteType(String type) {
+		return (super.canParseSiteType(type) || SiteURLContentProvider.SITE_TYPE.equalsIgnoreCase(type));
+	}
+    /* (non-Javadoc)
+     * @see org.eclipse.update.core.BaseSiteFactory#createFeatureReferenceModel()
+     */
+    public SiteFeatureReferenceModel createFeatureReferenceModel() {
+        return new UpdateSiteFeatureReference();
+    }
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/TargetFeature.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/TargetFeature.java
new file mode 100644
index 0000000..6c789f1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/TargetFeature.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.Feature;
+import org.eclipse.update.core.IFeatureContentConsumer;
+import org.eclipse.update.core.Utilities;
+
+/**
+ * 
+ */
+public class TargetFeature extends Feature {
+
+
+	/**
+	 * The content consumer of the DefaultFeature
+	 */
+	private IFeatureContentConsumer contentConsumer;
+	
+
+	/**
+	 * Constructor for TargetFeature.
+	 */
+	public TargetFeature() {
+		super();
+	}
+
+	/**
+	 * Sets the content Consumer
+	 */
+	public void setContentConsumer(IFeatureContentConsumer contentConsumer) {
+		this.contentConsumer = contentConsumer;
+		contentConsumer.setFeature(this);
+	}
+
+	/*
+	 * @see IFeature#getFeatureContentConsumer()
+	 */
+	public IFeatureContentConsumer getFeatureContentConsumer() throws CoreException {
+		if (this.contentConsumer == null) {
+			throw Utilities.newCoreException( NLS.bind(Messages.Feature_NoFeatureContentConsumer, (new String[] { getURL().toExternalForm() })), null);
+		}
+		return contentConsumer;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/URLEncoder.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/URLEncoder.java
new file mode 100644
index 0000000..3200a9f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/URLEncoder.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ *     James D Miles (IBM Corp.) - bug 191368, Policy URL doesn't support UTF-8 characters
+ *******************************************************************************/
+package org.eclipse.update.internal.core;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Assert;
+/**
+ * Encodes a <code>URL</code> into an <code>ASCII</code> readable
+ * <code>URL</code> that is safe for transport. Encoded
+ * <code>URL</code>s can be decoded using the <code>URLDecoder</code>.
+ *
+ * @see URLDecoder
+ */
+public final class URLEncoder {
+	/**
+	 * Prevents instances from being created.
+	 */
+	private URLEncoder() {
+	}
+	/**
+	 * Encodes the given file and reference parts of a <code>URL</code> into
+	 * an <code>ASCII</code> readable <code>String</code> that is safe for
+	 * transport. Returns the result.
+	 *
+	 * @return the result of encoding the given file and reference parts of
+	 *         a <code>URL</code> into an <code>ASCII</code> readable
+	 *         <code>String</code> that is safe for transport
+	 */
+	public static String encode(String file, String query, String ref) {
+		StringBuffer buf = new StringBuffer();
+		StringTokenizer tokenizer = new StringTokenizer(file, "/", true); //$NON-NLS-1$
+
+		while (tokenizer.hasMoreTokens()) {
+			String token = tokenizer.nextToken();
+			if (token.equals("/")) { //$NON-NLS-1$
+				buf.append(token);
+			} else {
+				buf.append(encodeSegment(token));
+			}
+		}
+
+		if (query != null){
+			buf.append('?');
+			buf.append(query);
+		}
+
+		if (ref != null) {
+			buf.append('#');
+			buf.append(encodeSegment(ref));
+		}
+
+		return buf.toString();
+	}
+	/**
+	 * Encodes the given <code>URL</code> into an <code>ASCII</code>
+	 * readable <code>URL</code> that is safe for transport. Returns the
+	 * result.
+	 *
+	 * @return the result of encoding the given <code>URL</code> into an
+	 *         <code>ASCII</code> readable <code>URL</code> that is safe for
+	 *         transport
+	 */
+	public static URL encode(URL url) throws MalformedURLException {
+		// encode the path not the file as the URL may contain a query
+		String file = url.getPath();
+		String query = url.getQuery();
+		String ref = url.getRef();
+		String auth = url.getAuthority();
+		String host = url.getHost();
+		int port = url.getPort();
+		String userinfo = url.getUserInfo();
+
+		// do not encode if there is an authority, such as in
+		// ftp://user:password@host:port/path 
+		// because the URL constructor does not allow it
+		URL result = url;
+		if (auth == null || auth.equals("") || userinfo == null) // $NON-NLS-1$ $NON-NLS-2$  //$NON-NLS-1$
+			result =  new URL(url.getProtocol(), host, port, encode(file, query, ref));
+		return result;
+	}
+	private static String encodeSegment(String segment) {
+		
+		// if we find a '%' in the string, consider the URL to be already encoded
+		if (segment.indexOf('%')!=-1) return segment;
+		
+		StringBuffer result = new StringBuffer(segment.length());
+
+		for (int i = 0; i < segment.length(); ++i) {
+			char c = segment.charAt(i);
+			if (mustEncode(c)) {
+				byte[] bytes = null;
+				try {
+					bytes = new Character(c).toString().getBytes("UTF8"); //$NON-NLS-1$
+				} catch (UnsupportedEncodingException e) {
+					Assert.isTrue(false, e.getMessage());
+				}
+				for (int j = 0; j < bytes.length; ++j) {
+					result.append('%');
+					result.append(Integer.toHexString((bytes[j] >> 4) & 0x0F));
+					result.append(Integer.toHexString(bytes[j] & 0x0F));
+				}
+			} else {
+				result.append(c);
+			}
+		}
+
+		return result.toString();
+	}
+	
+	private static boolean mustEncode(char c) {
+		if (c >= 'a' && c <= 'z') {
+			return false;
+		}
+
+		if (c >= 'A' && c <= 'Z') {
+			return false;
+		}
+
+		if (c >= '0' && c <= '9') {
+			return false;
+		}
+
+		if (c >= '\'' && c <= '.') {
+			return false;
+		}
+
+		if (c == '!' || c == '$' || c == '_' || c == '[' || c == ']') {
+			return false;
+		}
+
+		// needed otherwise file:///c:/file/ becomes file:///C%3a/file/
+		if (c ==':'){
+			return false;
+		}
+		return true;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/URLKey.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/URLKey.java
new file mode 100644
index 0000000..75c76ad
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/URLKey.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+ 
+import java.net.*;
+
+
+
+/**
+ * 
+ * 
+ */
+public class URLKey {
+
+	private URL url;
+	
+	/**
+	 * Constructor for URLKey.
+	 */
+	public URLKey(URL url) {
+		super();
+		this.url = url;
+	}
+
+	/**
+	 * @see java.lang.Object#equals(Object)
+	 */
+	public boolean equals(Object obj) {
+			if (obj == null) {
+				return false;
+			}
+
+			if (this == obj) {
+				return true;
+			}
+
+			if (obj instanceof URLKey) {
+				return equals(((URLKey) obj).getURL());
+			}
+
+			if (!(obj instanceof URL)) {
+				return false;
+			}
+
+			URL url2 = (URL)obj;
+			if (url == url2) {
+				return true;
+			}
+
+			return UpdateManagerUtils.sameURL(url,url2);
+	}
+
+	/**
+	 * @see java.lang.Object#hashCode()
+	 */
+	public int hashCode() {
+		return url.hashCode();
+	}
+
+	/**
+	 * Returns the url.
+	 * @return URL
+	 */
+	public URL getURL() {
+		return url;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateCore.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateCore.java
new file mode 100644
index 0000000..0707949
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateCore.java
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.signedcontent.SignedContentFactory;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.connection.ConnectionThreadManagerFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public class UpdateCore extends Plugin {
+
+	// debug options
+	public static boolean DEBUG;
+	public static boolean DEBUG_SHOW_INSTALL;
+	public static boolean DEBUG_SHOW_PARSING;
+	public static boolean DEBUG_SHOW_WARNINGS;
+	public static boolean DEBUG_SHOW_CONFIGURATION;
+	public static boolean DEBUG_SHOW_TYPE;
+	public static boolean DEBUG_SHOW_WEB;
+	public static boolean DEBUG_SHOW_IHANDLER;
+	public static boolean DEBUG_SHOW_RECONCILER;
+		
+	private static final String PREFIX = "org.eclipse.update.core"; //$NON-NLS-1$
+	public static final String P_HISTORY_SIZE = PREFIX + ".historySize"; //$NON-NLS-1$
+	public static final String P_CHECK_SIGNATURE = PREFIX + ".checkSignature"; //$NON-NLS-1$
+	public static final String P_AUTOMATICALLY_CHOOSE_MIRROR =  PREFIX + ".automaticallyChooseMirror"; //$NON-NLS-1$
+	public static final String P_UPDATE_VERSIONS = PREFIX + ".updateVersions"; //$NON-NLS-1$
+	public static final String EQUIVALENT_VALUE = "equivalent"; //$NON-NLS-1$
+	public static final String COMPATIBLE_VALUE = "compatible"; //$NON-NLS-1$
+	
+	public static int DEFAULT_HISTORY = 100;//Integer.MAX_VALUE;
+	
+	//The shared instance.
+	private static UpdateCore plugin;
+
+	//log
+	private static UpdateManagerLogWriter log;
+	private static final String LOG_FILE="install.log"; //$NON-NLS-1$
+
+	// bundle data
+	private BundleContext context;
+	private ServiceTracker pkgAdminTracker;
+	private ServiceTracker verifierFactoryTracker;
+	private ServiceTracker proxyTracker;
+	
+	// Session
+	private UpdateSession updateSession = null;
+	/**
+	 * HTTP response code indicating success.
+	 */
+	public static final int HTTP_OK = 200;
+	
+	/**
+	 * The constructor.
+	 */
+	public UpdateCore() {
+		plugin = this;
+	}
+	
+
+	/**
+	 * Returns the shared instance.
+	 */
+	public static UpdateCore getPlugin() {
+		return plugin;
+	}
+	
+
+	private boolean getBooleanDebugOption(String flag, boolean dflt) {
+		String result = Platform.getDebugOption(flag);
+		if (result == null)
+			return dflt;
+		else
+			return result.trim().equalsIgnoreCase("true"); //$NON-NLS-1$
+	}
+
+	/**
+	 * dumps a String in the trace
+	 */
+	public static void debug(String s) {
+		StringBuffer msg = new StringBuffer();
+		msg.append(getPlugin().toString());
+		msg.append("^"); //$NON-NLS-1$
+		msg.append(Integer.toHexString(Thread.currentThread().hashCode()));
+		msg.append(" "); //$NON-NLS-1$
+		msg.append(s);
+		System.out.println(msg.toString());
+	}
+	
+	/**
+	 * Dumps a String in the log if WARNING is set to true
+	 */
+	public static void warn(String s) {
+		if (DEBUG && DEBUG_SHOW_WARNINGS) {
+			if (s!=null){
+				s="WARNING: "+s; //$NON-NLS-1$
+			}
+			log(s, null); 
+		}
+	}
+
+	/**
+	 * Dumps an exception in the log if WARNING is set to true
+	 * 
+	 * @param s log string
+	 * @param e exception to be logged
+	 * @since 2.0
+	 */
+	public static void warn(String s, Throwable e) {
+		if (DEBUG && DEBUG_SHOW_WARNINGS){
+			if (s!=null){
+				s="UPDATE MANAGER INFO: "+s; //$NON-NLS-1$
+			}
+			log(s,e);
+		}
+	}
+			
+	/**
+	 * Logs a status
+	 */
+	public static void log(IStatus status){
+		UpdateCore.getPlugin().getLog().log(status);		
+	}
+	
+	/**
+	 * Logs an error
+	 */
+	public static void log(Throwable e){		
+		log("",e); //$NON-NLS-1$
+	}	
+	
+	/**
+	 * Logs a string and an  error
+	 */
+	public static void log(String msg, Throwable e){
+		IStatus status = null;
+		if (e instanceof CoreException) 
+			status = ((CoreException)e).getStatus();
+		else 
+			status = Utilities.newCoreException(msg,e).getStatus();		
+		if (status!=null)
+			log(status);
+	}		
+	/*
+	 * Method log.
+	 * @param newConfiguration
+	 */
+	public static void log(IInstallConfiguration newConfiguration) {
+		if (log!=null)
+			log.log(newConfiguration);
+	}
+
+	/*
+	 * Get update log location relative to platform configuration
+	 */
+	private static File getInstallLogFile() throws IOException {
+		
+		IPlatformConfiguration config = ConfiguratorUtils.getCurrentPlatformConfiguration();		
+		URL configurationLocation = config.getConfigurationLocation();
+		if (configurationLocation==null){
+			warn("Unable to retrieve location for update manager log file"); //$NON-NLS-1$
+			return null;
+		}
+//		URL configLocation = Platform.resolve(configurationLocation);
+		File updateStateLocation = null;
+
+		if ("file".equalsIgnoreCase(configurationLocation.getProtocol())) { //$NON-NLS-1$
+			File path = new File(configurationLocation.getFile());
+			updateStateLocation = new File(path.getParentFile(), LOG_FILE);
+		}
+		return updateStateLocation;
+	}
+
+	/**
+	 * Sends the GET request to the server and returns the server's
+	 * response.
+	 *
+	 * @param url the URL to open on the server
+	 * @return the server's response
+	 * @throws IOException if an I/O error occurs. Reasons include:
+	 * <ul>
+	 * <li>The client is closed.
+	 * <li>The client could not connect to the server
+	 * <li>An I/O error occurs while communicating with the server
+	 * <ul>
+	 */
+
+	
+	/*
+	 * Returns true if the feature is a patch
+	 */
+	public static boolean isPatch(IFeature candidate) {
+		IImport[] imports = candidate.getImports();
+
+		for (int i = 0; i < imports.length; i++) {
+			IImport iimport = imports[i];
+			if (iimport.isPatch())
+				return true;
+		}
+		return false;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+	 */
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+		this.context = context;
+
+        DEBUG = getBooleanDebugOption("org.eclipse.update.core/debug", false); //$NON-NLS-1$
+
+		if (DEBUG) {
+			DEBUG_SHOW_WARNINGS = getBooleanDebugOption("org.eclipse.update.core/debug/warning", false); //$NON-NLS-1$
+			DEBUG_SHOW_PARSING = getBooleanDebugOption("org.eclipse.update.core/debug/parsing", false); //$NON-NLS-1$
+			DEBUG_SHOW_INSTALL = getBooleanDebugOption("org.eclipse.update.core/debug/install", false); //$NON-NLS-1$
+			DEBUG_SHOW_CONFIGURATION = getBooleanDebugOption("org.eclipse.update.core/debug/configuration", false); //$NON-NLS-1$
+			DEBUG_SHOW_TYPE = getBooleanDebugOption("org.eclipse.update.core/debug/type", false); //$NON-NLS-1$
+			DEBUG_SHOW_WEB = getBooleanDebugOption("org.eclipse.update.core/debug/web", false); //$NON-NLS-1$
+			DEBUG_SHOW_IHANDLER = getBooleanDebugOption("org.eclipse.update.core/debug/installhandler", false); //$NON-NLS-1$
+			DEBUG_SHOW_RECONCILER = getBooleanDebugOption("org.eclipse.update.core/debug/reconciler", false); //$NON-NLS-1$
+		}
+		
+		//
+		try {
+			File logFile = getInstallLogFile();
+			if (logFile!=null)
+				log = new UpdateManagerLogWriter(logFile);
+		} catch (IOException e){
+			warn("",e); //$NON-NLS-1$
+		}
+	}
+	/* (non-Javadoc)
+	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+	 */
+	public void stop(BundleContext context) throws Exception {
+		super.stop(context);
+		
+		JarContentReference.shutdown(); // make sure we are not leaving jars open
+		Utilities.shutdown(); // cleanup temp area
+		if (log!=null)
+			log.shutdown();
+		
+		ConnectionThreadManagerFactory.getConnectionManager().shutdown();
+
+		
+		this.context = null;
+		if (pkgAdminTracker != null) {
+			pkgAdminTracker.close();
+			pkgAdminTracker = null;
+		}
+		if (verifierFactoryTracker != null) {
+			verifierFactoryTracker.close();
+			verifierFactoryTracker = null;
+		}
+		if (proxyTracker != null) {
+			proxyTracker.close();
+			proxyTracker = null;
+		}
+	}
+	
+	public BundleContext getBundleContext() {
+		return context;
+	}
+	
+	PackageAdmin getPackageAdmin() {
+		if (pkgAdminTracker == null) {
+			pkgAdminTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null);
+			pkgAdminTracker.open();
+		}
+		return (PackageAdmin)pkgAdminTracker.getService();
+	}
+	
+	public IProxyService getProxyService() {
+		if (proxyTracker == null) {
+		    proxyTracker=new ServiceTracker(getBundle().getBundleContext(),
+		            IProxyService.class.getName(), null);
+		    proxyTracker.open();
+		}
+		return (IProxyService)proxyTracker.getService();
+	}
+
+
+	public SignedContentFactory getSignedContentFactory() {
+		if (verifierFactoryTracker == null) {
+			verifierFactoryTracker = new ServiceTracker(context, SignedContentFactory.class.getName(), null);
+			verifierFactoryTracker.open();
+		}
+		return (SignedContentFactory)verifierFactoryTracker.getService();
+	}
+	
+	public UpdateSession getUpdateSession() {
+		synchronized(UpdateSession.class) {
+			if (updateSession == null) {
+				updateSession = new UpdateSession();
+			}
+		}		
+		return updateSession;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateManagerLogWriter.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateManagerLogWriter.java
new file mode 100644
index 0000000..1daf0ca
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateManagerLogWriter.java
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.update.configuration.IActivity;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+
+
+/**
+ * A log writer that writes update manager log entries.  
+ */
+public class UpdateManagerLogWriter {
+	private File logFile = null;
+	private Writer log = null;
+
+	private static final String CONFIGURATION = "!CONFIGURATION"; //$NON-NLS-1$
+	private static final String ACTIVITY = "!ACTIVITY"; //$NON-NLS-1$
+
+	private static final String SUCCESS = "success"; //$NON-NLS-1$
+	private static final String FAILURE = "failure"; //$NON-NLS-1$
+
+	private static final String FEATURE_INSTALL = "feature-install"; //$NON-NLS-1$
+	private static final String FEATURE_REMOVE = "feature-remove"; //$NON-NLS-1$
+	private static final String SITE_INSTALL = "site-install"; //$NON-NLS-1$
+	private static final String SITE_REMOVE = "site-remove"; //$NON-NLS-1$
+	private static final String UNCONFIGURE = "feature-disable"; //$NON-NLS-1$
+	private static final String CONFIGURE = "feature-enable"; //$NON-NLS-1$
+	private static final String REVERT = "revert"; //$NON-NLS-1$
+	private static final String RECONCILIATION = "reconciliation"; //$NON-NLS-1$
+	private static final String PRESERVED = "preserve-configuration"; //$NON-NLS-1$	
+	private static final String UNKNOWN = "unknown"; //$NON-NLS-1$	
+
+	private static final String LINE_SEPARATOR;
+
+	static {
+		String s = System.getProperty("line.separator"); //$NON-NLS-1$
+		LINE_SEPARATOR = s == null ? "\n" : s; //$NON-NLS-1$
+	}
+
+	/*
+	 * 
+	 */
+	public UpdateManagerLogWriter(File file) {
+		this.logFile = file;
+		UpdateCore.warn("UPDATE MANAGER LOG Location: "+file.getAbsolutePath()); //$NON-NLS-1$
+
+		// If the file does not exist, prime it with the sites in the exisiting config
+		if (!file.exists())
+			initLog();
+	}
+	
+	/*
+	 * Initializes the log with the original configuration
+	 */
+	private void initLog() {
+		try {
+			IPlatformConfiguration runtimeConfig = ConfiguratorUtils.getCurrentPlatformConfiguration();
+			IPlatformConfiguration.ISiteEntry[] sites = runtimeConfig.getConfiguredSites();
+			ConfigurationActivity[] activities = new ConfigurationActivity[sites.length];
+			for (int i=0; i<sites.length; i++) {
+				activities[i] = new ConfigurationActivity(IActivity.ACTION_SITE_INSTALL);
+				activities[i].setLabel(FileLocator.toFileURL(sites[i].getURL()).toExternalForm());
+				activities[i].setDate(new Date());
+				activities[i].setStatus(IActivity.STATUS_OK);
+			}
+			Date date = new Date(runtimeConfig.getChangeStamp());
+			safeWriteConfiguration(date, activities);
+		} catch (Exception e) {
+			// silently ignore errors
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private void closeLogFile() throws IOException {
+		try {
+			if (log != null) {
+				log.flush();
+				log.close();
+			}
+		} finally {
+			log = null;
+		}
+	}
+	
+	/*
+	 * 
+	 */
+	public void log(IInstallConfiguration installConfig) {
+		safeWriteConfiguration(installConfig.getCreationDate(), installConfig.getActivities());
+	}
+	
+	
+	/*
+	 * 
+	 */
+	private void openLogFile() {
+		try {
+			log = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile.getAbsolutePath(), true), "UTF-8")); //$NON-NLS-1$
+		} catch (IOException e) {
+			// there was a problem opening the log file so log to the console
+			log = logForStream(System.err);
+		}
+	}
+	
+    /*
+     * 
+     */
+	private String getFormattedDate(Date date) {
+		try {
+			DateFormat formatter = new SimpleDateFormat("MMM dd, yyyy kk:mm:ss.SS"); //$NON-NLS-1$
+			return formatter.format(date);
+		} catch (Exception e) {
+			// If there were problems writing out the date, ignore and
+			// continue since that shouldn't stop us from losing the rest
+			// of the information
+		}
+		return Long.toString(System.currentTimeMillis());
+	}
+	
+	/*
+	 * 
+	 */
+	private Writer logForStream(OutputStream output) {
+		try {
+			return new BufferedWriter(new OutputStreamWriter(output, "UTF-8")); //$NON-NLS-1$
+		} catch (UnsupportedEncodingException e) {
+			return new BufferedWriter(new OutputStreamWriter(output));
+		}
+	}
+	
+	
+	/*
+	 * Shuts down the update manager log.
+	 */
+	public synchronized void shutdown() {
+		try {
+			if (logFile != null) {
+				closeLogFile();
+				logFile = null;
+			} else {
+				if (log != null) {
+					Writer old = log;
+					log = null;
+					old.flush();
+					old.close();
+				}
+			}
+		} catch (IOException e) {
+			//we've shutdown the log, so not much else we can do!
+			e.printStackTrace();
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private synchronized void safeWriteConfiguration(Date date, IActivity[] activities) {
+		// thread safety: (Concurrency003)
+		if (logFile != null)
+			openLogFile();
+		if (log == null)
+			log = logForStream(System.err);
+		try {
+			try {
+				write(date, activities);
+			} finally {
+				if (logFile != null)
+					closeLogFile();
+				else
+					log.flush();
+			}
+		} catch (Exception e) {
+			System.err.println("An exception occurred while writing to the update manager log:"); //$NON-NLS-1$
+			e.printStackTrace(System.err);
+			System.err.println("Logging to the console instead."); //$NON-NLS-1$
+			//we failed to write, so dump log entry to console instead
+			try {
+				log = logForStream(System.err);
+				write(date, activities);
+				log.flush();
+			} catch (Exception e2) {
+				System.err.println("An exception occurred while logging to the console:"); //$NON-NLS-1$
+				e2.printStackTrace(System.err);
+			}
+		} finally {
+			log = null;
+		}
+	}
+	
+
+	/*
+	 * !CONFIGURATION <label>
+	 */
+	private void write(Date date, IActivity[] activities) throws IOException {
+		writeln();
+		write(CONFIGURATION);
+		writeSpace();	
+		write(String.valueOf(date.getTime()));
+		writeSpace();
+		write(date.toString());
+		writeln();
+		for (int i = 0; i < activities.length; i++) {
+			write(activities[i]);
+		}				
+	}
+
+	/*
+	 * !ACTIVITY <date> <target> <action> <status>
+	 */
+	private void write(IActivity activity) throws IOException {
+		write(ACTIVITY);
+		writeSpace();	
+		write(String.valueOf(activity.getDate().getTime()));
+		writeSpace();
+		write(getFormattedDate(activity.getDate()));
+		writeSpace();
+		write(activity.getLabel());
+		writeSpace();
+		write(getAction(activity.getAction()));
+		writeSpace();
+		write((activity.getStatus()==IActivity.STATUS_OK)?SUCCESS:FAILURE);
+		writeln();		
+	}
+
+	/*
+	 * 
+	 */
+	private String getAction(int i) {
+		switch (i) {
+			case IActivity.ACTION_FEATURE_INSTALL :
+				return FEATURE_INSTALL;
+			case IActivity.ACTION_FEATURE_REMOVE :
+				return FEATURE_REMOVE;
+			case IActivity.ACTION_SITE_INSTALL :
+				return SITE_INSTALL;
+			case IActivity.ACTION_SITE_REMOVE :
+				return SITE_REMOVE;
+			case IActivity.ACTION_UNCONFIGURE :
+				return UNCONFIGURE;
+			case IActivity.ACTION_CONFIGURE :
+				return CONFIGURE;
+			case IActivity.ACTION_REVERT :
+				return REVERT;
+			case IActivity.ACTION_RECONCILIATION :
+				return RECONCILIATION;
+			case IActivity.ACTION_ADD_PRESERVED :
+				return PRESERVED;
+							
+			default :
+				return UNKNOWN+" ["+i+"]"; //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+
+	/*
+	 * 
+	 */
+	private void writeln() throws IOException {
+		write(LINE_SEPARATOR);
+	}
+	
+	/*
+	 * 
+	 */
+	private void write(String message) throws IOException {
+		if (message != null)
+			log.write(message);
+	}
+	
+	/*
+	 * 
+	 */
+	private void writeSpace() throws IOException {
+		write(" "); //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateManagerUtils.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateManagerUtils.java
new file mode 100644
index 0000000..d1e713a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateManagerUtils.java
@@ -0,0 +1,964 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 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
+ *     Chris Aniszczyk (IBM Corp.) - Fixed NPE
+ *******************************************************************************/
+package org.eclipse.update.internal.core;
+
+import org.eclipse.update.core.IUpdateConstants;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.InstallAbortedException;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+import org.eclipse.update.internal.core.connection.IResponse;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * 
+ */
+public class UpdateManagerUtils {
+
+	private static boolean OS_UNIX = org.eclipse.osgi.service.environment.Constants.OS_HPUX
+			.equals(Platform.getOS())
+			|| org.eclipse.osgi.service.environment.Constants.OS_AIX
+					.equals(Platform.getOS())
+			|| org.eclipse.osgi.service.environment.Constants.OS_LINUX
+					.equals(Platform.getOS())
+			|| org.eclipse.osgi.service.environment.Constants.OS_SOLARIS
+					.equals(Platform.getOS())
+			|| org.eclipse.osgi.service.environment.Constants.OS_MACOSX
+					.equals(Platform.getOS());
+	private static FragmentEntry[] noFragments = new FragmentEntry[0];
+	private static Map table;
+
+	static {
+		table = new HashMap();
+		table.put("compatible", new Integer(IUpdateConstants.RULE_COMPATIBLE)); //$NON-NLS-1$
+		table.put("perfect", new Integer(IUpdateConstants.RULE_PERFECT)); //$NON-NLS-1$
+		table.put("equivalent", new Integer(IUpdateConstants.RULE_EQUIVALENT)); //$NON-NLS-1$
+		table.put("greaterOrEqual", new Integer(IUpdateConstants.RULE_GREATER_OR_EQUAL)); //$NON-NLS-1$
+	}
+
+	// manage URL to File
+	private static Map urlFileMap;
+
+	private static Map localFileFragmentMap;
+	private static Stack bufferPool;
+	private static final int BUFFER_SIZE = 4096; // 4kbytes
+	private static final int INCREMENT_SIZE = 10240; // 10kbytes
+	/**
+	 * return the urlString if it is a absolute URL
+	 * otherwise, return the default URL if the urlString is null
+	 * The defaultURL may point ot a file, create a file URL then
+	 * if the urlString or the default URL are relatives, prepend the rootURL to it
+	 */
+	public static URL getURL(URL rootURL, String urlString, String defaultURL) throws MalformedURLException {
+		URL url = null;
+
+		// if no URL , provide Default
+		if (urlString == null || urlString.trim().equals("")) { //$NON-NLS-1$
+
+			// no URL, no default, return right now...
+			if (defaultURL == null || defaultURL.trim().equals("")) //$NON-NLS-1$
+				return null;
+			else
+				urlString = defaultURL;
+		}
+
+		// URL can be relative or absolute	
+		if (urlString.startsWith("/") && urlString.length() > 1) //$NON-NLS-1$
+			urlString = urlString.substring(1);
+		try {
+			url = new URL(urlString);
+		} catch (MalformedURLException e) {
+			// the url is not an absolute URL
+			// try relative
+			url = new URL(rootURL, urlString);
+		}
+
+		return url;
+	}
+
+	/**
+	 * return a relative String to rootURL 
+	 * if url contains rootURL so
+	 * new URL(rootURL, resultString) == url
+	 * 
+	 */
+	public static String getURLAsString(URL rootURL, URL url) {
+		String result = null;
+
+		if (rootURL == null) {
+			return (url == null) ? null : url.toString();
+		}
+
+		// if no URL , return null
+		if (url != null) {
+
+			result = url.toExternalForm();
+
+			if (rootURL.getHost() != null && !rootURL.getHost().equals(url.getHost()))
+				return result;
+
+			if (rootURL.getProtocol() != null && !rootURL.getProtocol().equals(url.getProtocol()))
+				return result;
+
+			if (rootURL.getPort() != url.getPort())
+				return result;
+
+			String rootURLFileString = rootURL.getFile();
+			rootURLFileString = rootURLFileString.replace(File.separatorChar, '/');
+			if (!rootURLFileString.endsWith("/")) { //$NON-NLS-1$
+				int index = rootURLFileString.lastIndexOf('/');
+				if (index != -1) {
+					rootURLFileString = rootURLFileString.substring(0, index);
+				}
+			}
+			String urlFileString = url.getFile();
+
+			if (urlFileString.startsWith(rootURLFileString)) {
+				result = urlFileString.substring(rootURLFileString.length());
+				result = result.replace(File.separatorChar, '/');
+			} else {
+				// we need to check the following
+				// file:/C:/ and file:C:/
+				if ("file".equalsIgnoreCase(url.getProtocol())) { //$NON-NLS-1$
+					File rootFile = new File(rootURLFileString);
+					File urlFile = new File(urlFileString);
+
+					File relativePath = urlFile;
+					while (relativePath != null && !rootFile.equals(relativePath.getParentFile())) {
+						relativePath = relativePath.getParentFile();
+					}
+
+					if (relativePath == null) {
+						UpdateCore.warn("Cannot calculate relative path"); //$NON-NLS-1$
+						return url.toString();
+					} else {
+						String relativeRootString = relativePath.getParentFile().getAbsolutePath();
+						String fullString = urlFile.getAbsolutePath();
+						if (!fullString.startsWith(relativeRootString)) {
+							UpdateCore.warn("Full path:" + fullString + " does not start with " + relativeRootString); //$NON-NLS-1$ //$NON-NLS-2$
+							return url.toString();
+						} else {
+							String returnString = fullString.substring(relativeRootString.length() + 1);
+							if (urlFile.isDirectory())
+								returnString += File.separator;
+							// we lost the last slash when tranforming in File
+							returnString = returnString.replace(File.separatorChar, '/');
+							return returnString;
+						}
+
+					}
+
+				} else {
+					result = url.toString();
+				}
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * returns a translated String
+	 */
+	public static String getResourceString(String infoURL, ResourceBundle bundle) {
+		String result = null;
+		if (infoURL != null) {
+			result = Platform.getResourceString(UpdateCore.getPlugin().getBundle(), infoURL, bundle);
+		}
+		return result;
+	}
+
+	/**
+	 * 
+	 */
+	public static URL copyToLocal(InputStream sourceContentReferenceStream, String localName, InstallMonitor monitor) throws MalformedURLException, IOException, InstallAbortedException {
+		URL result = null;
+		// create the Dir if they do not exist
+		// get the path from the File to resolve File.separator..
+		// do not use the String as it may contain URL like separator
+		File localFile = new File(localName);
+		int index = localFile.getPath().lastIndexOf(File.separator);
+		if (index != -1) {
+			File dir = new File(localFile.getPath().substring(0, index));
+			if (!dir.exists())
+				dir.mkdirs();
+		}
+
+		// transfer the content of the File
+		if (!localFile.isDirectory()) {
+			OutputStream localContentReferenceStream = new FileOutputStream(localFile);
+			try {
+				Utilities.copy(sourceContentReferenceStream, localContentReferenceStream, monitor);
+			} finally {
+				try {
+					localContentReferenceStream.close();
+				} catch (IOException e){}
+			}
+		}
+		result = localFile.toURL();
+		return result;
+	}
+
+	/*
+	 * [20305] need to slam permissions for executable libs on some
+	 * platforms. This is a temporary fix
+	 */
+	public static void checkPermissions(ContentReference ref, String filePath) {
+
+		if (ref.getPermission() != 0) {
+			UpdateCore.warn("Change permission for " + filePath + " to " + ref.getPermission()); //$NON-NLS-1$ //$NON-NLS-2$
+			// FIXME: change the code to use JNI
+		}
+
+		if (filePath != null && OS_UNIX && ref.getPermission() != 0) {
+			// add execute permission on shared libraries 20305
+			// do not remove write permission 20896
+			// chmod a+x *.sl
+			try {
+				Process pr = Runtime.getRuntime().exec(new String[] { "chmod", "a+x", filePath }); //$NON-NLS-1$ //$NON-NLS-2$
+				Thread chmodOutput = new StreamConsumer(pr.getInputStream());
+				chmodOutput.setName("chmod output reader"); //$NON-NLS-1$
+				chmodOutput.start();
+				Thread chmodError = new StreamConsumer(pr.getErrorStream());
+				chmodError.setName("chmod error reader"); //$NON-NLS-1$
+				chmodError.start();
+			} catch (IOException ioe) {
+			}
+
+		}
+	}
+
+	/**
+	 * Returns a random file name for the local system
+	 * attempt to conserve the extension if there is a '.' in the path
+	 * and no File.Seperator after the '.'
+	 * 
+	 * \a\b\c.txt -> c987659385.txt
+	 * c.txt -> c3854763.txt
+	 * c	-> c953867549
+	 */
+	public static String getLocalRandomIdentifier(String remotePath, Date date) {
+		int dotIndex = remotePath.lastIndexOf(".");	//$NON-NLS-1$
+		int fileIndex = remotePath.lastIndexOf(File.separator);
+		// if there is a separator after the dot
+		// do not consider it as an extension
+		String ext = (dotIndex != -1 && fileIndex < dotIndex) ? remotePath.substring(dotIndex) : ""; //$NON-NLS-1$
+		// the name is the string between the separator and the dot
+		// if there is no separator, it is the string up to the dot		
+		// if there is no dot, go to the end of the string 
+		if (fileIndex == -1)
+			fileIndex = 0;
+		if (dotIndex == -1)
+			dotIndex = remotePath.length();
+		// if I have a separator and no dot: /a/b/c -> c
+		// if my separator is the last /a/b/c/, fileIndex and dotIndex are the same, so it will return the default temp name
+		String name = (fileIndex < dotIndex) ? remotePath.substring(fileIndex, dotIndex) : "Eclipse_Update_TMP_"; //$NON-NLS-1$
+		String result = name + date.getTime() + ext;
+		return result;
+	}
+
+	/**
+	 * remove a file or directory from the file system.
+	 * used to clean up install
+	 */
+	public static void removeFromFileSystem(File file) {
+		if (!file.exists() || !file.canWrite())
+			return;
+
+		if (file.isDirectory()) {
+			String[] files = file.list();
+			if (files != null) // be careful since file.list() can return null
+				for (int i = 0; i < files.length; ++i)
+					removeFromFileSystem(new File(file, files[i]));
+		}
+
+		if (!file.delete()) {
+			String msg = NLS.bind(Messages.UpdateManagerUtils_UnableToRemoveFile, (new String[] { file.getAbsolutePath() }));
+			UpdateCore.log(msg, new Exception());
+		}
+	}
+
+	/**
+	 * remove all the empty directories recursively
+	 * used to clean up install
+	 */
+	public static void removeEmptyDirectoriesFromFileSystem(File file) {
+		if (!file.isDirectory())
+			return;
+
+		String[] files = file.list();
+		if (files != null) { // be careful since file.list() can return null
+			for (int i = 0; i < files.length; ++i) {
+				removeEmptyDirectoriesFromFileSystem(new File(file, files[i]));
+			}
+		}
+		if (!file.delete()) {
+			String msg = NLS.bind(Messages.UpdateManagerUtils_UnableToRemoveFile, (new String[] { file.getAbsolutePath() }));
+			UpdateCore.log(msg, new Exception());
+		}
+	}
+
+	/**
+	 * Returns the plugin entries that are in source array and
+	 * not in target array
+	 */
+	public static IPluginEntry[] diff(IPluginEntry[] sourceArray, IPluginEntry[] targetArray) { // No pluginEntry to Install, return Nothing to instal
+		if (sourceArray == null || sourceArray.length == 0) {
+			return new IPluginEntry[0];
+		} // No pluginEntry installed, Install them all
+		if (targetArray == null || targetArray.length == 0) {
+			return sourceArray;
+		} // if a IPluginEntry from sourceArray is NOT in
+		// targetArray, add it to the list
+		List list1 = Arrays.asList(targetArray);
+		List result = new ArrayList(0);
+		for (int i = 0; i < sourceArray.length; i++) {
+			if (!list1.contains(sourceArray[i]))
+				result.add(sourceArray[i]);
+		}
+
+		IPluginEntry[] resultEntry = new IPluginEntry[result.size()];
+		if (result.size() > 0)
+			result.toArray(resultEntry);
+		return resultEntry;
+	}
+
+	/**
+	 * 
+	 */
+	public static URL asDirectoryURL(URL url) throws MalformedURLException {
+		//url = URLEncoder.encode(url);
+		String path = url.getFile();
+		if (!path.endsWith("/")) { //$NON-NLS-1$
+			int index = path.lastIndexOf('/');
+			if (index != -1)
+				path = path.substring(0, index + 1);
+			// ignore any ref in original URL
+			url = new URL(url.getProtocol(), url.getHost(), url.getPort(), path);
+		}
+		return url;
+	}
+
+	/*
+	 * Compares two URL for equality
+	 * Return false if one of them is null
+	 */
+	public static boolean sameURL(URL url1, URL url2) {
+
+		if (url1 == null || url2 == null)
+			return false;
+		if (url1 == url2)
+			return true;
+		if (url1.equals(url2))
+			return true;
+
+		// check if URL are file: URL as we may
+		// have 2 URL pointing to the same featureReference
+		// but with different representation
+		// (i.e. file:/C;/ and file:C:/)
+		if (!"file".equalsIgnoreCase(url1.getProtocol())) //$NON-NLS-1$
+			return false;
+		if (!"file".equalsIgnoreCase(url2.getProtocol())) //$NON-NLS-1$
+			return false;
+
+		File file1 = getFileFor(url1);//new File(url1.getFile());
+		File file2 = getFileFor(url2);
+
+		if (file1 == null)
+			return false;
+
+		return (file1.equals(file2));
+	}
+	
+	/*
+	 * Method getFileFor.
+	 * @param url1
+	 * @return File
+	 */
+	private static File getFileFor(URL url1) {
+		if (urlFileMap == null) urlFileMap = new HashMap();
+		if (urlFileMap.get(url1)!=null) return (File)urlFileMap.get(url1);
+		File newFile = new File(url1.getFile());
+		urlFileMap.put(url1,newFile);
+		return newFile; 
+	}
+
+	/*
+	 * returns the list of FeatureReference that are parent of 
+	 * the Feature or an empty array if no parent found.
+	 * @param onlyOptional if set to <code>true</code> only return parents that consider the feature optional
+	 * @param child
+	 * @param possiblesParent
+	 */
+	public static IFeatureReference[] getParentFeatures(IFeature childFeature, IFeatureReference[] possiblesParent, boolean onlyOptional) throws CoreException {
+
+		if (childFeature == null)
+			return new IFeatureReference[0];
+
+		List parentList = new ArrayList();
+		IIncludedFeatureReference[] children = null;
+		IFeature compareFeature = null;
+		for (int i = 0; i < possiblesParent.length; i++) {
+			try {
+				IFeature possibleParentFeature = possiblesParent[i].getFeature(null);
+				if (possibleParentFeature != null) {
+					children = possibleParentFeature.getIncludedFeatureReferences();
+					for (int j = 0; j < children.length; j++) {
+						try {
+							compareFeature = children[j].getFeature(null);
+						} catch (CoreException e) {
+							UpdateCore.warn("", e); //$NON-NLS-1$
+						}
+						if (childFeature.equals(compareFeature)) {
+							if (onlyOptional) {
+								if (UpdateManagerUtils.isOptional(children[j])) {
+									parentList.add(possiblesParent[i]);
+								} else {
+									UpdateCore.warn("Feature :" + children[j] + " not optional. Not included in parents list."); //$NON-NLS-1$ //$NON-NLS-2$
+								}
+							} else {
+								parentList.add(possiblesParent[i]);
+							}
+						}
+					}
+				}
+			} catch (CoreException e) {
+				UpdateCore.warn("", e); //$NON-NLS-1$
+			}
+		}
+
+		IFeatureReference[] parents = new IFeatureReference[0];
+		if (parentList.size() > 0) {
+			parents = new IFeatureReference[parentList.size()];
+			parentList.toArray(parents);
+		}
+		return parents;
+	}
+
+	/*
+	 * If the return code of the HTTP connection is not 200 (OK)
+	 * thow an IO exception
+	 * 
+	 */
+	public static void checkConnectionResult(IResponse response,URL url) throws IOException {
+		// did the server return an error code ?
+		int result = response.getStatusCode();
+
+		if (result != UpdateCore.HTTP_OK) { 
+			String serverMsg = response.getStatusMessage();
+			response.close();
+			throw new FatalIOException(NLS.bind(Messages.ContentReference_HttpNok, (new Object[] { new Integer(result), serverMsg, url })));						
+		}
+	}
+
+	public static class StreamConsumer extends Thread {
+		InputStream is;
+		byte[] buf;
+		public StreamConsumer(InputStream inputStream) {
+			super();
+			this.setDaemon(true);
+			this.is = inputStream;
+			buf = new byte[512];
+		}
+		public void run() {
+			try {
+				int n = 0;
+				while (n >= 0)
+					n = is.read(buf);
+			} catch (IOException ioe) {
+			}
+		}
+	}
+
+	/**
+	 * Return the optional children to install
+	 * The optional features to install may not all be direct children 
+	 * of the feature.
+	 * Also include non-optional features
+	 * 
+	 * @param children all the nested features
+	 * @param optionalfeatures optional features to install
+	 * @return IFeatureReference[]
+	 */
+	public static IFeatureReference[] optionalChildrenToInstall(IFeatureReference[] children, IFeatureReference[] optionalfeatures) {
+
+		List optionalChildrenToInstall = new ArrayList();
+		for (int i = 0; i < children.length; i++) {
+			IFeatureReference optionalFeature = children[i];
+			if (!UpdateManagerUtils.isOptional(optionalFeature)) {
+				optionalChildrenToInstall.add(optionalFeature);
+			} else {
+				for (int j = 0; j < optionalfeatures.length; j++) {
+					if (optionalFeature.equals(optionalfeatures[j])) {
+						optionalChildrenToInstall.add(optionalFeature);
+						break;
+					}
+				}
+			}
+		}
+
+		IFeatureReference[] result = new IFeatureReference[optionalChildrenToInstall.size()];
+		if (optionalChildrenToInstall.size() > 0) {
+			optionalChildrenToInstall.toArray(result);
+		}
+
+		return result;
+	}
+
+	/**
+	 * returns the mapping of matching rules
+	 * the default returns perfect
+	 * 
+	 * @since 2.0.2
+	 */
+	public static int getMatchingRule(String rule) {
+		if (rule == null)
+			return IUpdateConstants.RULE_COMPATIBLE;
+		final Integer integer = (Integer) table.get(rule);
+		if (integer == null)
+			return IUpdateConstants.RULE_PERFECT;
+		int ruleInt = integer.intValue();
+		if (ruleInt == IUpdateConstants.RULE_NONE)
+			return IUpdateConstants.RULE_PERFECT;
+		return ruleInt;
+	}
+	
+	/**
+	 * returns the mapping of matching id rules
+	 * the default returns perfect
+	 * 
+	 * @since 2.0.2
+	 */
+	public static int getMatchingIdRule(String rule) {
+		
+		if (rule == null)
+			return IUpdateConstants.RULE_COMPATIBLE;
+		if (rule!=null && rule.equalsIgnoreCase("prefix")) //$NON-NLS-1$
+			return IUpdateConstants.RULE_PREFIX;
+		return IUpdateConstants.RULE_PERFECT;
+	}
+	
+	/**
+	 * Method isOptional.
+	 * @param featureReference
+	 * @return boolean
+	 */
+	public static boolean isOptional(IFeatureReference featureReference) {
+		if (featureReference==null) return false;
+		if (featureReference instanceof IIncludedFeatureReference){
+			return ((IIncludedFeatureReference)featureReference).isOptional();
+		}
+		return false;
+	}
+
+	/**
+	 * 
+	 */
+	public static boolean isValidEnvironment(IPlatformEnvironment candidate) {
+		if (candidate==null) return false;
+		String os = candidate.getOS();
+		String ws = candidate.getWS();
+		String arch = candidate.getOSArch();
+		String nl = candidate.getNL();
+		if (os!=null && !isMatching(os, SiteManager.getOS())) return false;
+		if (ws!=null && !isMatching(ws, SiteManager.getWS())) return false;
+		if (arch!=null && !isMatching(arch, SiteManager.getOSArch())) return false;
+		if (nl!=null && !isMatchingLocale(nl, SiteManager.getNL())) return false;
+		return true;
+	}
+
+	/* Original code - commented out to provide a replacement as per bug 98387
+	
+	private static boolean isMatching(String candidateValues, String siteValues) {
+		if (siteValues==null) return false;
+		if ("*".equals(candidateValues)) return true; //$NON-NLS-1$
+		if ("".equals(candidateValues)) return true; //$NON-NLS-1$
+		siteValues = siteValues.toUpperCase();		
+		StringTokenizer stok = new StringTokenizer(candidateValues, ","); //$NON-NLS-1$
+		while (stok.hasMoreTokens()) {
+			String token = stok.nextToken().toUpperCase();
+			if (siteValues.indexOf(token)!=-1) return true;
+		}
+		return false;
+	}
+	*/
+	
+	/*
+	 * Fixed bug 98387
+	 */
+	
+	private static boolean isMatching(String candidateValues, String siteValues) {
+		if (siteValues==null) return false;
+		if ("*".equals(candidateValues)) return true; //$NON-NLS-1$
+		if ("".equals(candidateValues)) return true; //$NON-NLS-1$
+		StringTokenizer siteTokens = new StringTokenizer(siteValues, ",");  //$NON-NLS-1$
+		//$NON-NLS-1$	
+		while(siteTokens.hasMoreTokens()) {
+		    StringTokenizer candidateTokens = new StringTokenizer
+	                                       (candidateValues, ","); //$NON-NLS-1$
+			String siteValue = siteTokens.nextToken();
+			while (candidateTokens.hasMoreTokens()) {
+				if (siteValue.equalsIgnoreCase
+	                             (candidateTokens.nextToken())) return true;
+			}
+		}
+		return false;
+	}
+
+	
+	/**
+	 * 
+	 */	
+	private static boolean isMatchingLocale(String candidateValues, String locale) {
+		if (locale==null) return false;
+		if ("*".equals(candidateValues)) return true; //$NON-NLS-1$
+		if ("".equals(candidateValues)) return true; //$NON-NLS-1$
+		
+		locale = locale.toUpperCase();
+		candidateValues = candidateValues.toUpperCase();	
+		StringTokenizer stok = new StringTokenizer(candidateValues, ","); //$NON-NLS-1$
+		while (stok.hasMoreTokens()) {
+			String candidate = stok.nextToken();
+			if (locale.indexOf(candidate) == 0)
+				return true;
+			if (candidate.indexOf(locale) == 0)
+				return true;
+		}
+		return false;
+	}
+
+	
+	/*
+	 * 
+	 */
+	private static void appendEscapedChar(StringBuffer buffer, char c) {
+		String replacement = getReplacement(c);
+		if (replacement != null) {
+			buffer.append('&');
+			buffer.append(replacement);
+			buffer.append(';');
+		} else {
+			if ((c >= ' ' && c <= 0x7E) || c == '\n' || c == '\r' || c == '\t') {
+				buffer.append(c);
+			} else {
+				buffer.append("&#"); //$NON-NLS-1$
+				buffer.append(Integer.toString(c));
+				buffer.append(';');
+			}
+		}
+	}
+
+	/*
+	 * 
+	 */
+	public static String xmlSafe(String s) {
+		StringBuffer result = new StringBuffer(s.length() + 10);
+		for (int i = 0; i < s.length(); ++i)
+			appendEscapedChar(result, s.charAt(i));
+		return result.toString();
+	}
+	
+	/*
+	 * 
+	 */
+	private static String getReplacement(char c) {
+		// Encode special XML characters into the equivalent character references.
+		// These five are defined by default for all XML documents.
+		switch (c) {
+			case '<' :
+				return "lt"; //$NON-NLS-1$
+			case '>' :
+				return "gt"; //$NON-NLS-1$
+			case '"' :
+				return "quot"; //$NON-NLS-1$
+			case '\'' :
+				return "apos"; //$NON-NLS-1$
+			case '&' :
+				return "amp"; //$NON-NLS-1$
+		}
+		return null;
+	}
+	
+	public static boolean isSameTimestamp(URL url, long timestamp) {	
+		try {
+			if (UpdateCore.getPlugin().getUpdateSession().isVisited(url)) {
+				return true;
+			}
+			URL resolvedURL = URLEncoder.encode(url);
+			IResponse response = ConnectionFactory.get(resolvedURL);
+			long remoteLastModified = response.getLastModified();
+			// 2 seconds tolerance, as some OS's may round up the time stamp
+			// to the closest second. For safety, we make it 2 seconds.
+			return Math.abs(remoteLastModified - timestamp)/1000 <= 2;
+		} catch (MalformedURLException e) {
+			return false;
+		} catch (IOException e) {
+			return false;
+		}
+	}
+	/**
+	 * The file is associated with a lookup key.
+	 * @param key optional lookup key, or <code>null</code>.
+	 * @param temp the local working file
+	 */
+	public synchronized static void mapLocalFileFragment(String key, FileFragment temp) {
+		// create file association 
+		if (key != null) {
+			if (localFileFragmentMap == null)
+				localFileFragmentMap = new HashMap();
+			localFileFragmentMap.put(key, temp);
+		}
+	}
+
+	/**
+	 * The file is associated with a lookup key.
+	 * @param key optional lookup key, or <code>null</code>.
+	 */
+	public synchronized static void unMapLocalFileFragment(String key) {
+		// remove file association 
+		if (key != null && localFileFragmentMap !=null) {
+			localFileFragmentMap.remove(key);
+		}
+	}
+	
+	/**
+	 * Returns a previously cached local file (in temporary area) matching the
+	 * specified key. 
+	 * 
+	 * @param key lookup key
+	 * @return cached file, or <code>null</code>.
+	 */
+	public static synchronized FileFragment lookupLocalFileFragment(String key) {
+		if (localFileFragmentMap == null)
+			return null;
+		return (FileFragment) localFileFragmentMap.get(key);
+	}
+	
+	/**
+	 * Copies specified input stream to the output stream. Neither stream
+	 * is closed as part of this operation.
+	 * 
+	 * @param is input stream
+	 * @param os output stream
+	 * @param monitor progress monitor
+     * @param expectedLength - if > 0, the number of bytes from InputStream will be verified
+	 * @@return the offset in the input stream where copying stopped. Returns -1 if end of input stream is reached.
+	 * @since 2.0
+	 */
+	public static long copy(InputStream is, OutputStream os, InstallMonitor monitor, long expectedLength) {
+		byte[] buf = getBuffer();
+		long offset=0;
+		try {
+			int len = is.read(buf);
+			int nextIncrement = 0;
+			while (len != -1) {
+				os.write(buf, 0, len);
+					offset += len;
+				if (monitor != null) {
+					nextIncrement += len;
+					// update monitor periodically
+					if (nextIncrement >= INCREMENT_SIZE){ 	
+						monitor.incrementCount(nextIncrement);
+						nextIncrement = 0;
+					}
+					if (monitor.isCanceled()) {
+						return offset;
+					}
+				}
+				if (expectedLength > 0 && offset == expectedLength) {
+					// everything read do not return offset, otherwise trying
+					// to read again from this offset will result in HTTP 416
+					break;
+				}
+				
+				len = is.read(buf);
+			}
+			if (nextIncrement > 0 && monitor != null)
+				monitor.incrementCount(nextIncrement);
+			if(expectedLength>0 && offset!=expectedLength)
+				throw new IOException(NLS.bind(Messages.UpdateManagerUtils_inputStreamEnded, (new String[] { String.valueOf(offset), String.valueOf(expectedLength) })));
+			return -1;
+		} catch(IOException e){
+			// Log the actual error, as this is no longer
+			// passed up the calling stack
+			UpdateCore.log(Messages.UpdateManagerUtils_copy + offset, e); 
+			return offset;
+		} finally {
+			freeBuffer(buf);
+		}
+	}
+
+	private static synchronized byte[] getBuffer() {
+		if (bufferPool == null) {
+			return new byte[BUFFER_SIZE];
+		}
+
+		try {
+			return (byte[]) bufferPool.pop();
+		} catch (EmptyStackException e) {
+			return new byte[BUFFER_SIZE];
+		}
+	}
+
+	private static synchronized void freeBuffer(byte[] buf) {
+		if (bufferPool == null)
+			bufferPool = new Stack();
+		bufferPool.push(buf);
+	}
+	
+	
+	/**
+	 * Returns a list of fragments. Zero length if no fragments.
+	 * @param bundle the bundle to get fragments for
+	 */
+	public static FragmentEntry[] getFragments(Bundle bundle) {
+		PackageAdmin pkgAdmin = UpdateCore.getPlugin().getPackageAdmin();
+		Bundle[] fragmentBundles = pkgAdmin.getFragments(bundle);
+		if (fragmentBundles == null) 
+			return noFragments;
+		
+		FragmentEntry[] fragments = new FragmentEntry[fragmentBundles.length];
+		for (int i = 0; i < fragments.length; i++) {
+			fragments[i] = new FragmentEntry((String) fragmentBundles[i]
+					.getHeaders().get(Constants.BUNDLE_SYMBOLICNAME),
+					(String) fragmentBundles[i].getHeaders().get(
+							Constants.BUNDLE_VERSION), Platform
+							.getResourceString(fragmentBundles[i],
+									(String) fragmentBundles[i].getHeaders()
+											.get(Constants.BUNDLE_VERSION)),
+					fragmentBundles[i].getLocation());
+		}
+		return fragments;	
+	}
+	
+	public static String getWritableXMLString(String value) {
+		StringBuffer buf = new StringBuffer();
+		if(value == null)
+			return buf.toString();
+		for (int i = 0; i < value.length(); i++) {
+			char c = value.charAt(i);
+			switch (c) {
+				case '&' :
+					buf.append("&amp;"); //$NON-NLS-1$
+					break;
+				case '<' :
+					buf.append("&lt;"); //$NON-NLS-1$
+					break;
+				case '>' :
+					buf.append("&gt;"); //$NON-NLS-1$
+					break;
+				case '\'' :
+					buf.append("&apos;"); //$NON-NLS-1$
+					break;
+				case '\"' :
+					buf.append("&quot;"); //$NON-NLS-1$
+					break;
+				case 0x00 :
+					buf.append(" "); //$NON-NLS-1$
+					break;						
+				default :
+					buf.append(c);
+					break;
+			}
+		}
+		return buf.toString();
+	}
+	
+	public static LiteFeature[] getLightFeatures(ExtendedSite site) {
+		
+		URL fullDigestURL;
+		try {
+			fullDigestURL = getFullDigestURL( site, Locale.getDefault().getCountry(), Locale.getDefault().getLanguage());
+		} catch (MalformedURLException e) {
+			UpdateCore.log("Could not access digest on the site: " + e.getMessage(), null); //$NON-NLS-1$
+			return null;
+		}
+		
+		Digest digest = new Digest( fullDigestURL);
+		try {
+			LiteFeature[] features =  (LiteFeature[])digest.parseDigest();
+			for(int i = 0; i < features.length; i++) {
+				features[i].setSite(site);
+			}
+			return features;
+		} catch(Exception e){ 
+			UpdateCore.log("Digest could not be parsed:" + e.getMessage(), null); //$NON-NLS-1$
+			return null;
+		}
+	}
+	
+	private static URL getFullDigestURL(ExtendedSite site, String country, String language) throws MalformedURLException {
+		
+		String digestURL = (site.getDigestURL().endsWith("/")? site.getDigestURL(): site.getDigestURL() + "/"); //$NON-NLS-1$ //$NON-NLS-2$ 
+		
+		if (digestURL.indexOf("://") == -1) { //$NON-NLS-1$
+			String siteURL = site.getLocationURL().toExternalForm();
+			if (siteURL.endsWith(Site.SITE_XML)) {
+				siteURL = siteURL.substring(0, siteURL.length() - Site.SITE_XML.length());
+			} 
+			if (digestURL.equals("/")) { //$NON-NLS-1$
+				digestURL = siteURL;
+			} else {
+				if (digestURL.startsWith("/")) { //$NON-NLS-1$
+					digestURL = digestURL.substring(1, digestURL.length());
+				}
+				digestURL = siteURL + digestURL;
+			}
+		}
+		
+		digestURL += "digest";  //$NON-NLS-1$
+		
+		if ( isLocalSupported(site, country, language)) {
+			return new URL(digestURL + "_" + language + "_" + country + ".zip"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+		if ( isLangaugeSupported(site, language)) {
+			return new URL(digestURL + "_" + language + ".zip"); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		return new URL(digestURL + ".zip"); //$NON-NLS-1$
+	}
+
+	private static boolean isLangaugeSupported(ExtendedSite site, String language) {
+		String[] availableLanguages =  site.getAvailableLocals();
+		if ((availableLanguages == null) || (availableLanguages.length == 0)) {
+			return false;
+		}
+		for(int i = 0; i < availableLanguages.length; i++) {
+			if (availableLanguages[i].equals(language)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private static boolean isLocalSupported(ExtendedSite site, String country, String language) {
+		String localeCode = language + "_" + country; //$NON-NLS-1$
+		String[] availableLocals =  site.getAvailableLocals();
+		if ((availableLocals == null) || (availableLocals.length == 0)) {
+			return false;
+		}
+		for(int i = 0; i < availableLocals.length; i++) {
+			if (availableLocals[i].equals(localeCode)) {
+				return true;
+			}
+		}
+		return false;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdatePreferencesInitializer.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdatePreferencesInitializer.java
new file mode 100644
index 0000000..3d5a5db
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdatePreferencesInitializer.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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.update.internal.core;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+
+/**
+ * Class to initialize the preferences for the Update core plugin.
+ */
+public class UpdatePreferencesInitializer extends AbstractPreferenceInitializer {
+
+	/**
+	 * Default constructor
+	 */
+	public UpdatePreferencesInitializer() {
+		super();
+	}
+
+	/**
+	 * Initializes the default preferences settings for this plug-in.
+	 * <p>
+	 * This method is called sometime after the preference store for this
+	 * plug-in is created. Default values are never stored in preference
+	 * stores; they must be filled in each time. This method provides the
+	 * opportunity to initialize the default values.
+	 * </p>
+	 * <p>
+	 * The default implementation of this method does nothing. A subclass that needs
+	 * to set default values for its preferences must reimplement this method.
+	 * Default values set at a later point will override any default override
+	 * settings supplied from outside the plug-in (product configuration or
+	 * platform start up).
+	 * </p>
+	 */
+	public void initializeDefaultPreferences() {
+		Plugin plugin = UpdateCore.getPlugin();
+		plugin.getPluginPreferences().setDefault(UpdateCore.P_CHECK_SIGNATURE, true);
+		plugin.getPluginPreferences().setDefault(UpdateCore.P_AUTOMATICALLY_CHOOSE_MIRROR, false);
+		plugin.getPluginPreferences().setDefault(UpdateCore.P_HISTORY_SIZE, UpdateCore.DEFAULT_HISTORY);
+		plugin.getPluginPreferences().setDefault(UpdateCore.P_UPDATE_VERSIONS, UpdateCore.EQUIVALENT_VALUE);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSession.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSession.java
new file mode 100644
index 0000000..9b2cb64
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSession.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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 - Initial API and implementation
+ *     James D Miles (IBM Corp.) - bug 191783, NullPointerException in FeatureDownloader
+ *******************************************************************************/
+
+package org.eclipse.update.internal.core;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class UpdateSession {
+	
+	private boolean enabled = false;
+	private Set visitedURLs = Collections.synchronizedSet(new HashSet());
+	
+	UpdateSession() {
+	}
+	
+	public boolean isVisited(URL url) {
+		if (!enabled)
+			return false;
+		return visitedURLs.contains(url.toExternalForm());
+	}
+
+	public void markVisited(URL url) {
+		if (!enabled)
+			return ;
+		visitedURLs.add(url.toExternalForm());
+	}
+	
+	/*
+	 * Session will not start caching URLs prior to calling this
+	 * method. If you want to use update session facility make sure
+	 * you call this method first
+	 */
+	public void reset() {
+		this.enabled = true;
+		visitedURLs.clear();
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSiteFeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSiteFeatureReference.java
new file mode 100644
index 0000000..1a906a5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSiteFeatureReference.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+import org.eclipse.update.core.*;
+
+/**
+ * Feature reference on an update site.
+ * @since 3.1
+ */
+public class UpdateSiteFeatureReference extends SiteFeatureReference {
+    private String os;
+    private String ws;
+    private String nl;
+    private String arch;
+    private String patch;  
+
+    /**
+     * Get optional operating system specification as a comma-separated string.
+     *
+     * @return the operating system specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getOS() {
+        return os;
+    }
+
+
+    /**
+     * Get optional windowing system specification as a comma-separated string.
+     *
+     * @return the windowing system specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getWS() {
+        return ws;
+    }
+
+
+    /**
+     * Get optional system architecture specification as a comma-separated string.
+     *
+     * @return the system architecture specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getOSArch() {
+        return arch;
+    }
+
+
+    /**
+     * Get optional locale specification as a comma-separated string.
+     *
+     * @return the locale specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getNL() {
+        return nl;
+    }
+
+    /**
+     * Sets the operating system specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param os operating system specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setOS(String os) {
+        assertIsWriteable();
+        this.os = os;
+    }
+
+
+    /**
+     * Sets the windowing system specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param ws windowing system specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setWS(String ws) {
+        assertIsWriteable();
+        this.ws = ws;
+    }
+
+
+    /**
+     * Sets the locale specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param nl locale specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setNL(String nl) {
+        assertIsWriteable();
+        this.nl = nl;
+    }
+
+
+    /**
+     * Sets the system architecture specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param arch system architecture specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setArch(String arch) {
+        assertIsWriteable();
+        this.arch = arch;
+    }
+
+    /**
+     * Returns the patch mode.
+     * @since 3.1
+     */
+    public String getPatch() {
+        return patch;
+    }
+
+
+    /**
+     * Sets the patch mode.
+     * @since 3.1
+     */
+    public void setPatch(String patch) {
+        this.patch = patch;
+    }
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSiteIncludedFeatureReference.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSiteIncludedFeatureReference.java
new file mode 100644
index 0000000..372d74f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/UpdateSiteIncludedFeatureReference.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+import org.eclipse.update.core.*;
+
+/**
+ * Feature reference on an update site.
+ * @since 3.1
+ */
+public class UpdateSiteIncludedFeatureReference extends IncludedFeatureReference{
+    private String os;
+    private String ws;
+    private String nl;
+    private String arch;
+    private String patch;
+ 
+ public UpdateSiteIncludedFeatureReference() {
+  super();
+ }
+
+    public UpdateSiteIncludedFeatureReference(IIncludedFeatureReference include) {
+  super(include);
+ }
+
+
+ /**
+     * Get optional operating system specification as a comma-separated string.
+     *
+     * @return the operating system specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getOS() {
+        return os;
+    }
+
+
+    /**
+     * Get optional windowing system specification as a comma-separated string.
+     *
+     * @return the windowing system specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getWS() {
+        return ws;
+    }
+
+
+    /**
+     * Get optional system architecture specification as a comma-separated string.
+     *
+     * @return the system architecture specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getOSArch() {
+        return arch;
+    }
+
+
+    /**
+     * Get optional locale specification as a comma-separated string.
+     *
+     * @return the locale specification string, or <code>null</code>.
+     * @since 3.1
+     */
+    public String getNL() {
+        return nl;
+    }
+
+    /**
+     * Sets the operating system specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param os operating system specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setOS(String os) {
+        assertIsWriteable();
+        this.os = os;
+    }
+
+
+    /**
+     * Sets the windowing system specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param ws windowing system specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setWS(String ws) {
+        assertIsWriteable();
+        this.ws = ws;
+    }
+
+
+    /**
+     * Sets the locale specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param nl locale specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setNL(String nl) {
+        assertIsWriteable();
+        this.nl = nl;
+    }
+
+
+    /**
+     * Sets the system architecture specification.
+     * Throws a runtime exception if this object is marked read-only.
+     *
+     * @param arch system architecture specification as a comma-separated list
+     * @since 3.1
+     */
+    public void setArch(String arch) {
+        assertIsWriteable();
+        this.arch = arch;
+    }
+
+    /**
+     * Returns the patch mode.
+     * @since 3.1
+     */
+    public String getPatch() {
+        return patch;
+    }
+
+
+    /**
+     * Sets the patch mode.
+     * @since 3.1
+     */
+    public void setPatch(String patch) {
+        this.patch = patch;
+    }
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Volume.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Volume.java
new file mode 100644
index 0000000..6bd66de
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/Volume.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core;
+
+import java.io.File;
+
+import org.eclipse.update.configuration.IVolume;
+import org.eclipse.update.core.model.ModelObject;
+
+/**
+ * Default implementation of a IVolume
+ */
+public class Volume extends ModelObject implements IVolume {
+
+	private long size;
+	private int type;
+	private String label;
+	private File root;
+
+	/**
+	 * Constructor for Volume.
+	 */
+	public Volume(File root,String label,int type,long size) {
+		super();
+		this.root = root;
+		this.label = label;
+		this.type = type;
+		this.size = size;
+	}
+
+	/**
+	 * @see IVolume#getFreeSpace()
+	 */
+	public long getFreeSpace() {
+		return size;
+	}
+
+	/**
+	 * @see IVolume#getLabel()
+	 */
+	public String getLabel() {
+		return label;
+	}
+
+	/**
+	 * @see IVolume#getType()
+	 */
+	public int getType() {
+		return type;
+	}
+
+	/**
+	 * @see IVolume#getFile()
+	 */
+	public File getFile() {
+		return root;
+	}
+
+	public Object getAdapter(Class arg0) {
+		return null;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/AbstractResponse.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/AbstractResponse.java
new file mode 100644
index 0000000..c82bb32
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/AbstractResponse.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.update.internal.core.connection;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * @author btripkov
+ *
+ */
+public abstract class AbstractResponse implements IResponse {
+
+	private static final long POLLING_INTERVAL = 200;
+	protected URLConnection connection;
+
+	protected InputStream openStreamWithCancel(URLConnection urlConnection, IProgressMonitor monitor) throws IOException, CoreException, TooManyOpenConnectionsException {
+	
+		ConnectionThreadManager.StreamRunnable runnable =
+			new ConnectionThreadManager.StreamRunnable(urlConnection);
+		Thread t = ConnectionThreadManagerFactory.getConnectionManager().getConnectionThread(
+				runnable);
+		t.start();
+		InputStream is = null;
+		try {
+			for (;;) {
+				if (monitor.isCanceled()) {
+					runnable.disconnect();
+	                connection = null;
+					break;
+				}
+				if (runnable.getInputStream() != null || !t.isAlive()) {
+					is = runnable.getInputStream();
+					break;
+				}
+				if (runnable.getIOException() != null) 
+					throw runnable.getIOException();
+				if (runnable.getException() != null) 
+						throw new CoreException(new Status(IStatus.ERROR,
+															UpdateCore.getPlugin().getBundle().getSymbolicName(), 
+															IStatus.OK,
+															runnable.getException().getMessage(), 
+															runnable.getException()));
+				t.join(POLLING_INTERVAL);
+				}
+		} catch (InterruptedException e) {
+		}
+		return is;
+	}
+
+	
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionFactory.java
new file mode 100644
index 0000000..6c2e3e0
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionFactory.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core.connection;
+
+import java.io.IOException;
+import java.net.URL;
+
+public class ConnectionFactory {
+
+	
+	public static IResponse get(URL url) throws IOException {
+		//Request request = null;
+		IResponse response = null;
+		
+		if ("file".equals(url.getProtocol())) { //$NON-NLS-1$
+			response = new FileResponse(url);
+		} else if (url != null && url.getProtocol().startsWith("http")) { //$NON-NLS-1$
+			response = new HttpResponse(url);
+		} else {
+			response = new OtherResponse(url);
+		}
+
+		return response;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionThreadManager.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionThreadManager.java
new file mode 100644
index 0000000..d747cac
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionThreadManager.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.core.connection;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * This class manages threads that are dispatched to 
+ * obtained a valid input stream from an HTTP connection.
+ * Since obtaining an input stream is an I/O operation
+ * that may block for a long time, it is performed
+ * on a separate thread to keep the UI responsive.
+ * <p>
+ * In case that a connection blocks and does not
+ * terminate with an IOException after a timeout, 
+ * active threads may accumulate. The manager will
+ * refuse to create more than MAX_COUNT threads and
+ * instead will throw a CoreException with a child
+ * status object for each connection that is still pending.
+ * <p>
+ * If the connection is responsive but slow, the user
+ * may cancel it. In that case, the manager will
+ * close the stream to avoid resource leak.
+ */
+public class ConnectionThreadManager {
+	
+	// set connection timeout to 1 minute
+	private static final String CONNECT_TIMEOUT = "60000"; //$NON-NLS-1$
+	// set read timeout to 1 minute
+	private static final String READ_TIMEOUT = "60000"; //$NON-NLS-1$
+	// max number of active threads
+	private static final int MAX_COUNT = 9;
+	private List threads = new ArrayList(MAX_COUNT);
+	
+	
+
+	public static class StreamRunnable implements Runnable {
+		
+		private URLConnection urlConnection;
+		private IOException ioException;
+		private Exception exception;
+		private InputStream is;
+		private boolean disconnected;
+
+		public StreamRunnable(URLConnection urlConnection) {
+			this.urlConnection = urlConnection;
+		}
+
+		public InputStream getInputStream() {
+			return is;
+		}
+
+		public URL getURL() {
+			return urlConnection.getURL();
+		}
+
+		public IOException getIOException() {
+			return ioException;
+		}
+		
+		public Exception getException() {
+			return exception;
+		}
+
+		public void disconnect() {
+			if (urlConnection instanceof HttpURLConnection)
+				((HttpURLConnection)urlConnection).disconnect();
+			disconnected = true;
+		}
+
+		public void run() {
+			try {
+				is = urlConnection.getInputStream();
+				if (disconnected) {
+					// The connection was slow, but returned
+					// a valid input stream. However,
+					// the user canceled the connection
+					// so we must close to avoid 
+					// resource leak.
+					if (is != null) {
+						try {
+							is.close();
+						} catch (IOException ex) {
+							// at this point, we don't care
+						} finally {
+							is = null;
+						}
+					}
+				}
+			} catch (IOException e) {
+				ioException = e;
+			} catch (Exception e) {
+				exception = e;
+			} finally {
+				//threads.
+			}
+		}
+	}
+
+	
+	class ConnectionThread extends Thread {
+		
+		private StreamRunnable runnable;
+		
+		public ConnectionThread(StreamRunnable runnable) {
+			super(runnable, "update-connection"); //$NON-NLS-1$
+			this.runnable = runnable;
+		}
+
+		public StreamRunnable getRunnable() {
+			return runnable;
+		}
+	}
+
+	public ConnectionThreadManager() {
+		// In case we are running Sun's code.
+		setIfNotDefaultProperty("sun.net.client.defaultConnectTimeout", CONNECT_TIMEOUT); //$NON-NLS-1$
+		setIfNotDefaultProperty("sun.net.client.defaultReadTimeout", READ_TIMEOUT);  //$NON-NLS-1$
+	}
+	
+	private void setIfNotDefaultProperty(String key, String value) {
+		String oldValue = System.getProperty(key);
+		if (oldValue==null || oldValue.equals("-1")) //$NON-NLS-1$
+			System.setProperty(key, value);
+	}
+
+	public Thread getConnectionThread(StreamRunnable runnable) throws CoreException {
+		
+		validateExistingThreads();
+	
+		//if (threads == null)
+		//	threads = new Vector();
+		Thread t = new Thread(runnable);
+		t.setDaemon(true);
+		threads.add(t);
+		return t;
+	}
+
+	/*
+	 * Removes threads that are not alive any more from the 
+	 * list and ensures that there are at most MAX_COUNT threads
+	 * still working.
+	 */
+	private void validateExistingThreads() throws CoreException {
+		
+		if ((threads == null) || (threads.size() == 0))
+			return;
+		
+		int aliveCount = purgeTerminatedThreads();
+
+		if (aliveCount > MAX_COUNT) {
+			ArrayList children = new ArrayList();
+			String pluginId =
+				UpdateCore.getPlugin().getBundle().getSymbolicName();
+			for (int i = 0; i < threads.size(); i++) {
+				ConnectionThread t = (ConnectionThread) threads.get(i);
+				String url = t.getRunnable().getURL().toString();
+				IStatus status =
+					new Status(
+						IStatus.ERROR,
+						pluginId,
+						IStatus.OK,
+						NLS.bind(Messages.ConnectionThreadManager_unresponsiveURL, (new String[] { url })),
+						null);
+				children.add(status);
+			}
+			MultiStatus parentStatus =
+				new MultiStatus(
+					pluginId,
+					IStatus.OK,
+					(IStatus[]) children.toArray(new IStatus[children.size()]),
+					Messages.ConnectionThreadManager_tooManyConnections, 
+					null);
+			throw new CoreException(parentStatus);
+		}
+	}
+	
+	/*
+	 * Removes terminated threads from the list and returns
+	 * the number of those still active.
+	 */
+	
+	private int purgeTerminatedThreads() {
+		
+		if (threads.size() == 0) {
+			return 0;
+		}
+		
+		int aliveCount = 0;
+
+		Object[] array = threads.toArray();
+		for (int i = 0; i < array.length; i++) {
+			Thread t = (Thread) array[i];
+			if (!t.isAlive())
+				threads.remove(t);
+			else
+				aliveCount++;
+		}
+		return aliveCount;
+	}
+
+	public void shutdown() {
+		// We might want to kill the active threads but
+		// this is not really necessary since they are all
+		// daemons and will not prevent JVM to terminate.
+		threads.clear();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionThreadManagerFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionThreadManagerFactory.java
new file mode 100644
index 0000000..3257ead
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/ConnectionThreadManagerFactory.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core.connection;
+
+public class ConnectionThreadManagerFactory {
+
+	
+	//Connection manager
+	private static ConnectionThreadManager connectionManager;
+	
+	/**
+	 * Returns the manager that manages URL connection threads.
+	 */
+	public static ConnectionThreadManager getConnectionManager() {
+		
+		if (connectionManager == null)
+			connectionManager = new ConnectionThreadManager();
+		return connectionManager;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/FileResponse.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/FileResponse.java
new file mode 100644
index 0000000..6facd86
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/FileResponse.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core.connection;
+import org.eclipse.update.internal.core.UpdateCore;
+
+import java.io.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+
+public class FileResponse implements IResponse {
+
+	protected URL url;
+	protected long lastModified;
+
+	protected FileResponse(URL url) {
+		this.url = url;
+	}
+
+	public InputStream getInputStream() throws IOException {
+		return url.openStream();
+	}
+
+	public InputStream getInputStream(IProgressMonitor monitor)
+		throws IOException, CoreException {
+		return getInputStream();
+	}
+
+	public long getContentLength() {
+		return 0;
+	}
+
+	public int getStatusCode() {
+		return UpdateCore.HTTP_OK;
+	}
+	
+	public void close() {
+		// nothing to close
+	}
+
+	public String getStatusMessage() {
+		return ""; //$NON-NLS-1$
+	}
+
+	public long getLastModified() {
+		if (lastModified == 0) {
+			File f = new File(url.getFile());
+			if (f.isDirectory())
+				f = new File(f, "site.xml"); //$NON-NLS-1$
+			lastModified = f.lastModified();
+		}
+		return lastModified;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/HttpResponse.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/HttpResponse.java
new file mode 100644
index 0000000..a3cfa3a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/HttpResponse.java
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core.connection;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+
+public class HttpResponse extends AbstractResponse {
+	/**
+	 * Monitored InputStream.  Upon IOException, discards
+	 * connection so it is not reused.
+	 *
+	 */
+	private class MonitoringInputStream extends FilterInputStream {
+		private URLConnection connection;
+
+		public MonitoringInputStream(InputStream in, URLConnection connection) {
+			super(in);
+			this.connection = connection;
+		}
+
+		public int available() throws IOException {
+			try {
+				return in!=null?super.available():0;
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+		}
+
+		public void close() throws IOException {
+			try {
+				if (in!=null)
+					super.close();
+				if (connection instanceof HttpURLConnection) {
+					((HttpURLConnection)connection).disconnect();
+				}
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+		}
+
+		public int read() throws IOException {
+			try {
+				return in!=null?super.read():-1;
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+		}
+
+		public synchronized void reset() throws IOException {
+			try {
+				if (in!=null)
+					super.reset();
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+		}
+
+		public int read(byte[] b) throws IOException {
+			try {
+				return in!=null?super.read(b):-1;
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+		}
+
+		public int read(byte[] b, int off, int len) throws IOException {
+			try {
+				return in!=null?super.read(b, off, len):-1;
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+		}
+
+		public long skip(long n) throws IOException {
+			try {
+				return in!=null?super.skip(n):0;
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+		}
+
+	}
+	
+	protected URL url;
+	protected InputStream in;
+	protected long lastModified;
+	protected long offset;
+
+	protected HttpResponse(URL url) {
+		
+        this.url = url;
+	}
+
+	public InputStream getInputStream() throws IOException {
+		if (in == null && url != null) {
+			if (connection == null || offset > 0)
+				connection = url.openConnection();
+			if (offset > 0)
+				connection.setRequestProperty("Range", "bytes=" + offset + "-"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			try {
+				in = new MonitoringInputStream(connection.getInputStream(), connection);
+			} catch (IOException ioe) {
+				connection = null;
+				throw ioe;
+			}
+			checkOffset();
+		}
+		return in;
+	}
+	
+	public void close() {
+        if( null != in ) {
+                try {
+					in.close();
+				} catch (IOException e) {
+				}
+                in = null;
+        }
+        if (connection!=null) {
+        	((HttpURLConnection)connection).disconnect();
+        	connection = null;
+        }
+	}
+	
+	/**
+	 * @see IResponse#getInputStream(IProgressMonitor)
+	 */
+	public InputStream getInputStream(IProgressMonitor monitor)
+		throws IOException, CoreException, TooManyOpenConnectionsException {
+		if (in == null && url != null) {
+			if (connection == null || offset > 0)
+				connection = url.openConnection();
+			if (offset > 0)
+				connection.setRequestProperty("Range", "bytes=" + offset + "-"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+			if (monitor != null) {
+				try {
+					this.in = new MonitoringInputStream(openStreamWithCancel(
+							connection, monitor), connection);
+				} catch (IOException ioe) {
+					connection = null;
+					throw ioe;
+				}
+			} else {
+				try {
+					this.in = new MonitoringInputStream(connection
+							.getInputStream(), connection);
+				} catch (IOException ioe) {
+					connection = null;
+					throw ioe;
+				}
+			}
+			// this can also be run inside a monitoring thread, but it is safe
+			// to
+			// just call it now, if the input stream has already been obtained
+			checkOffset();
+			if (connection != null) {
+				this.lastModified = connection.getLastModified();
+			}
+		}
+		return in;
+	}
+
+	public long getContentLength() {
+		if (connection != null)
+			return connection.getContentLength();
+		return 0;
+	}
+
+	public int getStatusCode() {
+		if (connection == null)
+			try {
+				connection = url.openConnection();
+			} catch (IOException e) {
+			}
+		if (connection != null) {
+			try {
+				return ((HttpURLConnection) connection).getResponseCode();
+			} catch (IOException e) {
+				UpdateCore.warn("", e); //$NON-NLS-1$
+			}
+		}
+		return UpdateCore.HTTP_OK;
+	}
+
+	public String getStatusMessage() {
+		if (connection != null) {
+			try {
+				return ((HttpURLConnection) connection).getResponseMessage();
+			} catch (IOException e) {
+				UpdateCore.warn("", e); //$NON-NLS-1$
+			}
+		}
+		return ""; //$NON-NLS-1$
+	}
+
+	public long getLastModified() {
+		if (lastModified == 0) {
+			if (connection == null)
+				try {
+					connection = url.openConnection();
+				} catch (IOException e) {
+				}
+			if (connection != null)
+				lastModified = connection.getLastModified();
+		}
+		return lastModified;
+	}
+
+	public void setOffset(long offset) {
+		this.offset = offset;
+	}
+	private void checkOffset() throws IOException {
+		if (offset == 0)
+			return;
+		String range = connection.getHeaderField("Content-Range"); //$NON-NLS-1$
+		//System.out.println("Content-Range=" + range);
+		if (range == null) {
+			//System.err.println("Server does not support ranges");
+			throw new IOException(Messages.HttpResponse_rangeExpected); 
+		} else if (!range.startsWith("bytes " + offset + "-")) { //$NON-NLS-1$ //$NON-NLS-2$
+			//System.err.println("Server returned wrong range");
+			throw new IOException(Messages.HttpResponse_wrongRange); 
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/IResponse.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/IResponse.java
new file mode 100644
index 0000000..f37b819
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/IResponse.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.update.internal.core.connection;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public interface IResponse {
+
+	/**
+	 * Method getInputStream.
+	 * 
+	 * @return InputStream
+	 */
+	public InputStream getInputStream() throws IOException;
+
+	/**
+	 * A special version of 'getInputStream' that can be canceled.
+	 * A monitor thread checks the state of the monitor
+	 * and disconnects the connection if 'isCanceled()' is detected.
+	 * 
+	 * @param monitor
+	 *            the progress monitor
+	 * @return InputStream an opened stream or null if failed.
+	 * @throws IOException
+	 *             if there are problems
+	 * @throws CoreException
+	 *             if no more connection threads are available
+	 */
+	public InputStream getInputStream(IProgressMonitor monitor)
+		throws IOException, CoreException, TooManyOpenConnectionsException;
+
+	/**
+	 * Method getContentLength.
+	 * 
+	 * @return long
+	 */
+	public long getContentLength();
+
+	/**
+	 * Method getStatusCode.
+	 * 
+	 * @return int
+	 */
+	public int getStatusCode();
+
+	/**
+	 * Method getStatusMessage.
+	 * 
+	 * @return String
+	 */
+	public String getStatusMessage();
+
+	/**
+	 * Returns the timestamp of last modification to the resource
+	 * 
+	 * @return
+	 */
+	public long getLastModified();
+	
+	/**
+	 * Close the connection if open.
+	 */
+	public void close();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/OtherResponse.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/OtherResponse.java
new file mode 100644
index 0000000..fe9d82e
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/OtherResponse.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.core.connection;
+
+import org.eclipse.update.internal.core.UpdateCore;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class OtherResponse extends AbstractResponse {
+
+	protected URL url;
+	protected InputStream in;
+	protected long lastModified;
+
+	protected OtherResponse(URL url) {
+		this.url = url;
+	}
+
+	public InputStream getInputStream() throws IOException {
+		if (in == null && url != null) {
+            if (connection == null)
+                connection = url.openConnection();
+			in = connection.getInputStream();
+			this.lastModified = connection.getLastModified();
+		}
+		return in;
+	}
+	
+	public void close() {
+        if( null != in ) {
+                try {
+					in.close();
+				} catch (IOException e) {
+				}
+                in = null;
+        }
+	}	
+	
+	/**
+	 * @see IResponse#getInputStream(IProgressMonitor)
+	 */
+	public InputStream getInputStream(IProgressMonitor monitor)
+		throws IOException, CoreException {
+		if (in == null && url != null) {
+            if (connection == null)
+                connection = url.openConnection();
+
+			if (monitor != null) {
+				this.in =
+					openStreamWithCancel(connection, monitor);
+			} else {
+				this.in = connection.getInputStream();
+			}
+			if (in != null) {
+				this.lastModified = connection.getLastModified();
+			}
+		}
+		return in;
+	}
+
+	public long getContentLength() {
+		if (connection != null)
+			return connection.getContentLength();
+		return 0;
+	}
+
+	public int getStatusCode() {
+		return UpdateCore.HTTP_OK;
+	}
+
+	public String getStatusMessage() {
+		return ""; //$NON-NLS-1$
+	}
+
+	public long getLastModified() {
+		if (lastModified == 0 && connection != null) {
+			lastModified = connection.getLastModified();
+		}
+		return lastModified;
+	}
+	
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/TooManyOpenConnectionsException.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/TooManyOpenConnectionsException.java
new file mode 100644
index 0000000..a8f846e
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/connection/TooManyOpenConnectionsException.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.core.connection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+
+public class TooManyOpenConnectionsException extends CoreException {
+
+	private static final long serialVersionUID = 1L;
+	
+	public TooManyOpenConnectionsException(IStatus status) {
+		super(status);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/messages.properties b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/messages.properties
new file mode 100644
index 0000000..c8020cb
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/core/messages.properties
@@ -0,0 +1,279 @@
+###############################################################################
+# Copyright (c) 2000, 2009 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
+###############################################################################
+# Install Update Core Properties File
+#
+
+ProductProvider=Update Manager
+BaseSiteFactory_CannotRetriveParentDirectory=Unable to retrieve parent directory for URL \"{0}\".
+ContentReference_UnableToCreateInputStream= Unable to create an input stream for the reference \"{0}\".
+ContentReference_UnableToReturnReferenceAsFile= Unable to return reference \"{0}\" as File.
+ContentReference_UnableToReturnReferenceAsURL= Unable to return reference \"{0}\" as URL.
+ContentReference_HttpNok= Server returned HTTP response code: \"{0} {1}\" for URL: {2}.
+ContentReference_FileDoesNotExist=The file \"{0}\" does not exist on the file system.
+Feature_SiteAlreadySet= Internal Error: Site already set for the feature \"{0}\".
+Feature_TaskInstallFeatureFiles= Installing feature files:
+Feature_TaskInstallPluginFiles= Installing plug-in \"{0}\" files:
+Feature_NoContentProvider= Internal Error. The feature \"{0}\" does not have a content provider.
+Feature_NoFeatureContentConsumer= Internal Error. The Feature \"{0}\" does not have a content consumer.
+Feature_NoURL= Unknown URL.
+Feature_FeatureVersionToString= Feature: \"{0}\" Version: \"{1}\".
+Feature_InstallationCancelled= Installation has been cancelled by the User.
+Feature_UnableToInitializeFeatureReference= Unable to initialize feature reference for versioned identifier \"{0}\".
+FeatureContentProvider_Downloading= Downloading:
+FeatureContentProvider_UnableToRetrieve= Unable to retrieve remote reference \"{0}\".
+FeatureContentProvider_UnableToCreate= Unable to create local file \"{0}\".
+FeatureContentProvider_ExceptionDownloading = An exception occurred while downloading feature from \"{0}\".
+InstallHandler_unableToCreateHandler = Unable to create custom install handler for feature \"{0}\".
+InstallHandler_notFound = Custom install handler required by feature \"{0}\" was not found.
+InstallHandler_invalidHandler = Feature \"{0}\" contains invalid custom install handler.
+InstallHandler_callException = Error reported by custom install handler for feature \"{0}\".
+InstallHandler_error = Unable to complete action for feature \"{0}\" due to errors.
+InstallCommand_site=site {0}
+InstallMonitor_DownloadSize = ({0}K bytes)
+InstallMonitor_DownloadSizeLong = ({0}K of {1}K bytes)
+JarContentReference_Unpacking= Unpacking:
+Site_CannotFindCategory= Unable to find category: \"{0}\" in Site: \"{1}\".
+Site_NoCategories= The Site does not contain any categories.
+Site_NoContentProvider= Internal Error. Content Provider not set for Site: \"{0}\".
+VersionedIdentifier_IdOrVersionNull= Internal Error. Unable to create an Identifier. The unique identifier or the version is null or empty: unique identifier=\"{0}\" version=\"{1}\".
+SiteFile_CannotRemovePlugin= Unable to remove plug-in: \"{0}\" from Site: \"{1}\".
+SiteFile_CannotRemoveFeature= Unable to remove feature: \"{0}\" from Site: \"{1}\".
+SiteFile_UnableToCreateURL= Unable to create URL for location \"{0}\".
+SiteFile_Removing= Removing:
+SiteFileFactory_UnableToCreateURL= Internal Error. Unable to create URL from \"{0}\".
+SiteFileFactory_UnableToAccessSite= Unable to access site
+SiteFileFactory_DirectoryDoesNotExist=The directory \"{0}\" does not exist on the file system.
+DefaultFeatureParser_location= Feature location: {0}
+DefaultFeatureParser_NoFeatureTag= Error parsing feature stream. Unable to find root element \"feature\" in the stream.
+DefaultFeatureParser_WrongParsingStack= Internal Error parsing feature stream. Unexpected Parsing Stack: \"{0}\"
+DefaultFeatureParser_UnknownElement= Error parsing feature stream. Unknown element \"{0}\" in parsing state \"{1}\". Check the validity of the XML file.
+DefaultFeatureParser_TooManyURLtag= Error parsing feature stream. There is more than one \"URL\" element in the XML stream. There should be only one.
+DefaultFeatureParser_UnknownStartState= Internal Error parsing feature stream. Unknown start state \"{0}\".
+DefaultFeatureParser_IdOrVersionInvalid= Error parsing feature stream. The unique identifier or the version is null or empty for the State: \"{2}\": unique identifier=\"{0}\" version=\"{1}\".
+DefaultFeatureParser_MissingId= Error parsing feature stream. The \"id\" tag of the element \"{0}\" is null or empty. Value is required.
+DefaultFeatureParser_ParsingStackBackToInitialState= Internal Error parsing feature stream. Parsing stack back to Initial State.
+DefaultFeatureParser_ElementAlreadySet= Error parsing feature stream. Element: \"{0}\" already set for the feature.
+DefaultFeatureParser_StateIncludeWrongElement= Error parsing feature stream. State: \"{0}\" should not include Element \"{1}\".
+DefaultFeatureParser_RequireStateWithoutImportElement=Error parsing feature stream. State: \"require\" must include at least an \"import\" element.
+DefaultFeatureParser_MissingPatchVersion=Error parsing feature stream. Attribute "version" of the element "import" must be set when "patch" is set to "true".
+DefaultFeatureParser_wrongMatchForPatch=Error parsing feature stream. When attribute "patch" of the element "import" is set to "true", the only valid value for the "match" attribute is "perfect".
+DefaultFeatureParser_patchWithPlugin=Error parsing feature stream. Attribute "patch" can only be used for features.
+DefaultFeatureParser_MultiplePatchImports=Error parsing feature stream. State: "require" must not contain more than one "import" element with the attribute "patch" set to "true".
+DefaultFeatureParser_UnknownEndState= Internal Error parsing feature stream. Unknown end state \"{0}\".
+DefaultFeatureParser_ErrorParsing= Error Parsing feature stream. Error: \"{0}\"
+DefaultFeatureParser_ErrorlineColumnMessage= Error Parsing feature stream. Element \"{0}\" line: \"{1}\" column:\"{2}\". Error: \"{3}\".
+DefaultFeatureParser_ErrorParsingFeature= Error Parsing feature stream.
+DefaultFeatureParser_UnknownState= Unknown State \"{0}\".
+DefaultFeatureParser_NoLicenseText= Error parsing feature stream. State: \"license\" must include some text.
+DefaultFeatureParser_PluginAndFeatureId=One Import statement is incorrect: Both feature and plug-in ID are either null or specified.
+SiteContentProvider_ErrorCreatingURLForArchiveID= Internal Error. Unable to create URL for Archive Identifier \"{0}\" on Site \"{1}\".
+DefaultSiteParser_NoSiteTag= Error parsing site stream. Unable to find root element \"site\" in the stream.
+DefaultSiteParser_WrongParsingStack= Internal Error parsing site stream. Unexpected Parsing Stack: \"{0}\"
+DefaultSiteParser_UnknownElement= Error parsing site stream. Unknown element \"{0}\" in parsing state \"{1}\". Check the validity of the XML file.
+DefaultSiteParser_UnknownStartState= Internal Error parsing site stream. Unknown start state \"{0}\".
+DefaultSiteParser_Missing= Error parsing site stream. The \"{0}\" tag of the element \"{1}\" is null or empty. Value is required.
+DefaultSiteParser_ParsingStackBackToInitialState= Internal Error parsing site stream. Parsing stack back to Initial State.
+DefaultSiteParser_ElementAlreadySet= Error parsing site stream. Element: \"{0}\" already set for the Site.
+DefaultSiteParser_UnknownEndState= Internal Error parsing site stream. Unknown end state \"{0}\".
+DefaultSiteParser_ErrorParsing= Error Parsing site stream. Error: \"{0}\"
+DefaultSiteParser_ErrorlineColumnMessage= Error Parsing site stream. Element \"{0}\" line: \"{1}\" column:\"{2}\". Error: \"{3}\".
+DefaultSiteParser_ErrorParsingSite= Error Parsing site stream.
+DefaultSiteParser_UnknownState= Unknown State \"{0}\".
+DefaultSiteParser_InvalidXMLStream= The XML stream is not a valid default \"site.xml\" file. The root tag is not site.
+ModelObject_ModelReadOnly= Internal Error. The Model Object is read-only.
+SiteModelObject_ErrorParsingSiteStream= Error parsing site stream.
+SiteModelObject_ErrorAccessingSiteStream= Error accessing site stream.
+InstallConfiguration_ErrorDuringFileAccess= Error accessing file: \"{0}\".
+InstallConfigurationParser_FeatureReferenceNoURL= Error Parsing Stream. A feature reference element does not have a URL tag. Value is required.
+FeatureExecutableContentProvider_FileDoesNotExist= The file \"{0}\" does not exist.
+FeatureExecutableContentProvider_InvalidDirectory= The path \"{0}\" is not a valid directory.
+FeatureExecutableContentProvider_UnableToCreateURLFor=Unable to create a URL for \"{0}\".
+FeatureExecutableContentProvider_UnableToRetrieveNonPluginEntry=Error retrieving the non-plug-in entry: \"{0}\".
+FeatureExecutableContentProvider_UnableToRetrieveFeatureEntry=Error retrieving feature entry content reference: \"{0}\".
+FeatureExecutableContentProvider_UnableToRetrievePluginEntry =Error retrieving the plug-in entry: \"{0}\".
+ConfiguredSite_NonInstallableSite= You cannot install in the site \"{0}\". It is considered non updateable.
+ConfiguredSite_NullFeatureToInstall= Internal Error. The feature to be installed is null.
+ConfiguredSite_NonUninstallableSite= You cannot uninstall from the site \"{0}\". It is considered non updateable.
+ConfiguredSite_NoSite= No Site
+ConfiguredSite_CannotFindFeatureToUnconfigure= Internal Error. Unable to disable feature \"{0}\" on site \"{1}\". Cannot find feature.
+ConfiguredSite_CannotFindFeatureToConfigure= Internal Error. Unable to enable feature \"{0}\". Cannot find feature.
+ConfiguredSite_CannotFindPluginEntry= Unable to retrieve Plug-in Entry \"{0}\" on site \"{1}\". Cannot find plug-in entry. Do you want to Continue ?
+ConfiguredSite_MissingPluginsBrokenFeature= Plug-in \"{0}\" version \"{1}\" referenced by this feature is missing.
+ConfiguredSite_UnableToRemoveConfiguredFeature= Unable to remove a enabled feature: \"{0}\" You must disable the feature first.
+ConfiguredSite_UnableToFindFeature= Unable to Find the feature \"{0}\" on this site.
+ConfiguredSite_SiteURLNull=The Site URL is null.
+ConfiguredSite_NonLocalSite=The Site is not on the local file system. Cannot validate.
+ConfiguredSite_NotSameProductId=The site cannot be modified by this product. It is already associated with product: \"{0}\".
+ConfiguredSite_ContainedInAnotherSite=This site is contained in another site: \"{0}\".
+ConfiguredSite_ReadOnlySite=The site is marked as read only.
+ConfiguredSite_UnableResolveURL= Internal Error. Unable to resolve URL \"{0}\".
+ConfiguredSite_UnableToAccessSite= Internal Error. Unable to access platform site \"{0}\".
+FeatureFactory_CreatingError= Error creating feature \"{0}\".
+FeatureModelFactory_ErrorAccesingFeatureStream= Error accessing feature stream.
+FeatureExecutableFactory_NullURL= Error creating feature. The URL used to create the feature is null.
+FeatureExecutableFactory_CannotCreateURL= Error creating feature. Unable to create URL for \"{0}\".
+FeaturePackagedContentProvider_NoManifestFile= Unable to retrieve \"{0}\" in feature located at \"{1}\".
+FeaturePackagedContentProvider_InvalidDirectory= The directory \"{0}\" is not a valid directory.
+FeaturePackagedContentProvider_ErrorRetrieving= Error retrieving \"{0}\".
+FeatureReference_UnableToResolveURL= Internal Error: Unable to resolve model using URL: \"{0}\".
+FeatureTypeFactory_UnableToFindFeatureFactory= Unable to find a feature factory for the extension id \"{0}\".
+InstallConfiguration_UnableToCreateURL= Unable to create URL from \"{0}\".
+InstallConfiguration_UnableToCast= Internal Error. The Configuration Site object is not a subclass of ConfiguredSiteModel
+InstallConfiguration_UnableToSavePlatformConfiguration=Unable to save platform configuration in \"{0}\".
+InstallConfiguration_AlreadyNativelyLinked=The site is already linked.
+InstallConfiguration_AlreadyProductSite=The location is already a product site.
+InstallConfiguration_unableToFindSite=Unable to find site {0} in platform configuration {1}.
+InternalSiteManager_UnableToCreateSiteWithType= Extension Error. The Site Factory of type \"{0}\" cannot access the Site \"{1}\".
+InternalSiteManager_UnableToAccessURL= Unable to access \"{0}\".
+InternalSiteManager_UnableToCreateURL= Unable to create URL from \"{0}\".
+InternalSiteManager_FailedRetryAccessingSite= Failed retry accessing site using default installed format instead of default packaged format.
+InternalSiteManager_ConnectingToSite = Connecting To Site...
+GlobalConsumer_ErrorCreatingFile= Unable to create file \"{0}\".
+SiteFileContentConsumer_UnableToCreateURL= Unable to create URL from \"{0}\".
+SiteFileContentConsumer_UnableToCreateURLForFile= Unable to create URL for the file \"{0}\".
+SiteFileContentConsumer_unableToDelete=Unable to delete {0}
+ContentConsumer_UnableToRename = Internal Error. Unable to rename \"{0}\" to \"{1}\".
+SiteFileFactory_UnableToObtainParentDirectory= Unable obtain the parent directory from the file \"{0}\".
+SiteFileFactory_FileDoesNotExist= Unable to access site \"{0}\". The directory does not exist.
+SiteFileFactory_UnableToCreateURLForFile= Unable to create URL from \"{0}\".
+SiteFileFactory_ErrorParsingFile= Unable to parse file \"{0}\".
+SiteFileFactory_ErrorAccessing= Unable to access file \"{0}\".
+SiteTypeFactory_UnableToFindSiteFactory= Unable to find a site factory for the extension id \"{0}\".
+UpdateManagerUtils_UnableToRemoveFile= Unable to remove \"{0}\" from the file system.
+UpdateManagerUtils_FileAlreadyExists= Unable to create \"{0}\" on the file system. File already exists.
+SiteLocal_UnableToCreateURLFor= Unable to create URL from \"{0}\".
+SiteLocal_UnableToDetermineFeatureStatusSiteNull=Unable to determine status of feature\"{0}\" The site is NULL.
+SiteLocal_TwoVersionSamePlugin1=Plug-in: \"{0}\" version: \"{1}\" referenced by this feature is not included at runtime. Runtime includes plug-in version \"{2}\".
+SiteLocal_TwoVersionSamePlugin2=Plug-in: \"{0}\" version: \"{1}\" referenced by this feature is not included at runtime. Runtime includes plug-in version \"{2}\" supplied by feature \"{3}\" version \"{4}\".
+SiteLocal_FeatureUnHappy=The feature is not configured properly.
+SiteLocal_FeatureHappy=The feature is configured properly.
+SiteLocal_FeatureAmbiguous=The feature may not be configured properly.
+SiteLocal_NestedFeatureUnHappy=Included feature \"{0}\" version \"{1}\" contains problems.
+SiteLocal_NestedFeatureUnavailable=Included feature \"{0}\" is missing.
+SiteLocal_NoPluginVersion=No plug-in: \"{0}\" included at runtime.
+SiteLocal_UnableToDetermineFeatureStatusConfiguredSiteNull=Unable to determine status of feature\"{0}\" The Configured Site is NULL.
+SiteLocal_FeatureDisable=The feature is disabled.
+SiteLocal_FeatureStatusUnknown=Unknown state.
+SiteLocal_NestedFeatureDisable=Included feature \"{0}\" version \"{1}\" is disabled.
+SiteURLFactory_UnableToCreateURL= Internal Error. Unable to create a URL from \"{0}\".
+SiteURLFactory_UnableToAccessSiteStream= Unable to access site: \"{0}\"
+JarVerifier_Verify= Verifying: \"{0}\"
+JarVerifier_UnableToFindEncryption= Unable to find algorithm to verify integrity of KeyStore \"{0}\".
+JarVerifier_UnableToLoadCertificate= Unable to load one or multiples certificates in the KeyStore \"{0}\".
+JarVerifier_UnableToFindProviderForKeystore= Unable to find provider for the KeyStore type \"{0}\".
+JarVerifier_KeyStoreNotLoaded= Internal Error. KeyStore not Initialized/Loaded.
+JarVerifier_UnableToAccessJar= Unable to access JAR file \"{0}\".
+JarVerifier_InvalidFile= The File \"{0}\" is not a valid JAR file. It does not contain a Manifest.
+JarVerifier_InvalidJar= The File \"{0}\" is not a valid JAR file.
+JarVerificationResult_ValidBetween= Valid between \"{0}\" and \"{1}\".
+JarVerificationResult_ExpiredCertificate= * EXPIRED CERTIFICATE *
+JarVerificationResult_CertificateNotYetValid= * CERTIFICATE NOT YET VALID *
+JarVerificationResult_CertificateValid= Valid certificate.
+JarVerificationService_UnsucessfulVerification=Verification of feature unsuccessful. Installation cancelled.
+JarVerificationService_CancelInstall=Installation cancelled.
+UpdateManagerUtils_UnableToLog=Unable to access error recovery log file: \"{0}\".
+ConnectionThreadManager_tooManyConnections = There are too many network connections still active. \
+If you are experiencing network problems, try again later. If the error persists, restart the \
+application.
+ConnectionThreadManager_unresponsiveURL = Still waiting for response: {0}
+IncludedFeatureReference_featureUninstalled= Feature {0} is uninstalled.
+
+# Properties moved mostly from the ui plugin
+
+ActivityConstraints_warning=The current configuration contains errors and this operation can have unpredictable results.
+ActivityConstraints_rootMessage = Requested operation cannot be performed because it would invalidate the current configuration. See details for more information.
+ActivityConstraints_rootMessageInitial = Current configuration contains errors that are not corrected by the requested operation and more errors would be introduced. See details for more information.
+ActivityConstraints_beforeMessage = ----- Current configuration problems -----
+ActivityConstraints_afterMessage = ----- Configuration problems after the operation -----
+ActivityConstraints_platform = Resulting configuration does not contain the platform.
+ActivityConstraints_primary = Resulting configuration does not contain the primary feature.
+ActivityConstaints_prereq_plugin = plug-in
+ActivityConstaints_prereq_feature = feature
+ActivityConstraints_prereq = requires {0} "{1}".
+ActivityConstraints_prereqPerfect = requires {0} "{1} ({2})".
+ActivityConstraints_prereqEquivalent = requires {0} "{1} ({2})", or equivalent.
+ActivityConstraints_prereqCompatible = requires {0} "{1} ({2})", or compatible.
+ActivityConstraints_prereqGreaterOrEqual = requires {0} "{1} ({2})", or later version.
+ActivityConstraints_os = operating system does not match current environment.
+ActivityConstraints_ws = windowing system does not match current environment.
+ActivityConstraints_arch = platform architecture does not match current environment.
+ActivityConstraints_cycle = definition contains a nested features cycle: {0}_{1}prereq
+ActivityConstraints_childMessage = {0} ({1}) {2}
+ActivityConstraints_optionalChild = Feature included in another feature cannot be enabled if the parent is disabled. Enable the parent first.
+ActivityConstraints_exclusive = must be processed separately from other features.
+ActivityConstraints_noLicense = does not have a valid license agreement.
+ActivityConstraints_readOnly = The site is not updateable: {0}.
+ActivityConstraints_platformModified = Platform configuration has been modified outside this program. A restart is recommended.
+DuplicateConflictsDialog_conflict = Version {0} in {1}
+OperationsManager_error_old= {0}: Error while updating the old feature version
+OperationsManager_installing=Initializing...
+OperationsManager_error_uninstall= {0} : Error while uninstalling feature
+Search_networkProblems=Network connection problems encountered during search.
+InstallConfiguration_location_exists = Site already exists.
+InstallLogParser_errors = Errors generated while parsing installation history file.
+SiteLocal_cloneConfig=Error clonning current configuration.
+
+UninstallCommand_featureNotInstalledByUM=Feature not installed by Update Manager: {0}
+
+UpdateManagerUtils_inputStreamEnded=InputStream ended after {0} bytes (expected {1} )
+UpdateSearchRequest_loadingPolicy=Loading update policy ...
+UpdateManagerUtils_copy=UpdateManagerUtils copy() at offset:
+UpdatePolicy_parsePolicy=Errors while parsing update policy
+UpdatePolicy_policyExpected=' is expected.
+UpdateSearchRequest_searching=Searching...
+UpdateSearchRequest_contacting=Contacting {0} ...
+UpdateSearchRequest_checking=Checking {0} ...
+UpdatePolicy_invalidURL=invalid URL -
+UpdatePolicy_nameNoNull= cannot be null.
+UpdatePolicy_UpdatePolicy=Update Policy:
+SiteFile_featureNotRemoved=Feature {0} was not removed
+SiteFile_pluginNotRemoved=Plugin {0} was not removed
+ErrorRecoveryLog_noFiletoRemove=Unable to find file to remove:
+UpdatesSearchCategory_errorSearchingForUpdates=Error while initializing the search for new updates
+Standalone_siteConfigured=Site is already configured:
+Standalone_noSite=Cannot find site:
+Standalone_noSite3=No site specified
+Standalone_noConfiguredSite=Cannot find configured site:
+Standalone_installing=Installing feature
+Standalone_notFoundOrNewer=Feature {0} cannot be found on {1} \nor a newer version is already installed.
+Standalone_duplicate=Duplicate conflicts
+Standalone_installed= Feature {0} has successfully been installed
+Standalone_cannotInstall=Cannot install feature
+Standalone_noFeatures1=There are no unconfigured features with id {0}
+Standalone_noFeatures2=Cannot find unconfigured feature  {0} with version {1}
+Standalone_noFeatures3=There are no configured features with id {0}
+Standalone_noFeatures4=Cannot find configured feature  {0} with version {1}
+Standalone_noConfigSiteForFeature=There are no configured sites for feature {0}
+Standalone_invalidCmd=Invalid command:
+Standalone_connection = Connection error
+Standalone_searching = Searching on:
+Standalone_cmdFailed = Command failed. Please check log file {0} for details.
+Standalone_cmdFailedNoLog = Command failed.
+Standalone_cmdCompleteWithErrors = Command completed with errors. Please check log file {0} for details.
+Standalone_cmdOK = Command completed successfully.
+Standalone_updating = Begin updating
+Standalone_noUpdate = Feature {0} cannot be updated.
+Standalone_updated= Feature {0} has successfully been updated.
+SiteFilePluginContentConsumer_unableToDelete=Unable to delete {0}
+SiteFilePackedPluginContentConsumer_unableToDelete=Unable to delete {0}
+
+JarProcessor_unpackNotFound = The unpack200 command cannot be found.
+JarProcessor_noPackUnpack = Pack, repack or sign cannot be specified with unpack.
+JarProcessor_packNotFound = The pack200 command cannot be found.
+SiteOptimizer_inputNotSpecified = No input file was specified;
+SiteOptimizer_inputFileNotFound = The input file \"{0}\" was not found.
+
+HttpResponse_rangeExpected = Server does not support ranges.
+HttpResponse_wrongRange = Server returned wrong range.
+DefaultSiteParser_mirrors = Error processing update site mirror.
+FeatureExecutableContentProvider_UnableToRetriveArchiveContentRef = Unable to retrieve archive content reference
+SiteCategory_other_label = Other
+SiteCategory_other_description = Features found under this category are either not categorized or their categories could not be found on server.
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorCommand.java
new file mode 100644
index 0000000..9b039c7
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorCommand.java
@@ -0,0 +1,246 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.mirror;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.PluginVersionIdentifier;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFeatureReference;
+import org.eclipse.update.core.JarContentReference;
+import org.eclipse.update.core.SiteFeatureReference;
+import org.eclipse.update.core.SiteFeatureReferenceModel;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.core.model.InvalidSiteTypeException;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.standalone.ScriptedCommand;
+import org.eclipse.update.standalone.StandaloneUpdateApplication;
+
+/**
+ * Mirrors a remote site locally.
+ */
+public class MirrorCommand extends ScriptedCommand {
+
+	private String featureId;
+	private String featureVersion;
+	private String fromSiteUrl;
+	private String toSiteDir;
+	private String mirrorURL;
+	private boolean ignoreNonPresentPlugins = false;
+	private MirrorSite mirrorSite;
+
+	public MirrorCommand(
+		String featureId,
+		String featureVersion,
+		String fromSiteUrl,
+		String toSiteDir,
+		String mirrorUrl, 
+		String ignoreNonPresentPlugins) {
+		this.featureId = featureId;
+		this.featureVersion = featureVersion;
+		this.fromSiteUrl = fromSiteUrl;
+		this.toSiteDir = toSiteDir;
+		this.mirrorURL = mirrorUrl;
+		this.ignoreNonPresentPlugins = (ignoreNonPresentPlugins != null) && (ignoreNonPresentPlugins.equals("true")); //$NON-NLS-1$
+	}
+
+	/**
+	 * true if success
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		if (!validateParameters()) {
+			return false;
+		}
+
+		try {
+			if (getMirrorSite() == null)
+				return false;
+
+			URL remoteSiteUrl = new URL(fromSiteUrl);
+			ISite remoteSite =
+				SiteManager.getSite(remoteSiteUrl, new NullProgressMonitor());
+
+			ISiteFeatureReference featureReferencesToMirror[] =
+				findFeaturesToMirror(remoteSite);
+			if (featureReferencesToMirror.length == 0) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(
+					Utilities.newCoreException(
+						"No matching features found on " + remoteSiteUrl + ".", //$NON-NLS-1$ //$NON-NLS-2$
+						null));
+				return false;
+			}
+
+			mirrorSite.mirrorAndExpose(
+				remoteSite,
+				featureReferencesToMirror,
+				null,
+				mirrorURL);
+			return true;
+		} catch (MalformedURLException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		} catch (CoreException ce) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(ce);
+			return false;
+		} finally {
+			JarContentReference.shutdown();
+		}
+	}
+	private boolean validateParameters() {
+		if (fromSiteUrl == null || fromSiteUrl.length() <= 0) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(
+				Utilities.newCoreException("from parameter is missing.", null)); //$NON-NLS-1$
+			return false;
+		}
+		try {
+			new URL(fromSiteUrl);
+		} catch (MalformedURLException mue) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(
+				Utilities.newCoreException("from must be a valid URL", null)); //$NON-NLS-1$
+			return false;
+		}
+		if (toSiteDir == null || toSiteDir.length() <= 0) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(
+				Utilities.newCoreException("to parameter is missing.", null)); //$NON-NLS-1$
+			return false;
+		}
+		return true;
+	}
+	private MirrorSite getMirrorSite()
+		throws MalformedURLException, CoreException {
+		// Create mirror site
+		if (mirrorSite == null) {
+			if (toSiteDir != null) {
+				MirrorSiteFactory factory = new MirrorSiteFactory();
+				System.out.print("Analyzing features already mirrored ..."); //$NON-NLS-1$
+				try {
+					mirrorSite =
+						(MirrorSite) factory.createSite(new File(toSiteDir));
+					mirrorSite.setIgnoreNonPresentPlugins(ignoreNonPresentPlugins);
+				} catch (InvalidSiteTypeException iste) {
+				}
+				System.out.println("  Done."); //$NON-NLS-1$
+			}
+			if (mirrorSite == null) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(
+					Utilities.newCoreException(
+						"Mirror site at " + toSiteDir + " cannot be accessed.", //$NON-NLS-1$ //$NON-NLS-2$
+						null));
+				return null;
+			}
+		}
+		return mirrorSite;
+
+	}
+	/**
+	 * Returns subset of feature references on remote site
+	 * as specified by optional featureId and featureVersion
+	 * parameters
+	 * @param remoteSite
+	 * @return ISiteFeatureReference[]
+	 * @throws CoreException
+	 */
+	private ISiteFeatureReference[] findFeaturesToMirror(ISite remoteSite)
+		throws CoreException {
+		ISiteFeatureReference remoteSiteFeatureReferences[] =
+			remoteSite.getRawFeatureReferences();
+		SiteFeatureReferenceModel existingFeatureModels[] =
+			mirrorSite.getFeatureReferenceModels();
+		Collection featureReferencesToMirror = new ArrayList();
+
+		PluginVersionIdentifier featureVersionIdentifier = null;
+
+		if (featureId == null) {
+			System.out.println(
+				"Parameter feature not specified.  All features on the remote site will be mirrored."); //$NON-NLS-1$
+		}
+		if (featureVersion == null) {
+			System.out.println(
+				"Parameter version not specified.  All versions of features on the remote site will be mirrored."); //$NON-NLS-1$
+		} else {
+			featureVersionIdentifier =
+				new PluginVersionIdentifier(featureVersion);
+		}
+		for (int i = 0; i < remoteSiteFeatureReferences.length; i++) {
+			VersionedIdentifier remoteFeatureVersionedIdentifier =
+				remoteSiteFeatureReferences[i].getVersionedIdentifier();
+
+			if (featureId != null
+				&& !featureId.equals(
+					remoteFeatureVersionedIdentifier.getIdentifier())) {
+				// id does not match
+				continue;
+			}
+			if (featureVersionIdentifier != null
+				&& !featureVersionIdentifier.isPerfect(
+					remoteFeatureVersionedIdentifier.getVersion())) {
+				// version does not match
+				continue;
+			}
+
+			for (int e = 0; e < existingFeatureModels.length; e++) {
+				if (existingFeatureModels[e]
+					.getVersionedIdentifier()
+					.equals(remoteFeatureVersionedIdentifier)) {
+					System.out.println(
+						"Feature " //$NON-NLS-1$
+							+ remoteFeatureVersionedIdentifier
+							+ " already mirrored and exposed."); //$NON-NLS-1$
+					// feature already mirrored and exposed in site.xml
+					continue;
+				}
+			}
+
+			// Check feature type
+			String type =
+				((SiteFeatureReference) remoteSiteFeatureReferences[i])
+					.getType();
+			if (type != null
+				&& !ISite.DEFAULT_PACKAGED_FEATURE_TYPE.equals(type)) {
+				// unsupported
+				throw Utilities.newCoreException(
+					"Feature " //$NON-NLS-1$
+						+ remoteFeatureVersionedIdentifier
+						+ " is of type " //$NON-NLS-1$
+						+ type
+						+ ".  Only features of type " //$NON-NLS-1$
+						+ ISite.DEFAULT_PACKAGED_FEATURE_TYPE
+						+ " are supported.", //$NON-NLS-1$
+					null);
+			}
+
+			featureReferencesToMirror.add(remoteSiteFeatureReferences[i]);
+			System.out.println(
+				"Feature " //$NON-NLS-1$
+					+ remoteSiteFeatureReferences[i].getVersionedIdentifier()
+					+ " will be mirrored."); //$NON-NLS-1$
+		}
+		return (ISiteFeatureReference[]) featureReferencesToMirror.toArray(
+			new ISiteFeatureReference[featureReferencesToMirror.size()]);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorSite.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorSite.java
new file mode 100644
index 0000000..7c40fe3
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorSite.java
@@ -0,0 +1,822 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.mirror;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.ICategory;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureContentProvider;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.INonPluginEntry;
+import org.eclipse.update.core.IPluginEntry;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFeatureReference;
+import org.eclipse.update.core.IURLEntry;
+import org.eclipse.update.core.IVerificationListener;
+import org.eclipse.update.core.Site;
+import org.eclipse.update.core.SiteFeatureReferenceModel;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.core.model.CategoryModel;
+import org.eclipse.update.core.model.SiteModelFactory;
+import org.eclipse.update.core.model.URLEntryModel;
+import org.eclipse.update.internal.core.CoreExceptionWithRootCause;
+import org.eclipse.update.internal.core.FatalIOException;
+import org.eclipse.update.internal.core.FeaturePackagedContentProvider;
+import org.eclipse.update.internal.core.ISiteContentConsumer;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.standalone.StandaloneUpdateApplication;
+
+/**
+ * Local mirror site.  Read/Write
+ */
+public class MirrorSite extends Site {
+	private final static String INDENT = "   "; //$NON-NLS-1$
+	private SiteModelFactory factory;
+	/**
+	 * plugin entries 
+	 */
+	private Collection downloadedPluginEntries = new ArrayList();
+	private Collection downloadedFeatureReferenceModels = new ArrayList();
+	private boolean ignoreNonPresentPlugins;
+	public MirrorSite(SiteModelFactory factory) {
+		this.factory = factory;
+	}
+
+	/**
+	 * Mirrors the specified features and listed optional features on this site.
+	 * @see ISite#install(IFeature, IVerificationListener, IProgressMonitor)
+	 * @param mirrorSiteUrl external URL of the mirror site or null;
+	 * if parameter is provided policy fragment will be generated
+	 * @exception CoreException
+	 */
+	public void mirrorAndExpose(
+		ISite remoteSite,
+		ISiteFeatureReference[] sourceFeatureRefs,
+		IFeatureReference[] optionalfeatures,
+		String mirrorSiteUrl)
+		throws CoreException {
+
+		mirrorAndExposeFeatures(
+			remoteSite,
+			sourceFeatureRefs,
+			optionalfeatures);
+
+		System.out.println(
+			"Installing features finished. Updating categories ..."); //$NON-NLS-1$
+		updateCategories(remoteSite);
+		System.out.println(
+			"Updating categories finished. Updating site description ..."); //$NON-NLS-1$
+		updateDescription(remoteSite);
+		System.out.println(
+			"Updating site description finished. Saving site.xml ..."); //$NON-NLS-1$
+		save();
+		if (mirrorSiteUrl != null) {
+			generateUpdatePolicy(mirrorSiteUrl);
+		}
+	}
+	private void mirrorAndExposeFeatures(
+		ISite remoteSite,
+		ISiteFeatureReference[] sourceFeatureRefs,
+		IFeatureReference[] optionalfeatures)
+		throws CoreException {
+
+		// Features that failed will be retried once again
+		Collection failedFeatures = new ArrayList();
+		for (int i = 0; i < sourceFeatureRefs.length; i++) {
+			try {
+				IFeature sourceFeature =
+					sourceFeatureRefs[i].getFeature(new NullProgressMonitor());
+				SiteFeatureReferenceModel featureRef =
+					mirrorFeature(
+						remoteSite,
+						sourceFeature,
+						optionalfeatures,
+						1);
+				// Set categories of the new feature
+				ICategory remoteCategories[] =
+					sourceFeatureRefs[i].getCategories();
+				for (int j = 0; j < remoteCategories.length; j++) {
+					featureRef.addCategoryName(remoteCategories[j].getName());
+				}
+
+				addFeatureReferenceModel(remoteSite, featureRef);
+			} catch (CoreException ce) {
+				failedFeatures.add(sourceFeatureRefs[i]);
+			}
+		}
+
+		// do we need to retry?
+		if (failedFeatures.size() > 0) {
+			sourceFeatureRefs =
+				(ISiteFeatureReference[]) failedFeatures.toArray(
+					new ISiteFeatureReference[failedFeatures.size()]);
+		} else {
+			return;
+		}
+
+		for (int i = 0; i < sourceFeatureRefs.length; i++) {
+			IFeature sourceFeature =
+				sourceFeatureRefs[i].getFeature(new NullProgressMonitor());
+			SiteFeatureReferenceModel featureRef =
+				mirrorFeature(remoteSite, sourceFeature, optionalfeatures, 1);
+			// Set categories of the new feature
+			ICategory remoteCategories[] = sourceFeatureRefs[i].getCategories();
+			for (int j = 0; j < remoteCategories.length; j++) {
+				featureRef.addCategoryName(remoteCategories[j].getName());
+			}
+
+			addFeatureReferenceModel(remoteSite, featureRef);
+		}
+	}
+
+	/**
+	 * Install the specified feature and listed optional features on this site.
+	 * @see ISite#install(IFeature, IVerificationListener, IProgressMonitor)
+	 * @exception CoreException
+	 */
+	private SiteFeatureReferenceModel mirrorFeature(
+		ISite remoteSite,
+		IFeature sourceFeature,
+		IFeatureReference[] optionalfeatures,
+		int indent)
+		throws CoreException {
+		String tab = ""; //$NON-NLS-1$
+		for (int i = 0; i < indent; i++)
+			tab += " "; //$NON-NLS-1$
+		System.out.println(
+			tab
+				+ "Mirroring feature " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		SiteFeatureReferenceModel existingFeatures[] =
+			getDownloadedFeatureReferenceModels();
+		for (int e = 0; e < existingFeatures.length; e++) {
+			if (existingFeatures[e]
+				.getVersionedIdentifier()
+				.equals(sourceFeature.getVersionedIdentifier())) {
+				System.out.println(
+					tab
+						+ "Feature " //$NON-NLS-1$
+						+ sourceFeature.getVersionedIdentifier()
+						+ " already exists.  Skipping downloading."); //$NON-NLS-1$
+				return existingFeatures[e];
+			}
+		}
+
+		final IFeatureContentProvider provider =
+			sourceFeature.getFeatureContentProvider();
+		
+		// TODO: passing command options could be made more general in future, so this 
+		// cast is not needed. 
+		if (provider instanceof FeaturePackagedContentProvider) {
+			((FeaturePackagedContentProvider) provider).setContinueOnError(ignoreNonPresentPlugins);
+		}
+		
+		System.out.println(
+			tab
+				+ "Getting plugin entries for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		final IPluginEntry[] sourceFeaturePluginEntries =
+			sourceFeature.getRawPluginEntries();
+
+		// determine list of plugins to install
+		// find the intersection between the plugin entries already contained
+		// on the target site, and plugin entries packaged in source feature
+
+		IPluginEntry[] pluginsToInstall =
+			UpdateManagerUtils.diff(
+				sourceFeaturePluginEntries,
+				getDownloadedPluginEntries());
+
+		System.out.println(
+			tab
+				+ "Getting non plugin entries for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		final INonPluginEntry[] nonPluginsToInstall =
+			sourceFeature.getRawNonPluginEntries();
+
+		System.out.println(
+			tab
+				+ "Getting included features for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		IFeatureReference[] children =
+			sourceFeature.getRawIncludedFeatureReferences();
+		if (optionalfeatures != null) {
+			children =
+				UpdateManagerUtils.optionalChildrenToInstall(
+					children,
+					optionalfeatures);
+		}
+
+		System.out.println(
+			tab
+				+ "Downloading feature archives for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		// download feature archives
+		provider.getFeatureEntryArchiveReferences(null);
+
+		System.out.println(
+			tab
+				+ "Downloading plug-in archives for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		// download plugin archives
+		for (int i = 0; i < pluginsToInstall.length; i++) {
+			try {
+				provider.getPluginEntryArchiveReferences(pluginsToInstall[i], null);
+			} catch (CoreException ce) {
+				if ( ignoreNonPresentPlugins && 
+						(ce instanceof CoreExceptionWithRootCause) &&
+						(((CoreExceptionWithRootCause)ce).getRootException() != null) && 
+						(((CoreExceptionWithRootCause)ce).getRootException() instanceof FatalIOException) ) {
+					System.out.println("Could not mirror plug-in " + pluginsToInstall[i].getVersionedIdentifier().toString() + ". It does not exist on the given site");  //$NON-NLS-1$//$NON-NLS-2$
+				} else {
+					throw ce;
+				}
+			}
+		}
+
+		System.out.println(
+			tab
+				+ "Downloading non plug-in archives for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		// download non-plugin archives
+		for (int i = 0; i < nonPluginsToInstall.length; i++) {
+			provider.getNonPluginEntryArchiveReferences(
+				nonPluginsToInstall[i],
+				null);
+		}
+
+		System.out.println(
+			tab
+				+ "Installing child features for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		// install child features first
+		for (int i = 0; i < children.length; i++) {
+			IFeature childFeature = children[i].getFeature(null);
+			mirrorFeature(
+				remoteSite,
+				childFeature,
+				optionalfeatures,
+				indent + 1);
+		}
+
+		System.out.println(
+			tab
+				+ "Storing plug-in archives for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		// store plugins' archives
+		for (int i = 0; i < pluginsToInstall.length; i++) {
+			try {
+				ContentReference[] references = provider.getPluginEntryArchiveReferences( pluginsToInstall[i], null);
+				storePluginArchive(references[0]);
+				addDownloadedPluginEntry(pluginsToInstall[i]);
+			} catch (CoreException ce) {
+				if ( ignoreNonPresentPlugins && 
+						(ce instanceof CoreExceptionWithRootCause) &&
+						(((CoreExceptionWithRootCause)ce).getRootException() != null) && 
+						(((CoreExceptionWithRootCause)ce).getRootException() instanceof FatalIOException) ) {
+					System.out.println("Could not write plug-in " + pluginsToInstall[i].getVersionedIdentifier().toString() + ". It does not exist on the given site"); //$NON-NLS-1$ //$NON-NLS-2$
+				} else {
+					//System.out.println("ignoreNonPresentPlugins:"+ignoreNonPresentPlugins); //$NON-NLS-1$
+					throw ce;
+				}
+			}
+		}
+
+		System.out.println(
+			tab
+				+ "Storing non plug-in archives for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		// store non plugins' archives
+		for (int i = 0; i < nonPluginsToInstall.length; i++) {
+			ContentReference[] references =
+				provider.getNonPluginEntryArchiveReferences(
+					nonPluginsToInstall[i],
+					null);
+			for (int r = 0; r < references.length; r++) {
+				storeNonPluginArchive(
+					sourceFeature.getVersionedIdentifier(),
+					references[r]);
+			}
+		}
+
+		System.out.println(
+			tab
+				+ "Storing feature archives for " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " ..."); //$NON-NLS-1$
+		// store feature archive
+		ContentReference[] references =
+			provider.getFeatureEntryArchiveReferences(null);
+		storeFeatureArchive(references[0]);
+
+		System.out.println(
+			tab
+				+ "Adding feature " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " to model ..."); //$NON-NLS-1$
+
+		// add feature model to site model
+		SiteFeatureReferenceModel featureRef =
+			factory.createFeatureReferenceModel();
+		featureRef.setSiteModel(this);
+		//featureRef.setURLString(featureURL.toExternalForm());
+		featureRef.setType(ISite.DEFAULT_PACKAGED_FEATURE_TYPE);
+		featureRef.setFeatureIdentifier(
+			sourceFeature.getVersionedIdentifier().getIdentifier());
+		featureRef.setFeatureVersion(
+			sourceFeature.getVersionedIdentifier().getVersion().toString());
+		addDownloadedFeatureReferenceModel(featureRef);
+
+		System.out.println(
+			tab
+				+ "Mirroring feature " //$NON-NLS-1$
+				+ sourceFeature.getVersionedIdentifier()
+				+ " finished."); //$NON-NLS-1$
+		return featureRef;
+
+	}
+	/**
+	 * Adds a feature reference model to this site,
+	 * and exposes in site.xml if remote site exposes given feature.
+	 */
+	public void addFeatureReferenceModel(
+		ISite remoteSite,
+		SiteFeatureReferenceModel featureReference) {
+		// check if remote site exposes this feature
+		ISiteFeatureReference remoteFeatures[] =
+			remoteSite.getRawFeatureReferences();
+		for (int i = 0; i < remoteFeatures.length; i++) {
+			ISiteFeatureReference remoteFeatureRef = remoteFeatures[i];
+			try {
+				if (remoteFeatureRef
+					.getVersionedIdentifier()
+					.equals(featureReference.getVersionedIdentifier())) {
+					addFeatureReferenceModel(featureReference);
+				}
+			} catch (CoreException ce) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(ce);
+			}
+		}
+		save();
+		System.out.println(
+			"Feature " //$NON-NLS-1$
+				+ featureReference.getVersionedIdentifier()
+				+ " added to site.xml."); //$NON-NLS-1$
+	}
+	/**
+	 * Adds feature model to site model, removing old feature
+	 */
+	public void addFeatureReferenceModel(SiteFeatureReferenceModel featureReference) {
+		SiteFeatureReferenceModel[] existingModels =
+			getFeatureReferenceModels();
+		for (int j = 0; j < existingModels.length; j++) {
+			if (existingModels[j]
+				.getVersionedIdentifier()
+				.equals(featureReference.getVersionedIdentifier())) {
+				super.removeFeatureReferenceModel(existingModels[j]);
+			}
+		}
+		super.addFeatureReferenceModel(featureReference);
+	}
+
+	/**
+	 * @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
+	 */
+	private void storeFeatureArchive(ContentReference contentReference)
+		throws CoreException {
+		InputStream inStream = null;
+		String featurePath = null;
+
+		try {
+			URL newURL =
+				new URL(
+					this.getURL(),
+					Site.DEFAULT_INSTALLED_FEATURE_PATH
+						+ contentReference.getIdentifier()
+						+ ".jar"); //$NON-NLS-1$
+			featurePath = newURL.getFile();
+			inStream = contentReference.getInputStream();
+			UpdateManagerUtils.copyToLocal(inStream, featurePath, null);
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+				"Error occurred while creating "+ featurePath+" file.", //$NON-NLS-1$ //$NON-NLS-2$
+				e);
+		} finally {
+			if (inStream != null) {
+				try {
+					inStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+
+	}
+	/**
+	* @see ISiteContentConsumer#store(ContentReference, IProgressMonitor)
+	*/
+	private void storePluginArchive(ContentReference contentReference)
+		throws CoreException {
+
+		InputStream inStream = null;
+		String pluginPath = null;
+		try {
+			URL newURL = new URL(getURL(), contentReference.getIdentifier());
+			pluginPath = newURL.getFile();
+			inStream = contentReference.getInputStream();
+			// added null check here,  since contentReference can, in theory, return null for input stream. 
+			if (inStream != null) {
+				UpdateManagerUtils.copyToLocal(inStream, pluginPath, null);
+			}
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+			"Error occurred while creating "+ pluginPath+" file.", //$NON-NLS-1$ //$NON-NLS-2$
+				e);
+		} finally {
+			if (inStream != null) {
+				try {
+					inStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+	}
+
+	private void storeNonPluginArchive(
+		VersionedIdentifier featureVersionedIdentifier,
+		ContentReference contentReference)
+		throws CoreException {
+
+		InputStream inStream = null;
+		File nonPluginArchivePath = null;
+		try {
+			URL newDirURL =
+				new URL(
+					getURL(),
+					Site.DEFAULT_INSTALLED_FEATURE_PATH
+						+ "/" //$NON-NLS-1$
+						+ featureVersionedIdentifier);
+			File dir = new File(newDirURL.getFile());
+			dir.mkdirs();
+			inStream = contentReference.getInputStream();
+			nonPluginArchivePath =
+				new File(dir, contentReference.getIdentifier());
+			UpdateManagerUtils.copyToLocal(
+				inStream,
+				nonPluginArchivePath.getAbsolutePath(),
+				null);
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+			"Error occurred while creating "+ nonPluginArchivePath.getAbsolutePath()+" file." //$NON-NLS-1$ //$NON-NLS-2$
+				,e);
+		} finally {
+			if (inStream != null) {
+				try {
+					inStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+	}
+
+	private void save() {
+		FileOutputStream fos = null;
+		try {
+			URL siteURL = new URL(this.getURL(), "site.xml"); //$NON-NLS-1$
+			fos = new FileOutputStream(new File(siteURL.getFile()));
+			OutputStreamWriter outWriter = new OutputStreamWriter(fos, "UTF-8"); //$NON-NLS-1$
+			PrintWriter writer = new PrintWriter(outWriter);
+			save(writer);
+			writer.flush();
+		} catch (IOException ioe) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(
+				Utilities.newCoreException(
+					"Site XML could not be saved.", //$NON-NLS-1$
+					ioe));
+		} finally {
+			if (fos != null) {
+				try {
+					fos.close();
+				} catch (IOException ioe2) {
+				}
+			}
+		}
+	}
+	private void save(PrintWriter writer) {
+		writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //$NON-NLS-1$
+		//writer.println("<!DOCTYPE site SYSTEM \"dtd/site.dtd\">");
+		writeSite("", writer); //$NON-NLS-1$
+	}
+
+	private void writeSite(String indent, PrintWriter writer) {
+		writer.print(indent + "<site"); //$NON-NLS-1$
+		String indent2 = indent + INDENT;
+		// default type
+		//writeIfDefined(indenta, writer, "type", getType());
+		// stored relative to site.xml
+		//writeIfDefined(indenta, writer, "url", getURL());
+		writer.println(">"); //$NON-NLS-1$
+		URLEntryModel description = getDescriptionModel();
+		if (description != null) {
+			writer.println();
+			writeDescription(indent2, writer, description);
+			writer.println();
+		}
+		writeFeatures(indent2, writer);
+		writeCategories(indent2, writer);
+		writer.println(indent + "</site>"); //$NON-NLS-1$
+	}
+	private void writeFeatures(String indent, PrintWriter writer) {
+		SiteFeatureReferenceModel[] featureReferenceModels =
+			getFeatureReferenceModels();
+		for (int i = 0; i < featureReferenceModels.length; i++) {
+			writer.print(indent);
+			writer.print("<feature"); //$NON-NLS-1$
+			writer.print(
+				" url=\"features/" //$NON-NLS-1$
+					+ featureReferenceModels[i].getFeatureIdentifier()
+					+ "_" //$NON-NLS-1$
+					+ featureReferenceModels[i].getFeatureVersion()
+					+ ".jar\""); //$NON-NLS-1$
+			writer.print(
+				" id=\"" //$NON-NLS-1$
+					+ featureReferenceModels[i].getFeatureIdentifier()
+					+ "\""); //$NON-NLS-1$
+			writer.print(
+				" version=\"" //$NON-NLS-1$
+					+ featureReferenceModels[i].getFeatureVersion()
+					+ "\""); //$NON-NLS-1$
+			writer.println(">"); //$NON-NLS-1$
+
+			String[] categoryNames =
+				featureReferenceModels[i].getCategoryNames();
+			for (int cn = 0; cn < categoryNames.length; cn++) {
+				writer.print(indent + INDENT);
+				writer.println(
+					"<category name=\"" + categoryNames[cn] + "\" />"); //$NON-NLS-1$ //$NON-NLS-2$
+
+			}
+
+			writer.print(indent);
+			writer.println("</feature>"); //$NON-NLS-1$
+			writer.println();
+		}
+	}
+	private void writeCategories(String indent, PrintWriter writer) {
+		CategoryModel[] categoryModels = getCategoryModels();
+		if (categoryModels.length <= 0) {
+			return;
+		}
+		for (int i = 0; i < categoryModels.length; i++) {
+			writer.print(indent);
+			writer.print("<category-def"); //$NON-NLS-1$
+			writer.print(
+				" name=\"" //$NON-NLS-1$
+					+ categoryModels[i].getName()
+					+ "\" label=\"" //$NON-NLS-1$
+					+ categoryModels[i].getLabel()
+					+ "\""); //$NON-NLS-1$
+			writer.println(">"); //$NON-NLS-1$
+			if (categoryModels[i].getDescriptionModel() != null) {
+				writeDescription(
+						indent + INDENT,
+						writer,
+						categoryModels[i].getDescriptionModel());
+			}
+			writer.print(indent);
+			writer.println("</category-def>"); //$NON-NLS-1$
+			writer.println();
+		}
+	}
+	private void writeDescription(
+		String indent,
+		PrintWriter writer,
+		URLEntryModel urlEntryModel) {
+		String url = urlEntryModel.getURLString();
+		String text = urlEntryModel.getAnnotationNonLocalized();
+		writer.print(indent);
+		writer.print("<description"); //$NON-NLS-1$
+		if (url != null)
+			writer.print(" url=\"" + url + "\""); //$NON-NLS-1$ //$NON-NLS-2$
+		if (text == null || text.length() <= 0) {
+			writer.println(" />"); //$NON-NLS-1$
+		} else {
+			writer.println(">"); //$NON-NLS-1$
+			if (text != null) {
+				writer.println(
+					indent + INDENT + UpdateManagerUtils.xmlSafe(text));
+			}
+			writer.println(indent + "</description>"); //$NON-NLS-1$
+		}
+	}
+	/**
+	 * Adds a plugin entry 
+	 * Either from parsing the file system or 
+	 * installing a feature
+	 * 
+	 * We cannot figure out the list of plugins by reading the Site.xml as
+	 * the archives tag are optionals
+	 */
+	public void addDownloadedPluginEntry(IPluginEntry pluginEntry) {
+		downloadedPluginEntries.add(pluginEntry);
+	}
+
+	private IPluginEntry[] getDownloadedPluginEntries() {
+		return (IPluginEntry[]) downloadedPluginEntries.toArray(
+			new IPluginEntry[downloadedPluginEntries.size()]);
+	}
+	/**
+	 * Adds a plugin entry 
+	 * Either from parsing the file system or 
+	 * installing a feature
+	 * 
+	 * We cannot figure out the list of plugins by reading the Site.xml as
+	 * the archives tag are optionals
+	 */
+	public void addDownloadedFeatureReferenceModel(SiteFeatureReferenceModel featureModel) {
+		downloadedFeatureReferenceModels.add(featureModel);
+	}
+
+	private SiteFeatureReferenceModel[] getDownloadedFeatureReferenceModels() {
+		return (
+			SiteFeatureReferenceModel[]) downloadedFeatureReferenceModels
+				.toArray(
+			new SiteFeatureReferenceModel[downloadedFeatureReferenceModels
+				.size()]);
+	}
+	/**
+	 * Checks if mirror site contains a feature with given ID and version
+	 * @param featureRefModel
+	 * @return true if such feature exists
+	 */
+	/*private boolean contains(SiteFeatureReferenceModel featureRefModel) {
+		ISiteFeatureReference featureRefs[] = getRawFeatureReferences();
+		for (int i = 0; i < featureRefs.length; i++) {
+			try {
+				if (featureRefs[i]
+					.getVersionedIdentifier()
+					.equals(featureRefModel.getVersionedIdentifier())) {
+					return true;
+				}
+			} catch (CoreException ce) {
+				ce.printStackTrace();
+			}
+		}
+		return false;
+	}*/
+
+	/**
+	 * Updates description of this site
+	 * from description of the remote site.
+	 */
+	private void updateDescription(ISite remoteSite) {
+		IURLEntry urlEntry = remoteSite.getDescription();
+		if (urlEntry != null) {
+			URLEntryModel newUrlEntryModel = new URLEntryModel();
+			URL url = urlEntry.getURL();
+			newUrlEntryModel.setAnnotation(urlEntry.getAnnotation());
+			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=136249
+			// URL is not required, so might be null
+			// The null case is (already) handled correctly in
+			// writeDescription
+			if (url != null) {
+				newUrlEntryModel.setURLString(url.toExternalForm());
+			}
+			this.setDescriptionModel(newUrlEntryModel);
+		}
+	}
+	/**
+	 * Updates all categories used by features on this site
+	 * from categories defined on remote site.
+	 * Categories not defined on remote site are unchanged.
+	 */
+	private void updateCategories(ISite remoteSite) {
+		// collect name of categories used on this site
+		Set usedCategoryNames = new HashSet();
+		SiteFeatureReferenceModel featureRefModels[] =
+			getFeatureReferenceModels();
+		for (int f = 0; f < featureRefModels.length; f++) {
+			String[] featureCategoryNames =
+				featureRefModels[f].getCategoryNames();
+
+			for (int c = 0; c < featureCategoryNames.length; c++) {
+				usedCategoryNames.add(featureCategoryNames[c]);
+			}
+		}
+
+		Collection newCategoryModels = new ArrayList();
+		for (Iterator it = usedCategoryNames.iterator(); it.hasNext();) {
+			String name = (String) it.next();
+			ICategory remoteCategory = remoteSite.getCategory(name);
+			if (remoteCategory == null) {
+				// remote site does not define this category
+				CategoryModel oldCategory = null;
+				try {
+					oldCategory = (CategoryModel) getCategory(name);
+				} catch (NullPointerException npe) {
+					// cannot reproduce npe anymore
+				}
+				if (oldCategory != null) {
+					newCategoryModels.add(oldCategory);
+				}
+			} else {
+				newCategoryModels.add(remoteCategory);
+			}
+
+		}
+		setCategoryModels(
+			(CategoryModel[]) newCategoryModels.toArray(
+				new CategoryModel[newCategoryModels.size()]));
+
+	}
+	private void generateUpdatePolicy(String url) {
+		FileOutputStream fos = null;
+		try {
+			URL siteURL = new URL(this.getURL(), "policy.xml"); //$NON-NLS-1$
+			fos = new FileOutputStream(new File(siteURL.getFile()));
+			OutputStreamWriter outWriter = new OutputStreamWriter(fos, "UTF-8"); //$NON-NLS-1$
+			PrintWriter writer = new PrintWriter(outWriter);
+
+			writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //$NON-NLS-1$
+			writer.println("<update-policy>"); //$NON-NLS-1$
+
+			writer.println(
+				"<!-- You can paste the following fragment, containing url-map elements, into another policy file. -->"); //$NON-NLS-1$
+			writeUrlMaps(writer, url);
+			writer.println("<!-- End of fragment with url-map elements. -->"); //$NON-NLS-1$
+
+			writer.println("</update-policy>"); //$NON-NLS-1$
+
+			writer.flush();
+		} catch (IOException ioe) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(
+				Utilities.newCoreException(
+					"policy.xml could not be saved", //$NON-NLS-1$
+					ioe));
+		} finally {
+			if (fos != null) {
+				try {
+					fos.close();
+				} catch (IOException ioe2) {
+				}
+			}
+		}
+	}
+	private void writeUrlMaps(PrintWriter writer, String url) {
+		SiteFeatureReferenceModel[] featureReferenceModels =
+			getFeatureReferenceModels();
+		for (int i = 0; i < featureReferenceModels.length; i++) {
+			writer.print("\t"); //$NON-NLS-1$
+			writer.print("<url-map"); //$NON-NLS-1$
+			writer.print(
+				" pattern=\"" //$NON-NLS-1$
+					+ featureReferenceModels[i].getFeatureIdentifier()
+					+ "\""); //$NON-NLS-1$
+			writer.print(" url=\"" + url + "\""); //$NON-NLS-1$ //$NON-NLS-2$
+			writer.println(" />"); //$NON-NLS-1$
+		}
+	}
+
+	public void setIgnoreNonPresentPlugins(boolean ignoreNonPresentPlugins) {
+		this.ignoreNonPresentPlugins = ignoreNonPresentPlugins;
+		
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorSiteFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorSiteFactory.java
new file mode 100644
index 0000000..482ab68
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/mirror/MirrorSiteFactory.java
@@ -0,0 +1,234 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.mirror;
+import java.io.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.internal.model.*;
+import org.eclipse.update.standalone.*;
+import org.xml.sax.*;
+
+public class MirrorSiteFactory extends BaseSiteFactory {
+	/*
+	 * @see SiteModelFactory#createSiteMapModel()
+	 */
+	public SiteModel createSiteMapModel() {
+		return new MirrorSite(this);
+	}
+	/*
+	 * @see ISiteFactory#createSite(URL)
+	 */
+	public ISite createSite(URL url)
+		throws CoreException, InvalidSiteTypeException {
+		return createSite(new File(url.getFile()));
+	}
+	/*
+	 * @see ISiteFactory#createSite(URL)
+	 */
+	public ISite createSite(File siteLocation)
+		throws CoreException, InvalidSiteTypeException {
+
+		InputStream siteStream = null;
+
+		if (!siteLocation.exists()) {
+			if (!siteLocation.mkdirs()) {
+				throw Utilities.newCoreException(
+					"Directory " //$NON-NLS-1$
+						+ siteLocation.getAbsolutePath()
+						+ " could not be created.", //$NON-NLS-1$
+					null);
+			}
+		}
+		if (!siteLocation.isDirectory() || !siteLocation.canWrite())
+			throw Utilities.newCoreException(
+				siteLocation.getAbsolutePath()
+					+ " is not a directory or is not writtable.", //$NON-NLS-1$
+				null);
+
+		MirrorSite site = null;
+		// parse public features
+		if (new File(siteLocation, Site.SITE_XML).exists()) {
+			try {
+				siteStream =
+					new FileInputStream(new File(siteLocation, Site.SITE_XML));
+			} catch (FileNotFoundException fnfe) {
+			}
+			site = (MirrorSite) parseSite(siteStream);
+			try {
+				if (siteStream != null)
+					siteStream.close();
+			} catch (IOException e) {
+			}
+		}
+		if (site == null) {
+			site = (MirrorSite) createSiteMapModel();
+		}
+		// parse downloaded plugins and fragments
+		parseDownloadedPluginsAndFragments(
+			site,
+			new File(siteLocation, Site.DEFAULT_PLUGIN_PATH));
+		// parse downloaded features
+		parseDownloadedFeatures(
+			site,
+			new File(siteLocation, Site.DEFAULT_FEATURE_PATH));
+
+		URL url;
+		try {
+			url = siteLocation.toURL();
+		} catch (MalformedURLException mue) {
+			throw Utilities.newCoreException(
+				"A URL for site " //$NON-NLS-1$
+					+ siteLocation.getAbsolutePath()
+					+ " could not be created.", //$NON-NLS-1$
+				mue);
+		}
+		SiteContentProvider contentProvider = null;
+		contentProvider = new SiteFileContentProvider(url);
+
+		site.setSiteContentProvider(contentProvider);
+		contentProvider.setSite(site);
+		try {
+			site.resolve(url, url);
+		} catch (MalformedURLException mue) {
+			throw Utilities.newCoreException(
+				"Unable to resolve URL " //$NON-NLS-1$
+					+ (url == null ? "" : url.toExternalForm()), //$NON-NLS-1$
+				mue);
+		}
+		return site;
+	}
+	/**
+	 *  
+	 */
+	private void parseDownloadedPluginsAndFragments(MirrorSite site,
+			File pluginDir) throws CoreException {
+		if (!pluginDir.exists()) {
+			return;
+		}
+		String[] dir = pluginDir.list(FeaturePackagedContentProvider.filter);
+		for (int i = 0; i < dir.length; i++) {
+			InputStream in = null;
+			try {
+				File file = new File(pluginDir, dir[i]);
+				JarContentReference jarReference = new JarContentReference(
+						null, file);
+				ContentReference ref = jarReference.peek("META-INF/MANIFEST.MF", null, null); //$NON-NLS-1$
+				if (ref != null) {
+					try {
+						in = ref.getInputStream();
+					}
+					catch (SecurityException e) {
+						// in case of an invalid signature in jar, we will catch 
+						// and re-throw a little more specific message. Otherwise, it's 
+						// impossible to tell which jar had the problem. 
+						String filename = file.getName();
+						CoreException updateException = Utilities.newCoreException(filename,e);
+						throw updateException;
+					}
+					BundleManifest manifest = new BundleManifest(in);
+					if (manifest.exists()) {
+						site
+								.addDownloadedPluginEntry(manifest
+										.getPluginEntry());
+						continue;
+					}
+				}
+				ref = jarReference.peek("plugin.xml", null, null); //$NON-NLS-1$
+				if (ref == null) {
+					ref = jarReference.peek("fragment.xml", null, null); //$NON-NLS-1$
+				}
+				if (ref != null) {
+					in = ref.getInputStream();
+					PluginEntry entry = new DefaultPluginParser().parse(in);
+					site.addDownloadedPluginEntry(entry);
+				}
+			} catch (IOException e) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(e);
+			} catch (SAXException e) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(e);
+			} finally {
+				if(in !=null){
+					try{
+						in.close();
+					}catch(IOException ce){
+					}
+				}
+			}
+		}
+	}
+	/**
+	* Method parseFeature.
+	* @throws CoreException
+	*/
+	private void parseDownloadedFeatures(MirrorSite site, File featureDir)
+		throws CoreException {
+		if (featureDir.exists()) {
+			String[] dir;
+			SiteFeatureReferenceModel featureRef;
+			URL featureURL;
+			File currentFeatureFile;
+			String newFilePath = null;
+
+			try {
+				// only list JAR files
+				dir = featureDir.list(FeaturePackagedContentProvider.filter);
+				for (int index = 0; index < dir.length; index++) {
+
+					// check if the JAR file contains a feature.xml
+					currentFeatureFile = new File(featureDir, dir[index]);
+					JarContentReference ref =
+						new JarContentReference("", currentFeatureFile); //$NON-NLS-1$
+					ContentReference result = null;
+					try {
+						result = ref.peek(Feature.FEATURE_XML, null, null);
+					} catch (IOException e) {
+						UpdateCore.warn(
+							"Exception retrieving feature.xml in file:" //$NON-NLS-1$
+								+ currentFeatureFile,
+							e);
+					}
+					if (result == null) {
+						UpdateCore.warn(
+							"Unable to find feature.xml in file:" //$NON-NLS-1$
+								+ currentFeatureFile);
+					} else {
+						featureURL = currentFeatureFile.toURL();
+						featureRef = createFeatureReferenceModel();
+						featureRef.setSiteModel(site);
+						featureRef.setURLString(featureURL.toExternalForm());
+						featureRef.setType(ISite.DEFAULT_PACKAGED_FEATURE_TYPE);
+						featureRef.setFeatureIdentifier(
+							featureRef
+								.getVersionedIdentifier()
+								.getIdentifier());
+						featureRef.setFeatureVersion(
+							featureRef
+								.getVersionedIdentifier()
+								.getVersion()
+								.toString());
+						site.addDownloadedFeatureReferenceModel(featureRef);
+					}
+				}
+			} catch (MalformedURLException e) {
+				throw Utilities.newCoreException(
+					"Unable to create URL for file " + newFilePath + ".", //$NON-NLS-1$ //$NON-NLS-2$
+					e);
+			}
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/BundleManifest.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/BundleManifest.java
new file mode 100644
index 0000000..fbbeeee
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/BundleManifest.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.model;
+import java.io.*;
+import java.util.jar.*;
+
+import org.eclipse.osgi.util.*;
+import org.eclipse.update.core.*;
+import org.osgi.framework.*;
+/**
+ * Parses MANIFEST.MF
+ */
+public class BundleManifest {
+	private PluginEntry pluginEntry;
+	private IOException exception;
+	/**
+	 * Constructor for local file
+	 */
+public BundleManifest(File manifest) {
+		super();
+		if (manifest.exists() && !manifest.isDirectory()) {
+			FileInputStream fos = null;
+			try {
+				fos = new FileInputStream(manifest);
+				parse(fos);
+			} catch (IOException ioe) {
+			} finally {
+				if (fos != null) {
+					try {
+						fos.close();
+					} catch (IOException e) {
+					}
+				}
+			}
+		}
+	}
+	/**
+	 * Constructor for local file
+	 */
+	public BundleManifest(InputStream input) {
+		super();
+		if (input != null) {
+			parse(input);
+		}
+	}
+	/**
+	 * Parses manifest, creates PluginEntry if manifest is valid, stores
+	 * exception if any occurs
+	 * 
+	 * @param in
+	 *            InputStream
+	 */
+	private void parse(InputStream in) {
+		try {
+			Manifest m = new Manifest(in);
+			Attributes a = m.getMainAttributes();
+			// plugin id
+			String symbolicName = a.getValue(Constants.BUNDLE_SYMBOLICNAME);
+			if (symbolicName == null) {
+				// In Eclipse manifest must have Bundle-SymbolicName attribute
+				return;
+			}
+			String id;
+			try {
+				ManifestElement[] elements = ManifestElement.parseHeader(
+						Constants.BUNDLE_SYMBOLICNAME, symbolicName);
+				id = elements[0].getValue();
+			} catch (BundleException be) {
+				throw new IOException(be.getMessage());
+			}
+			// plugin version
+			String version = a.getValue(Constants.BUNDLE_VERSION);
+			if (version == null) {
+				return;
+			}
+			String hostPlugin = a.getValue(Constants.FRAGMENT_HOST);
+			pluginEntry = new PluginEntry();
+			pluginEntry.setVersionedIdentifier(new VersionedIdentifier(id,
+					version));
+			pluginEntry.isFragment(hostPlugin != null
+					&& hostPlugin.length() > 0);
+		} catch (IOException ioe) {
+			exception = ioe;
+		}
+	}
+	public boolean exists() {
+		return exception != null || pluginEntry != null;
+	}
+	/**
+	 * Obtains PluginEntry from a manifest.
+	 * 
+	 * @return PluginEntry or null if valid manifest does not exist
+	 * @throws IOException
+	 *             if exception during parsing
+	 */
+	public PluginEntry getPluginEntry() throws IOException {
+		if (exception != null) {
+			throw exception;
+		} else {
+			return pluginEntry;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfigurationActivityModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfigurationActivityModel.java
new file mode 100644
index 0000000..faf1b63
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfigurationActivityModel.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.model;
+ 
+import java.util.Date;
+
+import org.eclipse.update.core.model.*;
+
+public class ConfigurationActivityModel extends ModelObject{
+	
+	private String label;
+	private int action;
+	private Date date;
+	private int status;
+	private InstallConfigurationModel installConfiguration;
+	
+
+	/**
+	 * Constructor for ConfigurationActivityModel.
+	 */
+	public ConfigurationActivityModel() {
+		super();
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public int getAction() {
+		return action;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public Date getDate() {
+		return date;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public int getStatus() {
+		return status;
+	}
+
+	/**
+	 * Sets the date.
+	 * @param date The date to set
+	 */
+	public void setDate(Date date) {
+		assertIsWriteable();
+		this.date = date;
+	}
+
+	/**
+	 * Sets the status.
+	 * @param status The status to set
+	 */
+	public void setStatus(int status) {
+		assertIsWriteable();
+		this.status = status;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public String getLabel() {
+		return label;
+	}
+
+	/**
+	 * Sets the label.
+	 * @param label The label to set
+	 */
+	public void setLabel(String label) {
+		assertIsWriteable();
+		this.label = label;
+	}
+
+	/**
+	 * Sets the action.
+	 * @param action The action to set
+	 */
+	public void setAction(int action) {
+		assertIsWriteable();
+		this.action = action;
+	}
+
+	/**
+	 * Gets the installConfiguration.
+	 * @return Returns a InstallConfigurationModel
+	 */
+	public InstallConfigurationModel getInstallConfigurationModel() {
+		return installConfiguration;
+	}
+
+	/**
+	 * Sets the installConfiguration.
+	 * @param installConfiguration The installConfiguration to set
+	 */
+	public void setInstallConfigurationModel(InstallConfigurationModel installConfiguration) {
+		assertIsWriteable();		
+		this.installConfiguration = installConfiguration;
+	}
+
+}
+
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfigurationPolicyModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfigurationPolicyModel.java
new file mode 100644
index 0000000..34e5116
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfigurationPolicyModel.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.model;
+
+import java.net.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * 
+ */
+public class ConfigurationPolicyModel extends ModelObject {
+
+	
+	
+
+	private int policy;
+	private Map /* of FeatureReferenceModel */configuredFeatureReferences;
+	private Map /* of FeatureReferenceModel */unconfiguredFeatureReferences;
+	
+	// since 2.0.2
+	private ConfiguredSiteModel configuredSiteModel;
+	
+	// since 2.1
+	private boolean enable;
+
+	/**
+	 * Constructor for ConfigurationPolicyModel.
+	 */
+	public ConfigurationPolicyModel() {
+		super();
+		enable = true;
+		configuredFeatureReferences = new HashMap();
+//		unconfiguredFeatureReferences = new HashMap();		
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public int getPolicy() {
+		return policy;
+	}
+
+	/**
+	 * Sets the policy.
+	 * @param policy The policy to set
+	 */
+	public void setPolicy(int policy) {
+		assertIsWriteable();
+		this.policy = policy;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public FeatureReferenceModel[] getConfiguredFeaturesModel() {
+		if (configuredFeatureReferences==null || configuredFeatureReferences.isEmpty())
+			return new FeatureReferenceModel[0];
+		return (FeatureReferenceModel[]) configuredFeatureReferences.keySet().toArray(arrayTypeFor(configuredFeatureReferences.keySet()));
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public FeatureReferenceModel[] getUnconfiguredFeaturesModel() {
+		// obtain unconfigured features by comparing configured ones with those installed
+		if (unconfiguredFeatureReferences == null
+				&& configuredSiteModel != null
+				&& configuredSiteModel.getSiteModel() != null) {
+			ISite site = (ISite) configuredSiteModel.getSiteModel();
+			ISiteFeatureReference[] siteFeatures = site.getFeatureReferences();
+			if (siteFeatures.length > getConfiguredFeaturesModel().length) {
+				for (int i=0; i<siteFeatures.length; i++) {
+					if (!(siteFeatures[i] instanceof SiteFeatureReference))
+						continue;
+					Iterator iterator = configuredFeatureReferences.keySet().iterator();
+					boolean found = false;
+					while(!found && iterator.hasNext()) {
+						FeatureReferenceModel f = (FeatureReferenceModel)iterator.next();
+						if (UpdateManagerUtils.sameURL(f.getURL(), siteFeatures[i].getURL()))
+							found = true;
+					}
+					if (!found)
+						addUnconfiguredFeatureReference((SiteFeatureReference)siteFeatures[i]);
+				}
+			}
+		}
+		if (unconfiguredFeatureReferences == null
+				|| unconfiguredFeatureReferences.isEmpty())
+			return new FeatureReferenceModel[0];
+		return (FeatureReferenceModel[]) unconfiguredFeatureReferences.keySet()
+				.toArray(arrayTypeFor(unconfiguredFeatureReferences.keySet()));
+	}
+
+	/**
+	 * Gets the configuredSiteModel.
+	 * @return Returns a ConfiguredSiteModel
+	 * @since 2.0.2
+	 */
+	public ConfiguredSiteModel getConfiguredSiteModel() {
+		return configuredSiteModel;
+	}
+
+	/**
+	 * Sets the configuredSiteModel.
+	 * @param configuredSiteModel The configuredSiteModel to set
+	 * @since 2.0.2
+	 */
+	public void setConfiguredSiteModel(ConfiguredSiteModel configuredSiteModel) {
+		this.configuredSiteModel = configuredSiteModel;
+	}
+
+	/**
+	 * 
+	 */
+	private boolean remove(FeatureReferenceModel feature, Map list) {
+		URL featureURL = feature.getURL();
+		boolean found = false;
+		Iterator iter = list.keySet().iterator();
+		while (iter.hasNext() && !found) {
+			FeatureReferenceModel element = (FeatureReferenceModel) iter.next();
+			if (UpdateManagerUtils.sameURL(element.getURL(),featureURL)) {
+				list.remove(element);
+				found = true;
+			}
+		}
+		return found;
+	}
+
+	/**
+	 * returns an array of string corresponding to plugins file
+	 */
+	/*package*/
+
+	
+	/**
+	 * 
+	 */
+	private void add(FeatureReferenceModel feature, Map list) {
+		URL featureURL = feature.getURL();
+		boolean found = false;
+		Iterator iter = list.keySet().iterator();
+		while (iter.hasNext() && !found) {
+			FeatureReferenceModel element = (FeatureReferenceModel) iter.next();
+			if (UpdateManagerUtils.sameURL(element.getURL(),featureURL)) {
+				found = true;
+			}
+		}
+
+		if (!found) {
+			list.put(feature,null);
+		} else {
+			UpdateCore.warn("Feature Reference :"+feature+" already part of the list."); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	/**
+	 * adds a feature in the configuredReference list
+	 * also used by the parser to avoid creating another activity
+	 */
+	public void addConfiguredFeatureReference(FeatureReferenceModel feature) {
+		assertIsWriteable();
+		
+		if (configuredFeatureReferences == null)
+			this.configuredFeatureReferences = new HashMap();
+		if (!configuredFeatureReferences.containsKey(feature)){
+			//DEBUG:
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION){
+				UpdateCore.debug("Configuring "+feature.getURLString()); //$NON-NLS-1$
+			}
+			this.add(feature, configuredFeatureReferences);
+		}	
+
+		// when user configure a feature,
+		// we have to remove it from unconfigured feature if it exists
+		// because the user doesn't know...
+		if (unconfiguredFeatureReferences != null) {
+			boolean success = remove(feature, unconfiguredFeatureReferences);
+			if (!success)
+				UpdateCore.warn("Feature not part of Unconfigured list: "+feature.getURLString());			 //$NON-NLS-1$
+		}
+
+	}
+
+	/**
+	 * adds a feature in the list
+	 * also used by the parser to avoid creating another activity
+	 */
+	public void addUnconfiguredFeatureReference(FeatureReferenceModel feature) {
+		assertIsWriteable();
+		if (unconfiguredFeatureReferences == null)
+			this.unconfiguredFeatureReferences = new HashMap();
+		if (!unconfiguredFeatureReferences.containsKey(feature)){
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION){
+				UpdateCore.debug("Unconfiguring "+feature.getURLString()); //$NON-NLS-1$
+			}
+			this.add(feature, unconfiguredFeatureReferences);
+		}	
+
+		// an unconfigured feature is always from a configured one no ?
+		// unless it was parsed right ?
+		if (configuredFeatureReferences != null) {
+			boolean success = remove(feature, configuredFeatureReferences);
+			if (!success)
+				UpdateCore.warn("Feature not part of Configured list: "+feature.getURLString());				 //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * removes a feature from any list
+	 */
+	public void removeFeatureReference(FeatureReferenceModel feature) {
+		assertIsWriteable();
+		if (unconfiguredFeatureReferences!=null){
+			boolean success = remove(feature, unconfiguredFeatureReferences);
+			if (!success)
+				UpdateCore.warn(feature.getURLString()+" not part of unconfigured list.");							 //$NON-NLS-1$
+		}
+
+		if (configuredFeatureReferences != null) {
+			boolean success = remove(feature, configuredFeatureReferences);
+			if (!success)
+				UpdateCore.warn(feature.getURLString()+" not part of configured list.");							 //$NON-NLS-1$
+		}
+	}
+	
+	/**
+	 * Sets the unconfiguredFeatureReferences.
+	 * @param featureReferences The unconfiguredFeatureReferences to set
+	 */
+	protected void setUnconfiguredFeatureReferences(IFeatureReference[] featureReferences) {
+		unconfiguredFeatureReferences = new HashMap();
+		for (int i = 0; i < featureReferences.length; i++) {
+			unconfiguredFeatureReferences.put(featureReferences[i],null);
+		}
+	}
+
+
+	/**
+	 * Sets the configuredFeatureReferences.
+	 * @param featureReferences The configuredFeatureReferences to set
+	 */
+	protected void setConfiguredFeatureReferences(IFeatureReference[] featureReferences) {
+		configuredFeatureReferences = new HashMap();
+		for (int i = 0; i < featureReferences.length; i++) {
+			configuredFeatureReferences.put(featureReferences[i],null);
+		}		
+	
+	}
+
+	/**
+	 * @return boolean
+	 */
+	public boolean isEnabled() {
+		return enable;
+	}
+
+	/**
+	 * @param value
+	 */
+	public void setEnabled(boolean value) {
+		enable = value;
+	}
+
+
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfiguredSiteModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfiguredSiteModel.java
new file mode 100644
index 0000000..a45b2c4
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ConfiguredSiteModel.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.model;
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * 
+ */
+public class ConfiguredSiteModel extends ModelObject {
+	private String[] previousPluginPath;
+
+	private SiteModel site;
+	private String platformURLString;
+	private ConfigurationPolicyModel policy;
+	private InstallConfigurationModel installConfiguration;
+	private boolean installable = false;
+
+	/**
+	 * Constructor
+	 */
+	public ConfiguredSiteModel() {
+		super();
+	}
+
+	/**
+	 * returns the site
+	 * @return The ISite 
+	 * @since 2.0
+	 */
+	public SiteModel getSiteModel() {
+		return site;
+	}
+
+	/**
+	 * Sets the site.
+	 * @param site The site to set
+	 */
+	public void setSiteModel(SiteModel site) {
+		assertIsWriteable();
+		this.site = site;
+	}
+
+	/**
+	 * returns the policy
+	 */
+	public ConfigurationPolicyModel getConfigurationPolicyModel() {
+		return policy;
+	}
+
+	/**
+	 * 
+	 * @since 2.0
+	 */
+	public void setConfigurationPolicyModel(ConfigurationPolicyModel policy) {
+		assertIsWriteable();
+		this.policy = policy;
+		policy.setConfiguredSiteModel(this);
+	}
+
+	/**
+	 * @since
+	 */
+	public boolean isUpdatable() {
+		return installable;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public void setUpdatable(boolean installable) {
+		assertIsWriteable();
+		this.installable = installable;
+	}
+
+	/**
+	 * Gets the installConfiguration.
+	 * @return Returns a InstallConfigurationModel
+	 */
+	public InstallConfigurationModel getInstallConfigurationModel() {
+		return installConfiguration;
+	}
+
+	/**
+	 * Sets the installConfiguration.
+	 * @param installConfiguration The installConfiguration to set
+	 */
+	public void setInstallConfigurationModel(InstallConfigurationModel installConfiguration) {
+		assertIsWriteable();
+		this.installConfiguration = installConfiguration;
+	}
+
+	/**
+	 * Gets the platformURLString.
+	 * @return Returns a String
+	 */
+	public String getPlatformURLString() {
+		return platformURLString;
+	}
+
+	/**
+	 * Sets the platformURLString.
+	 * @param platformURLString The platformURLString to set
+	 */
+	public void setPlatformURLString(String platformURLString) {
+		this.platformURLString = platformURLString;
+	}
+
+	
+		/**
+	 * Gets the previousPluginPath. The list of plugins the platform had.
+	 * @return Returns a String[]
+	 */
+	public String[] getPreviousPluginPath() {
+		if (previousPluginPath == null)
+			previousPluginPath = new String[0];
+		return previousPluginPath;
+	}
+
+	/**
+	 * Sets the previousPluginPath.
+	 * @param previousPluginPath The previousPluginPath to set
+	 */
+	public void setPreviousPluginPath(String[] previousPluginPath) {
+		this.previousPluginPath = new String[previousPluginPath.length];
+		System.arraycopy(previousPluginPath, 0, this.previousPluginPath, 0, previousPluginPath.length);
+	}
+
+	/*
+	 * creates a Status
+	 */
+	protected IStatus createStatus(int statusType, String msg, Exception e){
+		if (statusType!=IStatus.OK) statusType = IStatus.ERROR;
+		return createStatus(statusType,IStatus.OK, msg.toString(), e);
+	}
+
+	/*
+	 * creates a Status
+	 */
+	protected IStatus createStatus(int statusSeverity, int statusCode, String msg, Exception e){
+		String id =	UpdateCore.getPlugin().getBundle().getSymbolicName();
+	
+		StringBuffer completeString = new StringBuffer(""); //$NON-NLS-1$
+		if (msg!=null)
+			completeString.append(msg);
+		if (e!=null){
+			completeString.append("\r\n["); //$NON-NLS-1$
+			completeString.append(e.toString());
+			completeString.append("]\r\n"); //$NON-NLS-1$
+		}
+		return new Status(statusSeverity, id, statusCode, completeString.toString(), e);
+	}
+	
+	/**
+	 * @see org.eclipse.update.configuration.IConfiguredSite#isEnabled()
+	 */
+	public boolean isEnabled() {
+		return getConfigurationPolicyModel().isEnabled();
+	}
+
+	/**
+	 * @see org.eclipse.update.configuration.IConfiguredSite#setEnabled(boolean)
+	 */
+	public void setEnabled(boolean value) {
+		getConfigurationPolicyModel().setEnabled(value);
+	}
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/DefaultPluginParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/DefaultPluginParser.java
new file mode 100644
index 0000000..f406130
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/DefaultPluginParser.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.model;
+
+
+import java.io.*;
+import javax.xml.parsers.*;
+
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.xml.sax.*;
+import org.xml.sax.helpers.*;
+
+/**
+ * Parse default feature.xml
+ */
+
+public class DefaultPluginParser extends DefaultHandler {
+	private final static SAXParserFactory parserFactory =
+		SAXParserFactory.newInstance();
+	private SAXParser parser;
+	private String id = null;
+	private String version = null;
+	private PluginEntry pluginEntry;
+
+	private static final String PLUGIN = "plugin"; //$NON-NLS-1$
+	private static final String FRAGMENT = "fragment"; //$NON-NLS-1$
+
+	private class ParseCompleteException extends SAXException {
+
+        private static final long serialVersionUID = 1L;
+
+        public ParseCompleteException(String arg0) {
+			super(arg0);
+		}
+	}
+
+	/**
+	 * Constructor for DefaultFeatureParser
+	 */
+	public DefaultPluginParser() {
+		super();
+		try {
+			parserFactory.setNamespaceAware(true);
+			this.parser = parserFactory.newSAXParser();
+		} catch (ParserConfigurationException e) {
+			UpdateCore.log(e);
+		} catch (SAXException e) {
+			UpdateCore.log(e);
+		}
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public synchronized PluginEntry parse(InputStream in) throws SAXException, IOException {
+		try {
+			pluginEntry = new PluginEntry();
+			parser.parse(new InputSource(in), this);
+		} catch (ParseCompleteException e) {
+			// expected, we stopped the parsing when we have the information we need
+			/// no need to pursue the parsing
+		}
+
+		if (id == null || id.trim().length() == 0)
+			id = "_no_id_"; //$NON-NLS-1$
+		pluginEntry.setVersionedIdentifier(new VersionedIdentifier(id, version));
+		return pluginEntry;
+	}
+
+	/**
+	 * @see DefaultHandler#startElement(String, String, String, Attributes)
+	 */
+	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+
+		String tag = localName.trim();
+
+		if (tag.equalsIgnoreCase(PLUGIN)) {
+			pluginEntry.isFragment(false);			
+			processPlugin(attributes);
+			return;
+		}
+
+		if (tag.equalsIgnoreCase(FRAGMENT)) {
+			pluginEntry.isFragment(true);			
+			processPlugin(attributes);
+			return;
+		}
+	}
+
+	/** 
+	 * process plugin entry info
+	 */
+	private void processPlugin(Attributes attributes) throws ParseCompleteException {
+		id = attributes.getValue("id"); //$NON-NLS-1$
+		version = attributes.getValue("version"); //$NON-NLS-1$
+		throw new ParseCompleteException(""); //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ITimestamp.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ITimestamp.java
new file mode 100644
index 0000000..246287b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/ITimestamp.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.model;
+
+import java.util.Date;
+
+public interface ITimestamp {
+
+	public Date getTimestamp();
+	
+	public void setTimestamp( Date timestamp);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/InstallConfigurationModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/InstallConfigurationModel.java
new file mode 100644
index 0000000..97d1013
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/InstallConfigurationModel.java
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.model;
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configurator.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * An InstallConfigurationModel is 
+ * 
+ */
+
+public class InstallConfigurationModel extends ModelObject {
+
+	// performance
+	private URL bundleURL;
+	private URL base;
+	private boolean resolved = false;
+
+	private boolean isCurrent = false;
+	private URL locationURL;
+	private String locationURLString;
+	protected Date date;
+	private String label;
+	private List /* of ConfiguretionActivityModel */ activities;
+	private List /* of configurationSiteModel */ configurationSites;
+
+	protected boolean initialized = false;
+	protected boolean lightlyInitialized = false;
+
+	/**
+	 * default constructor. Create
+	 */
+	public InstallConfigurationModel() {
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public ConfiguredSiteModel[] getConfigurationSitesModel() {
+		if (!initialized) 
+			initialize();
+		if (configurationSites == null || configurationSites.size() == 0)
+			return new ConfiguredSiteModel[0];
+	
+		return (ConfiguredSiteModel[]) configurationSites.toArray(arrayTypeFor(configurationSites));
+	}
+
+	/**
+	 * Adds the configuration to the list
+	 * is called when adding a Site or parsing the XML file
+	 * in this case we do not want to create a new activity, so we do not want t call
+	 * addConfigurationSite()
+	 */
+	public void addConfigurationSiteModel(ConfiguredSiteModel site) {
+		if (configurationSites == null) {
+			configurationSites = new ArrayList();
+		}
+		if (!configurationSites.contains(site)) {
+			configurationSites.add(site);
+		}
+	}
+
+	public void setConfigurationSiteModel(ConfiguredSiteModel[] sites) {
+		configurationSites = null;
+		for (int i = 0; i < sites.length; i++) {
+			addConfigurationSiteModel(sites[i]);
+		}
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public boolean removeConfigurationSiteModel(ConfiguredSiteModel site) {
+		if (!initialized) initialize();
+
+		if (configurationSites != null) {
+			return configurationSites.remove(site);
+		}
+
+		return false;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public boolean isCurrent() {
+		if (!lightlyInitialized && !initialized ) 
+			doLightInitialization();
+		
+		return isCurrent;
+	}
+
+	/**
+	 *  @since 2.0
+	 */
+	public void setCurrent(boolean isCurrent) {
+		// do not check if writable as we may
+		// set an install config as Not current
+		this.isCurrent = isCurrent;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public ConfigurationActivityModel[] getActivityModel() {
+		if (activities == null && !initialized) 
+			initialize();
+		if (activities == null || activities.size() == 0)
+			return new ConfigurationActivityModel[0];
+		return (ConfigurationActivityModel[]) activities.toArray(arrayTypeFor(activities));
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public void addActivityModel(ConfigurationActivityModel activity) {
+		if (activities == null)
+			activities = new ArrayList();
+		if (!activities.contains(activity)) {
+			activities.add(activity);
+			activity.setInstallConfigurationModel(this);
+		}
+	}
+	/**
+	 * 
+	 */
+	public Date getCreationDate() {
+//		if (!initialized) initialize();
+		if (date == null)
+			doLightInitialization();
+		return date;
+	}
+	/**
+	 * Sets the date.
+	 * @param date The date to set
+	 */
+	public void setCreationDate(Date date) {
+		assertIsWriteable();
+		this.date = date;
+	}
+	/**
+	 * @since 2.0
+	 */
+	public URL getURL() {
+		//if (!initialized) initialize();
+		//no need to initialize, always set
+		delayedResolve();
+		return locationURL;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public String getLabel() {
+//		if (!initialized) initialize();
+		if (label == null)
+			doLightInitialization();
+		return label;
+	}
+
+	/**
+	 * @since 2.0.2
+	 */
+
+	public String toString() {
+		return getLabel();
+	}
+
+	/**
+	 * Sets the label.
+	 * @param label The label to set
+	 */
+	public void setLabel(String label) {
+		assertIsWriteable();
+		this.label = label;
+	}
+
+	/**
+	 * Gets the locationURLString.
+	 * @return Returns a String
+	 */
+	public String getLocationURLString() {
+		if (!initialized) delayedResolve();
+		return locationURLString;
+	}
+
+	/**
+	 * Sets the locationURLString.
+	 * @param locationURLString The locationURLString to set
+	 */
+	public void setLocationURLString(String locationURLString) {
+		assertIsWriteable();
+		this.locationURLString = locationURLString;
+		this.locationURL = null;
+	}
+
+	/*
+	 * @see ModelObject#resolve(URL, ResourceBundle)
+	 */
+	public void resolve(URL base, URL bundleURL) throws MalformedURLException {
+
+		this.base = base;
+		this.bundleURL = bundleURL;
+
+	}
+
+	/**
+	 * Returns the timeline.
+	 * @return long
+	 */
+	public long getTimeline() {
+		return 0;
+//		if (!initialized) initialize();
+//		return timeline;
+	}
+
+
+	/*
+	 * initialize the configurations from the persistent model.
+	 */
+	private void initialize() {
+		
+		try {
+			try {
+				IPlatformConfiguration platformConfig = getPlatformConfiguration();
+				
+				new InstallConfigurationParser(platformConfig, this, false);
+			} catch (FileNotFoundException exception) {
+				UpdateCore.warn(locationURLString + " does not exist, The local site is not in synch with the file system and is pointing to a file that doesn't exist.", exception); //$NON-NLS-1$
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_ErrorDuringFileAccess, (new String[] { locationURLString })), exception);
+			} catch (IOException exception) {
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_ErrorDuringFileAccess, (new String[] { locationURLString })), exception);
+			}
+			
+		} catch (CoreException e) {
+			UpdateCore.warn("Error processing configuration history:" + locationURL.toExternalForm(), e); //$NON-NLS-1$
+		} finally {
+			initialized = true;
+		}
+		
+		//finish resolve
+		// PERF:
+		try {
+			// delegate
+			resolveListReference(getActivityModel(), base, bundleURL);
+			resolveListReference(getConfigurationSitesModel(), base, bundleURL);
+		} catch (MalformedURLException e){}		
+	}
+
+	private IPlatformConfiguration getPlatformConfiguration() throws IOException {
+		IPlatformConfiguration platformConfig;
+		if (UpdateManagerUtils.sameURL(getURL(), ConfiguratorUtils.getCurrentPlatformConfiguration().getConfigurationLocation()))
+			platformConfig = ConfiguratorUtils.getCurrentPlatformConfiguration();
+		else 
+			platformConfig = ConfiguratorUtils.getPlatformConfiguration(getURL());
+		return platformConfig;
+	}
+	
+	private void doLightInitialization() {
+		try {
+			try {
+				IPlatformConfiguration platformConfig = getPlatformConfiguration();
+			
+				new InstallConfigurationParser(platformConfig, this, true);
+			} catch (FileNotFoundException exception) {
+				UpdateCore.warn(locationURLString + " does not exist, The local site is not in synch with the file system and is pointing to a file that doesn't exist.", exception); //$NON-NLS-1$
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_ErrorDuringFileAccess, (new String[] { locationURLString })), exception);
+			} catch (IOException exception) {
+				throw Utilities.newCoreException(NLS.bind(Messages.InstallConfiguration_ErrorDuringFileAccess, (new String[] { locationURLString })), exception);
+			} 
+		} catch (CoreException e) {
+			UpdateCore.warn("Error processing configuration history:" + locationURL.toExternalForm(), e); //$NON-NLS-1$
+		} finally {
+			lightlyInitialized = true;
+		}
+	}
+
+	/*
+	 * 
+	 */
+	private void delayedResolve() {
+
+		// PERF: delay resolution
+		if (resolved)
+			return;
+
+		resolved = true;
+		// resolve local elements
+		try {
+			//locationURL = resolveURL(base, bundleURL, locationURLString);		
+			locationURL = new URL(locationURLString);
+		} catch (MalformedURLException e){
+			File f = new File(locationURLString);
+			try {
+				if (f.exists())
+					locationURL = f.toURL();
+				else
+					locationURL = base;
+			} catch (MalformedURLException e1) {
+				locationURL = base;
+			}
+		}
+	}
+	
+	public void resetActivities() {
+		activities = null;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/InstallConfigurationParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/InstallConfigurationParser.java
new file mode 100644
index 0000000..b367bb0
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/InstallConfigurationParser.java
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.model;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Date;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.SiteFeatureReference;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.core.model.SiteModel;
+import org.eclipse.update.internal.configurator.FeatureEntry;
+import org.eclipse.update.internal.configurator.PlatformConfiguration;
+import org.eclipse.update.internal.configurator.SiteEntry;
+import org.eclipse.update.internal.core.BaseSiteLocalFactory;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+
+
+/**
+ * parse the default site.xml
+ */
+
+public class InstallConfigurationParser {
+	private PlatformConfiguration platformConfig;
+	private URL siteURL;
+	private InstallConfigurationModel config;
+	private ConfiguredSiteModel configSite;
+
+	/**
+	 * Constructor for DefaultSiteParser
+	 */
+	public InstallConfigurationParser(
+		IPlatformConfiguration platformConfig,
+		InstallConfigurationModel config, boolean light)
+		throws IOException, CoreException {
+
+		Assert.isTrue(platformConfig instanceof PlatformConfiguration);
+		this.platformConfig = (PlatformConfiguration)platformConfig;
+		
+		this.config = config;
+
+		// DEBUG:		
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+			UpdateCore.debug("Start parsing Configuration:" + (config).getURL().toExternalForm()); //$NON-NLS-1$
+		}
+		
+		if (light) {
+			processConfigurationLight(this.platformConfig);
+		} else {
+			processConfig(this.platformConfig);
+		}
+	}
+	
+	
+
+
+
+	/** 
+	 * process the Site info
+	 */
+	private void processSite(SiteEntry siteEntry) throws CoreException, IOException {
+
+		//site url
+		siteURL = siteEntry.getURL();
+		try {
+			siteURL = FileLocator.toFileURL(siteURL);
+			// TODO workaround bug in platform url resolution
+			if (siteURL.getProtocol().equals("file")) //$NON-NLS-1$
+				siteURL = new File(siteURL.getFile()).toURL();
+		} catch (IOException e) {
+			// keep original url
+		}
+
+		// policy
+		ISite site = SiteManager.getSite(siteURL,null);
+
+		// configuration site
+		BaseSiteLocalFactory factory = new BaseSiteLocalFactory();
+		configSite = factory.createConfigurationSiteModel((SiteModel) site, siteEntry.getSitePolicy().getType());
+
+		//platform url
+		configSite.setPlatformURLString(siteEntry.getURL().toExternalForm());
+		
+		// configured
+		configSite.setEnabled(siteEntry.isEnabled());
+
+		// check if the site exists and is updatable
+	 	configSite.setUpdatable(siteEntry.isUpdateable());
+	 	
+		// add to install configuration
+	 	config.addConfigurationSiteModel(configSite);
+	 	configSite.setInstallConfigurationModel(config);
+		
+		FeatureEntry[] features = siteEntry.getFeatureEntries();
+		for (int i=0; i<features.length; i++) {
+			processFeature(features[i]);
+		}
+	}
+
+	/** 
+	 * process the DefaultFeature info
+	 */
+	private void processFeature(FeatureEntry feature) throws CoreException, IOException {
+
+		// url
+		String path = feature.getURL(); 
+		URL url = UpdateManagerUtils.getURL(siteURL, path, null);
+
+		if (url != null) {
+			SiteFeatureReference ref = new SiteFeatureReference();
+			ref.setSite((ISite) configSite.getSiteModel());
+			ref.setURL(url);
+			(configSite.getConfigurationPolicyModel()).addConfiguredFeatureReference(ref);
+
+			//updateURL
+//TODO do we need the update url and to resolve it?
+//			String updateURLString = attributes.getValue("updateURL"); //$NON-NLS-1$
+//			URLEntry entry = new URLEntry();
+//			entry.setURLString(updateURLString);
+//			entry.resolve(siteURL,null);
+
+			// DEBUG:		
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+				UpdateCore.debug("End Processing DefaultFeature Tag: url:" + url.toExternalForm()); //$NON-NLS-1$
+			}
+
+		} else {
+			UpdateCore.log( Messages.InstallConfigurationParser_FeatureReferenceNoURL, new Exception()); 
+		}
+
+	}
+
+	/** 
+	 * process the Activity info
+	 */
+//	private void processActivity(Attributes attributes) {
+//
+//		// action
+//		String actionString = attributes.getValue("action"); //$NON-NLS-1$
+//		int action = Integer.parseInt(actionString);
+//
+//		// create
+//		ConfigurationActivityModel activity =
+//			new BaseSiteLocalFactory().createConfigurationActivityModel();
+//		activity.setAction(action);
+//
+//		// label
+//		String label = attributes.getValue("label"); //$NON-NLS-1$
+//		if (label != null)
+//			activity.setLabel(label);
+//
+//		// date
+//		String dateString = attributes.getValue("date"); //$NON-NLS-1$
+//		Date date = new Date(Long.parseLong(dateString));
+//		activity.setDate(date);
+//
+//		// status
+//		String statusString = attributes.getValue("status"); //$NON-NLS-1$
+//		int status = Integer.parseInt(statusString);
+//		activity.setStatus(status);
+//
+//		config.addActivityModel(activity);
+//
+//		// DEBUG:		
+//		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+//			UpdateCore.debug("End Processing Activity: action:" + actionString + " label: " + label + " date:" + dateString + " status" + statusString); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+//		}
+//
+//	}
+
+	/** 
+	 * process the Config info
+	 */
+	private void processConfig(PlatformConfiguration platformConfig) throws IOException, CoreException {
+
+		// date
+		processConfigurationLight(platformConfig);
+		
+		//timeline
+//		String timelineString = attributes.getValue("timeline"); //$NON-NLS-1$
+//		long timeline = config.getCreationDate().getTime();
+//		if (timelineString!=null) {
+//			timeline = Long.parseLong(timelineString);
+//		}
+//		config.setTimeline(timeline);
+
+		SiteEntry[] sites = platformConfig.getConfiguration().getSites();
+		for (int i=0; i<sites.length; i++)
+			processSite(sites[i]);
+
+	}
+
+
+
+	private void processConfigurationLight(PlatformConfiguration platformConfig) {
+		Date date = new Date(platformConfig.getChangeStamp());
+		config.setCreationDate(date);
+		config.setLabel(date.toString());
+		
+		config.setCurrent( date.equals(org.eclipse.update.internal.configurator.PlatformConfiguration.getCurrent().getConfiguration().getDate()));
+	}
+	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteLocalModel.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteLocalModel.java
new file mode 100644
index 0000000..499d471
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteLocalModel.java
@@ -0,0 +1,286 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.model;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.MissingResourceException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.update.core.model.ModelObject;
+import org.eclipse.update.internal.core.BaseSiteLocalFactory;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+
+/**
+ * This class manages the configurations.
+ */
+
+public class SiteLocalModel extends ModelObject {
+	public static final String CONFIG_FILE = "platform.xml"; //$NON-NLS-1$
+	private long stamp;
+	private String label;
+	private URL location;
+	private String locationURLString;
+	private int history = UpdateCore.DEFAULT_HISTORY;
+	private List /* of InstallConfigurationModel */configurations;
+	private List /* of InstallConfigurationModel */preservedConfigurations;
+	private InstallConfigurationModel currentConfiguration;
+
+	/**
+	 * Constructor for LocalSite
+	 */
+	public SiteLocalModel(){
+		super();
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public InstallConfigurationModel getCurrentConfigurationModel() {
+		return currentConfiguration;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public InstallConfigurationModel[] getConfigurationHistoryModel() {
+		if (configurations==null)
+			// initialize history
+			processHistory();
+		
+		if (configurations == null || configurations.size() == 0)
+			return new InstallConfigurationModel[0];
+		else
+			return (InstallConfigurationModel[])configurations.toArray(arrayTypeFor(configurations));
+	}
+
+	/**
+	 * adds a new configuration to the LocalSite
+	 *  the newly added configuration is teh current one
+	 */
+	public void addConfigurationModel(InstallConfigurationModel config) {
+		if (config != null) {
+			if (configurations == null)
+				configurations = new ArrayList();
+			if (!configurations.contains(config))
+				configurations.add(config);
+		}
+	}
+
+	/**
+	 * adds a new configuration to the LocalSite
+	 *  the newly added configuration is teh current one
+	 */
+	public boolean removeConfigurationModel(InstallConfigurationModel config) {
+		if (config != null) {
+			return configurations.remove(config);
+		}
+		return false;
+	}
+	/**
+	 * Gets the location of the local site.
+	 * @return Returns a URL
+	 */
+	public URL getLocationURL() {
+		return location;
+	}
+
+	/**
+	 * Gets the locationURLString.
+	 * @return Returns a String
+	 */
+	public String getLocationURLString() {
+		return locationURLString;
+	}
+
+
+	/**
+	 * Sets the locationURLString.
+	 * @param locationURLString The locationURLString to set
+	 */
+	public void setLocationURLString(String locationURLString) {
+		assertIsWriteable();
+		this.locationURLString = locationURLString;
+		this.location=null;
+	}
+
+
+	/**
+	 * @since 2.0
+	 */
+	public String getLabel() {
+		return label;
+	}
+
+	/**
+	 * Sets the label.
+	 * @param label The label to set
+	 */
+	public void setLabel(String label) {
+		assertIsWriteable();
+		this.label = label;
+	}
+
+	
+	/**
+	 * @since 2.0
+	 */
+	public int getMaximumHistoryCount() {
+		return history;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public void setMaximumHistoryCount(int history) {
+		assertIsWriteable();
+		this.history = history;
+	}
+
+	
+	/**
+	 * Adds a preserved configuration into teh collection
+	 * do not save the configuration
+	 * @since 2.0
+	 */
+	public void addPreservedInstallConfigurationModel(InstallConfigurationModel configuration) {
+		if (preservedConfigurations == null)
+			preservedConfigurations = new ArrayList();
+
+		preservedConfigurations.add(configuration);
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public boolean removePreservedConfigurationModel(InstallConfigurationModel configuration) {
+		if (preservedConfigurations != null) {
+			return preservedConfigurations.remove(configuration);
+		}
+		return false;
+	}
+
+	/**
+	 * @since 2.0
+	 */
+	public InstallConfigurationModel[] getPreservedConfigurationsModel() {
+		if (preservedConfigurations==null || preservedConfigurations.isEmpty())
+			return new InstallConfigurationModel[0];
+		return (InstallConfigurationModel[])preservedConfigurations.toArray(arrayTypeFor(preservedConfigurations));
+	}
+
+
+	/**
+	 * Sets the currentConfiguration.
+	 * @param currentConfiguration The currentConfiguration to set
+	 */
+	public void setCurrentConfigurationModel(InstallConfigurationModel currentConfiguration) {
+		assertIsWriteable();
+		this.currentConfiguration = currentConfiguration;
+		
+		//2.0.2 set the configuredSite of sites
+		ConfiguredSiteModel[] confSites = currentConfiguration.getConfigurationSitesModel();
+		for (int i = 0; i < confSites.length; i++) {
+			confSites[i].getSiteModel().setConfiguredSiteModel(confSites[i]);
+		}
+	}
+
+	/*
+	 * @see ModelObject#resolve(URL)
+	 */
+	public void resolve(URL base,URL bundleURL) throws MalformedURLException {
+		// local
+		location = resolveURL(base,bundleURL,getLocationURLString());
+		
+		// delegate
+		resolveListReference(getConfigurationHistoryModel(),base,bundleURL);
+		resolveListReference(getPreservedConfigurationsModel(),base,bundleURL);
+		resolveReference(getCurrentConfigurationModel(),base,bundleURL);
+	}
+	
+
+	/**
+	 * Gets the stamp.
+	 * @return Returns a long
+	 */
+	public long getStamp() {
+		return stamp;
+	}
+
+	/**
+	 * Sets the stamp.
+	 * @param stamp The stamp to set
+	 */
+	public void setStamp(long stamp) {
+		this.stamp = stamp;
+	}
+
+	/**
+	 * @see org.eclipse.update.core.model.ModelObject#getPropertyName()
+	 */
+	protected String getPropertyName() {
+		return "platform"; //$NON-NLS-1$
+	}
+
+	/*
+	 * reads the configuration/history directory
+	 */
+	private void processHistory() {
+		try {
+			URL historyURL = new URL(getLocationURL(), "history"); //$NON-NLS-1$
+			historyURL = FileLocator.toFileURL(historyURL);
+			File historyDir = new File(historyURL.getFile());
+			if (historyDir.exists()) {
+				File[] backedConfigs = historyDir.listFiles();
+				BaseSiteLocalFactory factory = new BaseSiteLocalFactory();
+				for (int i=0; i<backedConfigs.length; i++) {
+					String name = backedConfigs[i].getName();
+					if (name.endsWith(".xml")) //$NON-NLS-1$
+						name = name.substring(0, name.length()-4);
+					else 
+						continue;
+					Date date = new Date(Long.parseLong(name));
+					InstallConfigurationModel config = factory.createInstallConfigurationModel();
+					config.setLocationURLString(backedConfigs[i].getAbsolutePath().replace('\\', '/'));
+					config.setLabel(date.toString());
+					config.setCreationDate(date);
+					config.resolve(backedConfigs[i].toURL(), getResourceBundleURL());
+	
+					// add the config
+					addConfigurationModel(config);
+				}
+			}
+		} catch (Exception e) {
+			UpdateCore.warn("Error processing history: ", e); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * return the appropriate resource bundle for this sitelocal
+	 */
+	URL getResourceBundleURL() throws CoreException {
+		URL url = null;
+		try {
+			url = UpdateManagerUtils.asDirectoryURL(getLocationURL());
+		} catch (MissingResourceException e) {
+			UpdateCore.warn(e.getLocalizedMessage() + ":" + url.toExternalForm()); //$NON-NLS-1$
+		} catch (MalformedURLException e) {
+			UpdateCore.warn(e.getLocalizedMessage()); 
+		}
+		return url;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteLocalParser.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteLocalParser.java
new file mode 100644
index 0000000..4b72663
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteLocalParser.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.model;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.update.configuration.ILocalSite;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.internal.configurator.PlatformConfiguration;
+import org.eclipse.update.internal.core.BaseSiteLocalFactory;
+import org.eclipse.update.internal.core.InstallConfiguration;
+import org.eclipse.update.internal.core.LocalSite;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+
+/**
+ * parse the default site.xml
+ */
+
+public class SiteLocalParser {
+
+	private PlatformConfiguration platformConfig;
+	private SiteLocalModel site;
+	public static final String CONFIG = "config"; //$NON-NLS-1$
+	private ResourceBundle bundle;
+	private BaseSiteLocalFactory factory = new BaseSiteLocalFactory();
+
+	/**
+	 * return the appropriate resource bundle for this sitelocal
+	 */
+	private ResourceBundle getResourceBundle() throws CoreException {
+		ResourceBundle bundle = null;
+		URL url = null;
+		try {
+			url = UpdateManagerUtils.asDirectoryURL(site.getLocationURL());
+			ClassLoader l = new URLClassLoader(new URL[] { url }, null);
+			bundle = ResourceBundle.getBundle("platform", Locale.getDefault(), l); //$NON-NLS-1$
+		} catch (MissingResourceException e) {
+			UpdateCore.warn(e.getLocalizedMessage() + ":" + url.toExternalForm()); //$NON-NLS-1$
+		} catch (MalformedURLException e) {
+			UpdateCore.warn(e.getLocalizedMessage()); 
+		}
+		return bundle;
+	}
+
+	/**
+	 * Constructor for DefaultSiteParser
+	 */
+	public SiteLocalParser(IPlatformConfiguration platformConfig, ILocalSite site) throws IOException, CoreException {
+		Assert.isTrue(platformConfig instanceof PlatformConfiguration);
+		this.platformConfig = (PlatformConfiguration)platformConfig;
+		
+		Assert.isTrue(site instanceof SiteLocalModel);
+		this.site = (SiteLocalModel) site;
+
+		// DEBUG:		
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+			UpdateCore.debug("Start parsing localsite:" + ((SiteLocalModel) site).getLocationURLString()); //$NON-NLS-1$
+		}
+		
+		bundle = getResourceBundle();
+		
+		processConfig();
+		//processHistory();
+	}
+
+	
+//	/** 
+//	 * process the Site info
+//	 */
+//	private void processSite(Attributes attributes) throws MalformedURLException {
+//		//
+//		String info = attributes.getValue("label"); //$NON-NLS-1$
+//		info = UpdateManagerUtils.getResourceString(info, bundle);
+//		site.setLabel(info);
+//	
+//		// history
+//		String historyString = attributes.getValue("history"); //$NON-NLS-1$
+//		int history;
+//		if (historyString == null || historyString.equals("")) { //$NON-NLS-1$
+//			history = SiteLocalModel.DEFAULT_HISTORY;
+//		} else {
+//			history = Integer.parseInt(historyString);
+//		}
+//		site.setMaximumHistoryCount(history);
+//	
+//		//stamp
+//		String stampString = attributes.getValue("stamp"); //$NON-NLS-1$
+//		long stamp = Long.parseLong(stampString);
+//		site.setStamp(stamp);
+//	
+//		// DEBUG:		
+//		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+//			UpdateCore.debug("End process Site label:" + info); //$NON-NLS-1$
+//		}
+//	
+//	}
+
+	/** 
+	 * process the Config info
+	 */
+	private void processConfig() throws MalformedURLException, CoreException {
+
+		String label = platformConfig.getConfiguration().getDate().toString();
+		label = UpdateManagerUtils.getResourceString(label, bundle);
+		site.setLabel(label);
+
+		URL url = site.getLocationURL();
+		InstallConfigurationModel config = factory.createInstallConfigurationModel();
+		config.setLocationURLString(url.toExternalForm());
+		config.setLabel(label);
+		config.resolve(url, site.getResourceBundleURL());
+
+		// add the config
+		((LocalSite)site).addConfiguration((InstallConfiguration)config);
+
+		// DEBUG:		
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_PARSING) {
+			UpdateCore.debug("End Processing Config Tag: url:" + url.toExternalForm()); //$NON-NLS-1$
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteWithTimestamp.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteWithTimestamp.java
new file mode 100644
index 0000000..a74d323
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/model/SiteWithTimestamp.java
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.model;
+
+import java.util.Date;
+
+import org.eclipse.update.core.Site;
+
+/**
+ * This is a wrapper class for Site class that adds timestamp
+ * 
+ *
+ */
+public class SiteWithTimestamp extends Site implements ITimestamp {
+
+	private Date timestamp;
+	
+	/*private Site site;
+	
+	public SiteWithTimestamp( Site site) {
+		this.site = site;
+	}*/
+	
+	public Date getTimestamp() {
+		return timestamp;
+	}
+
+	public void setTimestamp(Date timestamp) {
+		this.timestamp = timestamp;
+		
+	}
+/*
+	public boolean isReadOnly() {
+		return site.isReadOnly();
+	}
+
+	public Object getAdapter(Class adapter) {		
+		return site.getAdapter(adapter);
+	}
+
+	public void addPluginEntry(IPluginEntry pluginEntry) {		
+		site.addPluginEntry(pluginEntry);
+	}
+
+	public IFeature createFeature(String type, URL url) throws CoreException {	
+		return site.createFeature(type, url);
+	}
+
+	public IFeature createFeature(String type, URL url, IProgressMonitor monitor) throws CoreException {		
+		return site.createFeature(type, url, monitor);
+	}
+
+	public IArchiveReference[] getArchives() {		
+		return site.getArchives();
+	}
+
+	public ICategory[] getCategories() {		
+		return site.getCategories();
+	}
+
+	public ICategory getCategory(String key) {		
+		return site.getCategory(key);
+	}
+
+	public IConfiguredSite getCurrentConfiguredSite() {	
+		return site.getCurrentConfiguredSite();
+	}
+
+	public String getDefaultPackagedFeatureType() {	
+		return site.getDefaultPackagedFeatureType();
+	}
+
+	public IURLEntry getDescription() {	
+		return site.getDescription();
+	}
+
+	public long getDownloadSizeFor(IFeature feature) {	
+		return site.getDownloadSizeFor(feature);
+	}
+
+	public ISiteFeatureReference getFeatureReference(IFeature feature) {	
+		return site.getFeatureReference(feature);
+	}
+
+	public ISiteFeatureReference[] getFeatureReferences() {	
+		return site.getFeatureReferences();
+	}
+
+	public long getInstallSizeFor(IFeature feature) {	
+		return site.getInstallSizeFor(feature);
+	}
+
+	public IURLEntry[] getMirrorSiteEntries() {	
+		return site.getMirrorSiteEntries();
+	}
+
+	public IPluginEntry[] getPluginEntries() {	
+		return site.getPluginEntries();
+	}
+
+	public IPluginEntry[] getPluginEntriesOnlyReferencedBy(IFeature feature) throws CoreException {	
+		return site.getPluginEntriesOnlyReferencedBy(feature);
+	}
+
+	public int getPluginEntryCount() {		
+		return site.getPluginEntryCount();
+	}
+
+	public ISiteFeatureReference[] getRawFeatureReferences() {	
+		return site.getRawFeatureReferences();
+	}
+
+	public ISiteContentProvider getSiteContentProvider() throws CoreException {	
+		return site.getSiteContentProvider();
+	}
+
+	public URL getURL() {	
+		return site.getURL();
+	}
+
+	public IFeatureReference install(IFeature sourceFeature, IFeatureReference[] optionalFeatures, IFeatureContentConsumer parentContentConsumer, IVerifier parentVerifier, IVerificationListener verificationListener, IProgressMonitor progress) throws CoreException {	
+		return site.install(sourceFeature, optionalFeatures, parentContentConsumer,
+				parentVerifier, verificationListener, progress);
+	}
+
+	public IFeatureReference install(IFeature sourceFeature, IFeatureReference[] optionalFeatures, IVerificationListener verificationListener, IProgressMonitor progress) throws InstallAbortedException, CoreException {		
+		return site.install(sourceFeature, optionalFeatures, verificationListener,
+				progress);
+	}
+
+	public IFeatureReference install(IFeature sourceFeature, IVerificationListener verificationListener, IProgressMonitor progress) throws InstallAbortedException, CoreException {		
+		return site.install(sourceFeature, verificationListener, progress);
+	}
+
+	public void remove(IFeature feature, IProgressMonitor progress) throws CoreException {		
+		site.remove(feature, progress);
+	}
+
+	public void setSiteContentProvider(ISiteContentProvider siteContentProvider) {		
+		site.setSiteContentProvider(siteContentProvider);
+	}
+
+	public void addArchiveReferenceModel(ArchiveReferenceModel archiveReference) {		
+		site.addArchiveReferenceModel(archiveReference);
+	}
+
+	public void addCategoryModel(CategoryModel category) {		
+		site.addCategoryModel(category);
+	}
+
+	public void addFeatureReferenceModel(SiteFeatureReferenceModel featureReference) {		
+		site.addFeatureReferenceModel(featureReference);
+	}
+
+	public void addMirrorModel(URLEntryModel mirror) {		
+		site.addMirrorModel(mirror);
+	}
+
+	public ArchiveReferenceModel[] getArchiveReferenceModels() {		
+		return site.getArchiveReferenceModels();
+	}
+
+	public CategoryModel[] getCategoryModels() {	
+		return site.getCategoryModels();
+	}
+
+	public ConfiguredSiteModel getConfiguredSiteModel() {	
+		return site.getConfiguredSiteModel();
+	}
+
+	public URLEntryModel getDescriptionModel() {	
+		return site.getDescriptionModel();
+	}
+
+	public SiteFeatureReferenceModel[] getFeatureReferenceModels() {		
+		return site.getFeatureReferenceModels();
+	}
+
+	public URL getLocationURL() {		
+		return site.getLocationURL();
+	}
+
+	public String getLocationURLString() {		
+		return site.getLocationURLString();
+	}
+
+	public URLEntryModel[] getMirrorSiteEntryModels() {	
+		return site.getMirrorSiteEntryModels();
+	}
+
+	public String getType() {	
+		return site.getType();
+	}
+
+	public void markReadOnly() {	
+		site.markReadOnly();
+	}
+
+	public void removeArchiveReferenceModel(ArchiveReferenceModel archiveReference) {	
+		site.removeArchiveReferenceModel(archiveReference);
+	}
+
+	public void removeCategoryModel(CategoryModel category) {	
+		site.removeCategoryModel(category);
+	}
+
+	public void removeFeatureReferenceModel(FeatureReferenceModel featureReference) {	
+		site.removeFeatureReferenceModel(featureReference);
+	}
+
+	public void removeMirror(URLEntryModel mirror) {		
+		site.removeMirror(mirror);
+	}
+
+	public void resolve(URL base, URL bundleURL) throws MalformedURLException {		
+		site.resolve(base, bundleURL);
+	}
+
+	public void setArchiveReferenceModels(ArchiveReferenceModel[] archiveReferences) {		
+		site.setArchiveReferenceModels(archiveReferences);
+	}
+
+	public void setCategoryModels(CategoryModel[] categories) {		
+		site.setCategoryModels(categories);
+	}
+
+	public void setConfiguredSiteModel(ConfiguredSiteModel configuredSiteModel) {		
+		site.setConfiguredSiteModel(configuredSiteModel);
+	}
+
+	public void setDescriptionModel(URLEntryModel description) {		
+		site.setDescriptionModel(description);
+	}
+
+	public void setFeatureReferenceModels(FeatureReferenceModel[] featureReferences) {		
+		site.setFeatureReferenceModels(featureReferences);
+	}
+
+	public void setLocationURLString(String locationURLString) {		
+		site.setLocationURLString(locationURLString);
+	}
+
+	public void setMirrorSiteEntryModels(URLEntryModel[] mirrors) {		
+		site.setMirrorSiteEntryModels(mirrors);
+	}
+
+	public void setMirrorsURLString(String mirrorsURL) {		
+		site.setMirrorsURLString(mirrorsURL);
+	}
+
+	public void setType(String type) {		
+		site.setType(type);
+	}
+*/
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/BatchFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/BatchFeatureOperation.java
new file mode 100644
index 0000000..cf8436f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/BatchFeatureOperation.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.operations.IOperation;
+import org.eclipse.update.operations.IOperationListener;
+
+public abstract class BatchFeatureOperation extends Operation implements IBatchFeatureOperation {
+
+	private IFeature[] features;
+	private IConfiguredSite[] targetSites;
+	
+	
+	public BatchFeatureOperation(IConfiguredSite[] targetSites, IFeature[] features) {
+		super();
+		this.features = features;
+		this.targetSites = targetSites;
+	}
+	
+	public IFeature[] getFeatures() {
+		return features;
+	}
+
+	public IConfiguredSite[] getTargetSites() {
+		return targetSites;
+	}
+
+	public void setTargetSites(IConfiguredSite[] targetSites) {
+		this.targetSites = targetSites;
+		
+	}
+	
+	public boolean execute(IProgressMonitor pm, IOperationListener listener)
+		throws CoreException, InvocationTargetException {
+
+		if (getFeatures() == null || getFeatures().length == 0)
+			return false;
+		IOperation[] operations = new IOperation[getFeatures().length];
+		
+		for ( int i = 0; i < getFeatures().length; i ++) { 
+			operations[i] = createOperation(getTargetSites()[i], getFeatures()[i]);
+		}
+		
+		boolean restartNeeded = false;
+
+		for ( int i = 0; i < operations.length; i ++) { 
+			try {
+				boolean status = operations[i].execute(pm, listener);
+				if (status)
+					restartNeeded = true; 
+			} catch (Throwable t) {
+				t.printStackTrace();
+			}
+		}	
+
+		return restartNeeded;
+
+	}
+
+	protected abstract IOperation createOperation(IConfiguredSite targetSite, IFeature feature);
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/BatchInstallOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/BatchInstallOperation.java
new file mode 100644
index 0000000..d524d2d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/BatchInstallOperation.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.update.internal.operations;
+
+import java.lang.reflect.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.operations.*;
+
+public class BatchInstallOperation
+	extends Operation
+	implements IBatchOperation {
+		
+	protected IInstallFeatureOperation[] operations;
+
+	public BatchInstallOperation(IInstallFeatureOperation[] operations) {
+		super();
+		this.operations = operations;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.operations.IMultiOperation#getOperations()
+	 */
+	public IFeatureOperation[] getOperations() {
+		return operations;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.operations.IOperation#execute(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public boolean execute(IProgressMonitor monitor, IOperationListener listener) throws CoreException, InvocationTargetException {
+		int installCount = 0;
+
+		if (operations == null || operations.length == 0)
+			return false;
+					
+		IStatus status = OperationsManager.getValidator().validatePendingChanges(operations);
+		if (status != null && status.getCode() == IStatus.ERROR) {
+			throw new CoreException(status);
+		}
+		
+//		// Check for duplication conflicts
+//		ArrayList conflicts =
+//			DuplicateConflictsValidator.computeDuplicateConflicts(
+//				operations,
+//				config);
+//		if (conflicts != null) {
+//			boolean continueProcessing = false;
+//			if (listener != null)
+//				continueProcessing = listener.beforeExecute(this, conflicts);
+//			if (!continueProcessing)
+//				return false;
+//		}
+		
+		OperationsManager.setInProgress(true);
+		if (monitor == null) 
+			monitor = new NullProgressMonitor();
+			
+		try {			
+			if (listener != null)
+				listener.beforeExecute(this, null);
+			
+			monitor.beginTask(
+				Messages.OperationsManager_installing,
+				operations.length);
+			for (int i = 0; i < operations.length; i++) {
+				SubProgressMonitor subMonitor =
+					new SubProgressMonitor(
+						monitor,
+						1,
+						SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
+
+				operations[i].execute(subMonitor, listener);
+				OperationsManager.addPendingOperation(operations[i]);
+
+				operations[i].markProcessed();
+				if (listener != null)
+					listener.afterExecute(operations[i], null);
+
+				//monitor.worked(1);
+				installCount++;
+			}
+			return SiteManager.getLocalSite().save();
+        } catch (InstallAbortedException e) {
+            // saves the current configuration
+            if (installCount > 0) {
+                try {
+                    SiteManager.getLocalSite().save();
+                } catch (CoreException ce) {
+                    UpdateUtils.logException(ce);
+                }
+            }
+            throw new InvocationTargetException(e);
+        } catch (CoreException e) {
+            // saves the current configuration
+            if (installCount > 0) {
+                try {
+                    SiteManager.getLocalSite().save();
+                } catch (CoreException ce) {
+                    UpdateUtils.logException(ce);
+                }
+            }
+            throw new InvocationTargetException(e);
+        } finally {
+			OperationsManager.setInProgress(false);
+			monitor.done();
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ConfigOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ConfigOperation.java
new file mode 100644
index 0000000..3bb18c0
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ConfigOperation.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Configure a feature.
+ * ConfigOperation
+ */
+public class ConfigOperation
+extends FeatureOperation
+implements IConfigFeatureOperation {
+
+	public ConfigOperation(
+			IConfiguredSite site,
+			IFeature feature) {
+		super(site, feature);
+	}
+
+	public boolean execute(IProgressMonitor pm, IOperationListener listener)
+	throws CoreException {
+
+		IStatus status =
+			OperationsManager.getValidator().validatePendingConfig(feature);
+		if (status != null && status.getCode() == IStatus.ERROR) {
+			throw new CoreException(status);
+		}
+		try {
+			targetSite.configure(feature);
+			//ensureUnique();
+
+			// Restart not needed
+			boolean restartNeeded = false;
+
+			// Check if this operation is cancelling one that's already pending
+			IOperation pendingOperation =
+				OperationsManager.findPendingOperation(feature);
+
+			if (pendingOperation instanceof IUnconfigFeatureOperation) {
+				// no need to do either pending change
+				OperationsManager.removePendingOperation(pendingOperation);
+			} else {
+				OperationsManager.addPendingOperation(this);
+			}
+
+			markProcessed();
+			if (listener != null)
+				listener.afterExecute(this, null);
+
+			restartNeeded = SiteManager.getLocalSite().save() && restartNeeded;
+
+			// notify the model
+			OperationsManager.fireObjectChanged(feature, null);
+
+			return restartNeeded;
+		} catch (CoreException e) {
+			undo();
+			UpdateUtils.logException(e);
+			throw e;
+		}
+	}
+
+	public void undo() throws CoreException {
+		targetSite.unconfigure(feature);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ConfigureFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ConfigureFeaturesOperation.java
new file mode 100644
index 0000000..3247973
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ConfigureFeaturesOperation.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.operations.IOperation;
+import org.eclipse.update.operations.OperationsManager;
+
+public class ConfigureFeaturesOperation extends BatchFeatureOperation implements
+		IConfigureFeaturesOperation {
+
+	public ConfigureFeaturesOperation(IConfiguredSite[] targetSites,
+			IFeature[] features) {
+		super(targetSites, features);
+	}
+
+	
+	protected IOperation createOperation(IConfiguredSite targetSite, IFeature feature) {
+		return OperationsManager.getOperationFactory().createConfigOperation(targetSite, feature);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/DuplicateConflictsValidator.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/DuplicateConflictsValidator.java
new file mode 100644
index 0000000..dda495e
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/DuplicateConflictsValidator.java
@@ -0,0 +1,225 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.IIncludedFeatureReference;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.operations.IInstallFeatureOperation;
+
+
+/**
+ * 
+ */
+public class DuplicateConflictsValidator  {
+
+	public static class IdEntry {
+		IConfiguredSite csite;
+		IFeature feature;
+
+		public IdEntry(IFeature feature, IConfiguredSite csite) {
+			this.feature = feature;
+			this.csite = csite;
+			if (csite == null) {
+				System.out.println("csite null"); //$NON-NLS-1$
+			}
+		}
+		public boolean isInstallCandidate() {
+			return csite != null;
+		}
+		public IFeature getFeature() {
+			return feature;
+		}
+
+		public String getIdentifier() {
+			return feature.getVersionedIdentifier().getIdentifier();
+		}
+		public IConfiguredSite getConfiguredSite() {
+			if (csite != null)
+				return csite;
+			return feature.getSite().getCurrentConfiguredSite();
+		}
+		public boolean sameLevel(IdEntry entry) {
+			VersionedIdentifier vid = feature.getVersionedIdentifier();
+			VersionedIdentifier evid =
+				entry.getFeature().getVersionedIdentifier();
+			return vid.equals(evid);
+		}
+		public String toString() {
+			IConfiguredSite configSite = getConfiguredSite();
+			String version =
+				feature.getVersionedIdentifier().getVersion().toString();
+			String location = configSite.getSite().getURL().getFile();
+			return NLS.bind(Messages.DuplicateConflictsDialog_conflict, (new String[] { version, location }));
+		}
+	}
+
+	public static ArrayList computeDuplicateConflicts(
+		IInstallFeatureOperation job,
+		IInstallConfiguration config,
+		IConfiguredSite targetSite,
+		IFeatureReference[] optionalFeatures) {
+		Hashtable featureTable = new Hashtable();
+		try {
+			computePresentState(featureTable, config);
+			computeNewFeature(
+				job.getFeature(),
+				targetSite,
+				featureTable,
+				optionalFeatures);
+			return computeConflicts(featureTable);
+		} catch (CoreException e) {
+			return null;
+		}
+	}
+
+	public static ArrayList computeDuplicateConflicts(
+		IInstallFeatureOperation[] jobs,
+		IInstallConfiguration config) {
+		Hashtable featureTable = new Hashtable();
+		computePresentState(featureTable, config);
+		computeNewFeatures(jobs, featureTable);
+		return computeConflicts(featureTable);
+	}
+
+	private static ArrayList computeConflicts(Hashtable featureTable) {
+		ArrayList result = null;
+		for (Enumeration iterator = featureTable.elements();
+			iterator.hasMoreElements();
+			) {
+			ArrayList candidate = (ArrayList) iterator.nextElement();
+			if (candidate.size() == 1)
+				continue;
+			ArrayList conflict = checkForConflict(candidate);
+			if (conflict != null) {
+				if (result == null)
+					result = new ArrayList();
+				result.add(conflict);
+			}
+		}
+		return result;
+	}
+
+	private static ArrayList checkForConflict(ArrayList candidate) {
+		IdEntry firstEntry = null;
+		for (int i = 0; i < candidate.size(); i++) {
+			IdEntry entry = (IdEntry) candidate.get(i);
+			if (firstEntry == null)
+				firstEntry = entry;
+			else if (!entry.sameLevel(firstEntry))
+				return candidate;
+		}
+		return null;
+	}
+
+	private static void computePresentState(
+		Hashtable table,
+		IInstallConfiguration config) {
+		IConfiguredSite[] csites = config.getConfiguredSites();
+		for (int i = 0; i < csites.length; i++) {
+			IConfiguredSite csite = csites[i];
+			IFeatureReference[] refs = csite.getConfiguredFeatures();
+			for (int j = 0; j < refs.length; j++) {
+				try {
+					addEntry(refs[j].getFeature(null), csite, table);
+				} catch (CoreException e) {
+					// don't let one bad feature stop the loop
+				}
+			}
+		}
+	}
+
+	private static void computeNewFeatures(
+		IInstallFeatureOperation[] jobs,
+		Hashtable featureTable) {
+		for (int i = 0; i < jobs.length; i++) {
+			IInstallFeatureOperation job = jobs[i];
+			IConfiguredSite targetSite = job.getTargetSite();
+			IFeature newFeature = job.getFeature();
+			try {
+				computeNewFeature(newFeature, targetSite, featureTable, null);
+			} catch (CoreException e) {
+			}
+		}
+	}
+
+	private static void computeNewFeature(
+		IFeature feature,
+		IConfiguredSite csite,
+		Hashtable table,
+		IFeatureReference[] optionalFeatures)
+		throws CoreException {
+		addEntry(feature, csite, table);
+		IIncludedFeatureReference[] irefs =
+			feature.getIncludedFeatureReferences();
+		for (int i = 0; i < irefs.length; i++) {
+			IIncludedFeatureReference iref = irefs[i];
+			boolean add = true;
+
+			if (iref.isOptional() && optionalFeatures != null) {
+				boolean found = false;
+				for (int j = 0; j < optionalFeatures.length; j++) {
+					IFeatureReference checked = optionalFeatures[j];
+					if (checked.equals(iref)) {
+						found = true;
+						break;
+					}
+				}
+				add = found;
+			}
+			if (add)
+				computeNewFeature(
+					iref.getFeature(null),
+					csite,
+					table,
+					optionalFeatures);
+		}
+	}
+
+	private static void addEntry(
+		IFeature feature,
+		IConfiguredSite csite,
+		Hashtable featureTable) {
+		String id = feature.getVersionedIdentifier().getIdentifier();
+		ArrayList entries = (ArrayList) featureTable.get(id);
+		if (entries == null) {
+			entries = new ArrayList();
+			featureTable.put(id, entries);
+		}
+		IdEntry entry = new IdEntry(feature, csite);
+		boolean replaced = false;
+		for (int i = 0; i < entries.size(); i++) {
+			IdEntry existingEntry = (IdEntry) entries.get(i);
+			IConfiguredSite existingSite = existingEntry.getConfiguredSite();
+			if (existingSite.equals(entry.getConfiguredSite())) {
+				// same site - replace it if not new
+				if (entry.isInstallCandidate()) {
+					entries.set(i, entry);
+					entries.remove(existingEntry);
+				}
+				replaced = true;
+				break;
+			}
+		}
+		if (!replaced)
+			entries.add(entry);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureHierarchyElement.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureHierarchyElement.java
new file mode 100644
index 0000000..1e71d39
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureHierarchyElement.java
@@ -0,0 +1,446 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.operations;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * This class is used to construct a joint feature hiearchy.
+ * Old feature reference represents feature that is
+ * found on in the current configuration. New feature
+ * reference is found in the feature that is an install/update
+ * candidate. The element is used to join nodes of the
+ * hiearchy formed by including features so that
+ * each node in the hiearchy contains references to the
+ * old and the new feature. Old and new features have
+ * the same IDs but different versions, except in 
+ * the case of optional features, where the tree may
+ * be constructed to bring in an optional feature
+ * that was not installed initially. In that case,
+ * some nodes may have old an new references with the
+ * same ID and version. 
+ * <p>
+ * Old feature reference may be null. That means
+ * that the older feature with the same ID but lower
+ * version was not found in the current configuration.
+ */
+public class FeatureHierarchyElement {
+
+	private Object root;
+	private ArrayList children;
+	private IFeatureReference oldFeatureRef;
+	private IFeatureReference newFeatureRef;
+	private boolean checked;
+	private boolean optionalChildren;
+	private boolean nativeUpgrade = false;
+
+	public FeatureHierarchyElement(
+		IFeatureReference oldRef,
+		IFeatureReference newRef) {
+		oldFeatureRef = oldRef;
+		newFeatureRef = newRef;
+	}
+
+	public void setRoot(Object root) {
+		this.root = root;
+	}
+
+	public Object getRoot() {
+		return root;
+	}
+
+	/*
+	 * Return true if element can be checked, false otherwise.
+	 */
+	public boolean isEditable() {
+		// cannot uncheck non-optional features
+		if (isOptional() == false)
+			return false;
+		// cannot uncheck optional feature that
+		// has already been installed
+		if (oldFeatureRef != null)
+			return false;
+		return true;
+	}
+
+	/**
+	 * A hirearchy node represents a 'false update' if
+	 * both old and new references exist and both
+	 * point to the feature with the same ID and version.
+	 * These nodes will not any bytes to be downloaded - 
+	 * they simply exist to allow the hirarchy to
+	 * reach the optional children that are missing
+	 * and will be installed.
+	 */
+
+	public boolean isFalseUpdate() {
+		if (oldFeatureRef != null && newFeatureRef != null) {
+			try {
+				return oldFeatureRef.getVersionedIdentifier().equals(
+					newFeatureRef.getVersionedIdentifier());
+			} catch (CoreException e) {
+			}
+		}
+		return false;
+	}
+	/**
+	 * Returns true if feature is included as optional.
+	 */
+	public boolean isOptional() {
+		return newFeatureRef instanceof IIncludedFeatureReference
+			&& ((IIncludedFeatureReference) newFeatureRef).isOptional();
+	}
+	/**
+	 * Returns true if this optional feature is selected
+	 * for installation. Non-optional features or non-editable
+	 * features are always checked.
+	 */
+	public boolean isChecked() {
+		return checked;
+	}
+
+	void setNativeUpgrade(boolean nativeUpgrade) {
+		this.nativeUpgrade = nativeUpgrade;
+	}
+
+	/**
+	 * Returns true if this optional feature should
+	 * be enabled when installed. By default, all
+	 * features in the hiearchy should be enabled.
+	 * The exception is for optional features that
+	 * are updated to a new version in case where
+	 * the older version of the optional feature
+	 * is disabled in the given configuration. 
+	 * In this case, the feature is
+	 * updated and disabled in order to maintain
+	 * its state.
+	 */
+	public boolean isEnabled(IInstallConfiguration config) {
+		if (nativeUpgrade)
+			return true;
+		if (isOptional() && oldFeatureRef != null) {
+			try {
+				IFeature oldFeature = oldFeatureRef.getFeature(null);
+				IConfiguredSite csite =
+					UpdateUtils.getConfigSite(oldFeature, config);
+				return csite.isConfigured(oldFeature);
+			} catch (CoreException e) {
+			}
+		}
+		return true;
+	}
+
+	public IFeature getFeature() {
+		try {
+			IFeature feature = newFeatureRef.getFeature(null);
+			return feature;
+		} catch (CoreException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Selects an editable feature for installation.
+	 */
+	public void setChecked(boolean checked) {
+		this.checked = checked;
+	}
+	/**
+	 * Returns label for UI presentation.
+	 */
+	public String getLabel() {
+		try {
+			return getFeatureLabel(newFeatureRef);
+		} catch (CoreException e) {
+			if (newFeatureRef instanceof IIncludedFeatureReference) {
+				String iname =
+					((IIncludedFeatureReference) newFeatureRef).getName();
+				if (iname != null)
+					return iname;
+			}
+			try {
+				VersionedIdentifier vid =
+					newFeatureRef.getVersionedIdentifier();
+				return vid.toString();
+			} catch (CoreException e2) {
+			}
+		}
+		return null;
+	}
+	/**
+	 * Computes label from the feature.
+	 */
+	private String getFeatureLabel(IFeatureReference featureRef)
+		throws CoreException {
+		IFeature feature = featureRef.getFeature(null);
+		return feature.getLabel()
+			+ " " //$NON-NLS-1$
+			+ feature.getVersionedIdentifier().getVersion().toString();
+	}
+	/**
+	 * Computes children by linking matching features from the
+	 * old feature's and new feature's hierarchy.
+	 */
+	public FeatureHierarchyElement[] getChildren(
+		boolean update,
+		boolean patch,
+		IInstallConfiguration config) {
+		computeChildren(update, patch, config);
+		FeatureHierarchyElement[] array =
+			new FeatureHierarchyElement[children.size()];
+		children.toArray(array);
+		return array;
+	}
+
+	public FeatureHierarchyElement[] getChildren() {
+		if (children != null) {
+			FeatureHierarchyElement[] array =
+				new FeatureHierarchyElement[children.size()];
+			children.toArray(array);
+			return array;
+		}
+
+		return new FeatureHierarchyElement[0];
+	}
+	/**
+	 * Computes children of this node.
+	 */
+	public void computeChildren(
+		boolean update,
+		boolean patch,
+		IInstallConfiguration config) {
+		if (children == null) {
+			children = new ArrayList();
+			try {
+				IFeature oldFeature = null;
+				IFeature newFeature = null;
+				newFeature = newFeatureRef.getFeature(null);
+				if (oldFeatureRef != null)
+					oldFeature = oldFeatureRef.getFeature(null);
+				optionalChildren =
+					computeElements(
+						oldFeature,
+						newFeature,
+						update,
+						patch,
+						config,
+						children);
+				for (int i = 0; i < children.size(); i++) {
+					FeatureHierarchyElement element =
+						(FeatureHierarchyElement) children.get(i);
+					element.setRoot(getRoot());
+				}
+			} catch (CoreException e) {
+			}
+		}
+	}
+	/**
+	 * 
+	 */
+	public boolean hasOptionalChildren() {
+		return optionalChildren;
+	}
+	/**
+	 * Adds checked optional features to the provided set.
+	 */
+	public void addCheckedOptionalFeatures(
+		boolean update,
+		boolean patch,
+		IInstallConfiguration config,
+		Set set) {
+		if (isOptional() && isChecked()) {
+			// Do not add checked optional features
+			// if this is an update case but
+			// the node is not a 'true' update
+			// (old and new feature are the equal)
+			if (!update || !isFalseUpdate())
+				set.add(newFeatureRef);
+		}
+		FeatureHierarchyElement[] elements = getChildren(update, patch, config);
+		for (int i = 0; i < elements.length; i++) {
+			elements[i].addCheckedOptionalFeatures(update, patch, config, set);
+		}
+	}
+
+	/**
+	 * Computes first-level children of the linked hierarchy
+	 * for the provided old and new features (same ID, different version
+	 * where new version is greater or equal the old version).
+	 * Old feature may be null. 
+	 */
+	public static boolean computeElements(
+		IFeature oldFeature,
+		IFeature newFeature,
+		boolean update,
+		boolean patch,
+		IInstallConfiguration config,
+		ArrayList list) {
+		Object[] oldChildren = null;
+		Object[] newChildren = getIncludedFeatures(newFeature);
+		boolean optionalChildren = false;
+
+		try {
+			if (oldFeature != null) {
+				oldChildren = getIncludedFeatures(oldFeature);
+			}
+			for (int i = 0; i < newChildren.length; i++) {
+				IFeatureReference oldRef = null;
+				IFeatureReference newRef = (IFeatureReference) newChildren[i];
+				if (oldChildren != null) {
+					String newId =
+						newRef.getVersionedIdentifier().getIdentifier();
+
+					for (int j = 0; j < oldChildren.length; j++) {
+						IFeatureReference cref =
+							(IFeatureReference) oldChildren[j];
+						try {
+							if (cref
+								.getVersionedIdentifier()
+								.getIdentifier()
+								.equals(newId)) {
+								oldRef = cref;
+								break;
+							}
+						} catch (CoreException ex) {
+						}
+					}
+				} else if (patch) {
+					// 30849 - find the old reference in the
+					// configuration.
+					if (!UpdateUtils.isPatch(newFeature)) {
+						oldRef = findPatchedReference(newRef, config);
+					}
+				}
+				// test if the old optional feature exists
+				if (oldRef != null
+					&& ((oldRef instanceof IIncludedFeatureReference
+						&& ((IIncludedFeatureReference) oldRef).isOptional())
+						|| patch)) {
+					try {
+						IFeature f = oldRef.getFeature(null);
+						if (f == null)
+							oldRef = null;
+					} catch (CoreException e) {
+						// missing
+						oldRef = null;
+					}
+				}
+				FeatureHierarchyElement element =
+					new FeatureHierarchyElement(oldRef, newRef);
+				// If this is an update (old feature exists), 
+				// only check the new optional feature if the old exists.
+				// Otherwise, always check.
+				if (element.isOptional() && (update || patch)) {
+					element.setChecked(oldRef != null);
+					if (oldRef == null) {
+						// Does not have an old reference,
+						// but it may contain an older
+						// feature that may still qualify
+						// for update. For example,
+						// an older version may have been
+						// installed natively from the CD-ROM.
+						if (hasOlderVersion(newRef)) {
+							element.setNativeUpgrade(true);
+							element.setChecked(true);
+						}
+					}
+				} else
+					element.setChecked(true);
+				list.add(element);
+				element.computeChildren(update, patch, config);
+				if (element.isOptional() || element.hasOptionalChildren())
+					optionalChildren = true;
+			}
+		} catch (CoreException e) {
+		}
+		return optionalChildren;
+	}
+	public static boolean hasOlderVersion(IFeatureReference newRef) {
+		try {
+			VersionedIdentifier vid = newRef.getVersionedIdentifier();
+			PluginVersionIdentifier version = vid.getVersion();
+			String mode = getUpdateVersionsMode();
+
+			IFeature[] allInstalled =
+				UpdateUtils.getInstalledFeatures(vid, false);
+			for (int i = 0; i < allInstalled.length; i++) {
+				IFeature candidate = allInstalled[i];
+				PluginVersionIdentifier cversion =
+					candidate.getVersionedIdentifier().getVersion();
+				// Verify that the difference qualifies as
+				// an update.
+				if (mode.equals(UpdateCore.EQUIVALENT_VALUE)) {
+					if (version.isEquivalentTo(cversion))
+						return true;
+				} else if (mode.equals(UpdateCore.COMPATIBLE_VALUE)) {
+					if (version.isCompatibleWith(cversion))
+						return true;
+				}
+			}
+		} catch (CoreException e) {
+		}
+		return false;
+	}
+
+	private static IFeatureReference findPatchedReference(
+		IFeatureReference newRef,
+		IInstallConfiguration config)
+		throws CoreException {
+		VersionedIdentifier vid = newRef.getVersionedIdentifier();
+		IConfiguredSite[] csites = config.getConfiguredSites();
+		for (int i = 0; i < csites.length; i++) {
+			IConfiguredSite csite = csites[i];
+			IFeatureReference[] refs = csite.getConfiguredFeatures();
+			for (int j = 0; j < refs.length; j++) {
+				IFeatureReference ref = refs[j];
+				VersionedIdentifier refVid = ref.getVersionedIdentifier();
+				if (vid.getIdentifier().equals(refVid.getIdentifier()))
+					return ref;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Returns included feature references for the given reference.
+	 */
+	public static Object[] getIncludedFeatures(IFeatureReference ref) {
+		try {
+			IFeature feature = ref.getFeature(null);
+			return getIncludedFeatures(feature);
+		} catch (CoreException e) {
+		}
+		return new Object[0];
+	}
+
+	/**
+	 * Returns included feature references for the given feature.
+	 */
+
+	public static Object[] getIncludedFeatures(IFeature feature) {
+		try {
+			return feature.getIncludedFeatureReferences();
+		} catch (CoreException e) {
+		}
+		return new Object[0];
+	}
+
+	private static String getUpdateVersionsMode() {
+		Preferences store = UpdateCore.getPlugin().getPluginPreferences();
+		return store.getString(UpdateCore.P_UPDATE_VERSIONS);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureOperation.java
new file mode 100644
index 0000000..dd7f988
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureOperation.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+
+public abstract class FeatureOperation extends Operation implements IFeatureOperation {
+	
+	protected IFeature feature;
+	protected IFeature oldFeature;
+	protected IConfiguredSite targetSite;
+
+//	private boolean optionalDelta;
+	
+	
+	public FeatureOperation(IConfiguredSite targetSite, IFeature feature) {
+		super();
+		this.feature = feature;
+		this.targetSite = targetSite;
+	}
+
+	public IFeature getFeature() {
+		return feature;
+	}
+	
+	public IFeature getOldFeature() {
+		return oldFeature;
+	}
+	
+//	public boolean isOptionalDelta() {
+//		return optionalDelta;
+//	}
+	
+	public IConfiguredSite getTargetSite() {
+		return targetSite;
+	}
+
+	public void setTargetSite(IConfiguredSite targetSite) {
+		this.targetSite = targetSite;
+	}
+	
+
+	static boolean unconfigure(IFeature feature, IConfiguredSite site)
+		throws CoreException {
+		IInstallConfiguration config = SiteManager.getLocalSite().getCurrentConfiguration();
+		if (site == null)
+			site = UpdateUtils.getConfigSite(feature, config);
+		
+		if (site != null) {
+			PatchCleaner cleaner = new PatchCleaner(site, feature);
+			boolean result = site.unconfigure(feature);
+			cleaner.dispose();
+			return result;
+		}
+		return false;
+	}
+
+	public void setFeature(IFeature feature) {
+		this.feature = feature;
+	}
+	
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureStatus.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureStatus.java
new file mode 100644
index 0000000..982ed53
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/FeatureStatus.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+
+
+/**
+ * The feature status provide info about the broken features and what is
+ * wrong.
+ */
+public class FeatureStatus extends Status {
+	public final static int CODE_OTHER = IStatus.OK;//0
+	public final static int CODE_PREREQ_FEATURE = 1;
+	public final static int CODE_PREREQ_PLUGIN = 2;
+	public final static int CODE_EXCLUSIVE = 4;
+	public final static int CODE_CYCLE = 8;
+	public final static int CODE_OPTIONAL_CHILD = 16;
+	public final static int CODE_ENVIRONMENT = 32;
+	IFeature feature;
+
+	public FeatureStatus(IFeature feature, int severity, String pluginId, int code, String message, Throwable exception) {
+		super(severity, pluginId, code, message, exception);
+		this.feature = feature;
+	}
+	public IFeature getFeature() {
+		return feature;
+	}
+	public boolean equals(Object obj) {
+		if (!(obj instanceof FeatureStatus))
+			return false;
+		FeatureStatus fs = (FeatureStatus) obj;
+		// only check for feature, regardless of status type
+		if (fs.getFeature() == feature)
+			return true;
+		else if (fs.getFeature() == null && feature == null)
+			return fs.getMessage().equals(getMessage());
+		else if (fs.getFeature() == null && feature != null)
+			return false;
+		else if (fs.getFeature() != null && feature == null)
+			return false;
+		else if (fs.getFeature().equals(feature))
+			return true;
+		else
+			return false;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IBatchFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IBatchFeatureOperation.java
new file mode 100644
index 0000000..75d7ada
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IBatchFeatureOperation.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.operations.IOperation;
+
+public interface IBatchFeatureOperation extends IOperation {
+
+	/**
+	 * Returns the features to operate on.
+	 * @return the features to operate on.
+	 */
+	public abstract IFeature[] getFeatures();
+	/**
+	 * Returns the site in which the operation is applied.
+	 * @return the site that owns or will own the feature.
+	 */
+	public abstract IConfiguredSite[] getTargetSites();
+
+	/**
+	 * Sets the site in which the feature is being operated on.
+	 * @param targetSite the site in which the featre is being operated on.
+	 */
+	public abstract void setTargetSites(IConfiguredSite[] targetSite);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IConfigureFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IConfigureFeaturesOperation.java
new file mode 100644
index 0000000..b1c3534
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IConfigureFeaturesOperation.java
@@ -0,0 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+public interface IConfigureFeaturesOperation extends IBatchFeatureOperation {
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureAndUninstallFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureAndUninstallFeatureOperation.java
new file mode 100644
index 0000000..ce5006d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureAndUninstallFeatureOperation.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import org.eclipse.update.operations.IFeatureOperation;
+
+public interface IUnconfigureAndUninstallFeatureOperation extends
+		IFeatureOperation {
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureAndUninstallFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureAndUninstallFeaturesOperation.java
new file mode 100644
index 0000000..de9216f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureAndUninstallFeaturesOperation.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+public interface IUnconfigureAndUninstallFeaturesOperation extends
+		IBatchFeatureOperation {
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureFeaturesOperation.java
new file mode 100644
index 0000000..bdf5d5d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUnconfigureFeaturesOperation.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+
+public interface IUnconfigureFeaturesOperation extends IBatchFeatureOperation {
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUninstallFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUninstallFeaturesOperation.java
new file mode 100644
index 0000000..46e6117
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/IUninstallFeaturesOperation.java
@@ -0,0 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+public interface IUninstallFeaturesOperation extends IBatchFeatureOperation {
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/InstallOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/InstallOperation.java
new file mode 100644
index 0000000..a282a70
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/InstallOperation.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.operations;
+
+import java.util.HashSet;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.operations.*;
+
+/**
+ * Configure a feature.
+ * ConfigOperation
+ */
+public class InstallOperation
+	extends FeatureOperation
+	implements IInstallFeatureOperation {
+	
+	public boolean equals(Object arg) {
+		if (arg == null) {
+			return false;
+		}
+		
+		if (!(arg instanceof InstallOperation)) {
+			return false;
+		}
+		
+		InstallOperation io = (InstallOperation)arg;
+		
+		return io.getFeature().getVersionedIdentifier().equals(this.getFeature().getVersionedIdentifier());
+	}
+
+	public int hashCode() {
+		// TODO Auto-generated method stub
+		return super.getFeature().hashCode();
+	}
+
+	private IFeatureReference[] optionalFeatures;
+	private IFeature[] unconfiguredOptionalFeatures;
+	private IVerificationListener verifier;
+
+	/**
+	 * Constructor
+	 * @param site
+	 * @param feature
+	 * @param optionalFeatures optional features to install. If null, the operation will install them all (if any)
+	 * @param unconfiguredOptionalElements optional features unconfigured before the operation. They should remain unconfigured after the install.
+	 * @param verifier
+	 */
+	public InstallOperation(
+		IConfiguredSite site,
+		IFeature feature,
+		IFeatureReference[] optionalFeatures,
+		IFeature[] unconfiguredOptionalElements,
+		IVerificationListener verifier) {
+		super(site, feature);
+		IFeature[] installed = UpdateUtils.getInstalledFeatures(feature);
+		if (installed.length > 0)
+			this.oldFeature = installed[0];
+		this.unconfiguredOptionalFeatures = unconfiguredOptionalElements;
+		this.optionalFeatures = optionalFeatures;
+		this.verifier = verifier;
+	}
+
+	public IFeatureReference[] getOptionalFeatures() {
+		return optionalFeatures;
+	}
+
+	public boolean execute(IProgressMonitor pm, IOperationListener listener)
+		throws CoreException {
+
+		boolean reinstall = false;
+		if (oldFeature != null
+			&& feature.getVersionedIdentifier().equals(
+				oldFeature.getVersionedIdentifier()))
+			reinstall = true;
+
+		setOptionalFeatures();
+
+		if (optionalFeatures == null)
+			targetSite.install(feature, verifier, pm);
+		else
+			targetSite.install(feature, optionalFeatures, verifier, pm);
+
+		if (!reinstall) {
+
+			if (oldFeature != null) { //&& isOptionalDelta()) {
+				preserveOptionalState();
+
+				boolean oldSuccess = unconfigure(oldFeature, null); // pick any site containing old feature
+				if (!oldSuccess) {
+					IInstallConfiguration config = SiteManager.getLocalSite().getCurrentConfiguration();
+					if (!UpdateUtils.isNestedChild(config, oldFeature)) {
+						// "eat" the error if nested child
+						String message =
+							NLS.bind(Messages.OperationsManager_error_old, (new String[] { oldFeature.getLabel() }));
+						IStatus status =
+							new Status(
+								IStatus.ERROR,
+								UpdateUtils.getPluginId(),
+								IStatus.OK,
+								message,
+								null);
+						throw new CoreException(status);
+					}
+				}
+			}
+
+//			if (oldFeature == null) {
+//				ensureUnique();
+//			}
+		}
+		return true;
+	}
+
+	private void preserveOptionalState() {
+		if (unconfiguredOptionalFeatures == null)
+			return;
+
+		for (int i = 0; i < unconfiguredOptionalFeatures.length; i++) {
+			try {
+				// Get the feature that matches the original unconfigured ones.
+				IFeature localFeature =
+					UpdateUtils.getLocalFeature(
+						targetSite,
+						unconfiguredOptionalFeatures[i]);
+				if (localFeature != null)
+					targetSite.unconfigure(localFeature);
+
+			} catch (CoreException e) {
+				// Ignore this - we will leave with it
+			}
+		}
+	}
+	
+	private void setOptionalFeatures() {
+		try {
+			// Ensure optional features are correctly set
+			if (optionalFeatures == null && UpdateUtils.hasOptionalFeatures(feature) ) {
+				JobRoot jobRoot = new JobRoot(this);
+				IInstallConfiguration config = SiteManager.getLocalSite().getCurrentConfiguration();
+				HashSet set = new HashSet();
+				boolean update = oldFeature != null;
+				boolean patch = UpdateUtils.isPatch(feature);
+				FeatureHierarchyElement[] elements = jobRoot.getElements();
+				for (int i = 0; i < elements.length; i++) {
+					elements[i].addCheckedOptionalFeatures(update, patch, config, set);
+				}
+				optionalFeatures = new IFeatureReference[set.size()];
+				set.toArray(optionalFeatures);
+				unconfiguredOptionalFeatures = jobRoot.getUnconfiguredOptionalFeatures(config, targetSite);
+			}
+		} catch (CoreException e) {
+			UpdateUtils.logException(e);
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/JobRoot.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/JobRoot.java
new file mode 100644
index 0000000..bc4a535
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/JobRoot.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.operations;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+public class JobRoot {
+	private IInstallFeatureOperation job;
+	private FeatureHierarchyElement[] elements;
+
+	public JobRoot(IInstallFeatureOperation job) {
+		this.job = job;
+	}
+
+	public IInstallFeatureOperation getJob() {
+		return job;
+	}
+
+	public FeatureHierarchyElement[] getElements() {
+		if (elements == null)
+			computeElements();
+		return elements;
+	}
+
+	/**
+	 * Returns unconfigured features before an install.
+	 * After installing the features, the caller must get the local features that match
+	 * these unconfigured features and unconfigure them.
+	 * @param config
+	 * @param targetSite
+	 * @return
+	 */
+	public IFeature[] getUnconfiguredOptionalFeatures(
+		IInstallConfiguration config,
+		IConfiguredSite targetSite) {
+
+		ArrayList unconfiguredOptionalFeatures = new ArrayList();
+		getUnconfiguredOptionalFeatures(unconfiguredOptionalFeatures, config, targetSite, getElements(), UpdateUtils.isPatch(job.getFeature()));
+		IFeature[] unconfiguredOptionalFeaturesArray =
+			new IFeature[unconfiguredOptionalFeatures.size()];
+		unconfiguredOptionalFeatures.toArray(unconfiguredOptionalFeaturesArray);
+		return unconfiguredOptionalFeaturesArray;
+	}
+
+	private void getUnconfiguredOptionalFeatures(
+		ArrayList unconfiguredOptionalFeatures,
+		IInstallConfiguration config,
+		IConfiguredSite targetSite,
+		FeatureHierarchyElement[] optionalElements,
+		boolean isPatch) {
+		for (int i = 0; i < optionalElements.length; i++) {
+			FeatureHierarchyElement[] children =
+				optionalElements[i].getChildren(true, isPatch, config);
+			getUnconfiguredOptionalFeatures(
+				unconfiguredOptionalFeatures,
+				config,
+				targetSite,
+				children,
+				isPatch);
+			if (!optionalElements[i].isEnabled(config)) {
+				unconfiguredOptionalFeatures.add(optionalElements[i].getFeature());
+			}
+		}
+	}
+
+	private void computeElements() {
+		try {
+			IFeature oldFeature = job.getOldFeature();
+			IFeature newFeature = job.getFeature();
+			ArrayList list = new ArrayList();
+			boolean patch = UpdateUtils.isPatch(newFeature);
+			FeatureHierarchyElement.computeElements(
+				oldFeature,
+				newFeature,
+				oldFeature != null,
+				patch,
+				SiteManager.getLocalSite().getCurrentConfiguration(),
+				list);
+			elements = new FeatureHierarchyElement[list.size()];
+			list.toArray(elements);
+			for (int i = 0; i < elements.length; i++) {
+				elements[i].setRoot(this);
+			}
+		} catch (CoreException e) {
+			UpdateUtils.logException(e);
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/Operation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/Operation.java
new file mode 100644
index 0000000..5250bb2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/Operation.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.update.operations.*;
+
+
+public abstract class Operation implements IOperation {
+	
+	private boolean processed;
+	
+	
+	public Operation() {
+	}
+	
+	public boolean isProcessed() {
+		return processed;
+	}
+	
+	public void markProcessed() {
+		processed = true;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/OperationFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/OperationFactory.java
new file mode 100644
index 0000000..636f376
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/OperationFactory.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+public class OperationFactory implements IOperationFactory {
+
+	public OperationFactory() {
+	}
+
+	public IConfigFeatureOperation createConfigOperation(
+		IConfiguredSite targetSite,
+		IFeature feature) {
+		return new ConfigOperation(targetSite, feature);
+	}
+
+	public IBatchOperation createBatchInstallOperation(IInstallFeatureOperation[] operations) {
+		return new BatchInstallOperation(operations);
+	}
+
+	public IInstallFeatureOperation createInstallOperation(
+		IConfiguredSite targetSite,
+		IFeature feature,
+		IFeatureReference[] optionalFeatures,
+		IFeature[] unconfiguredOptionalFeatures,
+		IVerificationListener verifier) {
+		return new InstallOperation(
+			targetSite,
+			feature,
+			optionalFeatures,
+			unconfiguredOptionalFeatures,
+			verifier);
+	}
+
+	public IUnconfigFeatureOperation createUnconfigOperation(
+		IConfiguredSite targetSite,
+		IFeature feature) {
+		return new UnconfigOperation(targetSite, feature);
+	}
+
+	public IConfigFeatureOperation createReplaceFeatureVersionOperation(
+		IFeature feature,
+		IFeature anotherFeature) {
+	
+		return new ReplaceFeatureVersionOperation(feature, anotherFeature);		
+	}
+		
+	public IUninstallFeatureOperation createUninstallOperation(
+		IConfiguredSite targetSite,
+		IFeature feature) {
+		return new UninstallOperation(targetSite, feature);
+	}
+
+	public IRevertConfigurationOperation createRevertConfigurationOperation(
+		IInstallConfiguration config,
+		IProblemHandler problemHandler) {
+		return new RevertConfigurationOperation(
+			config,
+			problemHandler);
+	}
+
+	public IToggleSiteOperation createToggleSiteOperation(
+		IConfiguredSite site) {
+		return new ToggleSiteOperation(site);
+	}
+	
+	public IUnconfigureAndUninstallFeatureOperation createUnconfigureAndUninstallFeatureOperation(
+			IConfiguredSite targetSite,
+			IFeature feature) {
+			return new UnconfigureAndUninstallFeatureOperation(targetSite, feature);
+	}
+	
+	public IUnconfigureFeaturesOperation createUnconfigureFeaturesOperation (
+			IConfiguredSite[] targetSites,
+			IFeature[] features) {
+			return new UnconfigureFeaturesOperation(targetSites, features);
+		}
+
+	public IConfigureFeaturesOperation createConfigureFeaturesOperation(
+			IConfiguredSite[] targetSites,
+			IFeature[] features) {
+		
+			return new ConfigureFeaturesOperation(targetSites, features);		
+	}
+			
+	public IUninstallFeaturesOperation createUninstallFeaturesOperation(
+			IConfiguredSite[] targetSites,
+			IFeature[] features) {
+			return new UninstallFeaturesOperation(targetSites, features);
+	}
+	
+	public IUnconfigureAndUninstallFeaturesOperation createUnconfigureAndUninstallFeaturesOperation(
+			IConfiguredSite[] targetSites,
+			IFeature[] features) {
+			return new UnconfigureAndUninstallFeaturesOperation(targetSites, features);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/OperationValidator.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/OperationValidator.java
new file mode 100644
index 0000000..03af25b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/OperationValidator.java
@@ -0,0 +1,1397 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.operations;
+
+//import java.io.*;
+//import java.net.*;
+//import java.nio.channels.*;
+import org.eclipse.update.core.IUpdateConstants;
+
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.configurator.IPlatformConfiguration;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.configurator.PlatformConfiguration;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.operations.IInstallFeatureOperation;
+import org.eclipse.update.operations.IOperationValidator;
+import org.osgi.framework.*;
+
+/**
+ *  
+ */
+public class OperationValidator implements IOperationValidator {
+	/**
+	 * Checks if the platform configuration has been modified outside this program.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePlatformConfigValid() {
+		ArrayList status = new ArrayList(1);
+		checkPlatformWasModified(status);
+		
+		// report status
+		if (status.size() > 0)
+			return createMultiStatus(Messages.ActivityConstraints_rootMessage, status, IStatus.ERROR);
+		return null;
+	}
+	
+	/*
+	 * Called by UI before performing operation. Returns null if no errors, a
+	 * status with IStatus.WARNING code when the initial configuration is
+	 * broken, or a status with IStatus.ERROR when there the operation
+	 * introduces new errors
+	 */
+	public IStatus validatePendingInstall(
+		IFeature oldFeature,
+		IFeature newFeature) {
+		// check initial state
+		ArrayList beforeStatus = new ArrayList();
+		validateInitialState(beforeStatus);
+
+		// check proposed change
+		ArrayList status = new ArrayList();
+		checkPlatformWasModified(status);
+		validateInstall(oldFeature, newFeature, status);
+
+		// report status
+		return createCombinedReportStatus(beforeStatus, status);
+	}
+
+	/*
+	 * Called by UI before performing operation
+	 */
+	public IStatus validatePendingUnconfig(IFeature feature) {
+		// check initial state
+		ArrayList beforeStatus = new ArrayList();
+		validateInitialState(beforeStatus);
+
+		// check proposed change
+		ArrayList status = new ArrayList();
+		checkPlatformWasModified(status);
+		validateUnconfigure(feature, status);
+
+		// report status
+		return createCombinedReportStatus(beforeStatus, status);
+	}
+
+	/*
+	 * Called by UI before performing operation
+	 */
+	public IStatus validatePendingConfig(IFeature feature) {
+		// check initial state
+		ArrayList beforeStatus = new ArrayList();
+		validateInitialState(beforeStatus);
+
+		// check proposed change
+		ArrayList status = new ArrayList();
+		checkPlatformWasModified(status);
+		validateConfigure(feature, status);
+
+		// report status
+		return createCombinedReportStatus(beforeStatus, status);
+	}
+
+	/**
+	 * Called before performing operation.
+	 */
+	public IStatus validatePendingReplaceVersion(
+		IFeature feature,
+		IFeature anotherFeature) {
+		// check initial state
+		ArrayList beforeStatus = new ArrayList();
+		validateInitialState(beforeStatus);
+
+		// check proposed change
+		ArrayList status = new ArrayList();
+		checkPlatformWasModified(status);
+		validateReplaceVersion(feature, anotherFeature, status);
+
+		// report status
+		return createCombinedReportStatus(beforeStatus, status);
+	}
+
+
+	/*
+	 * Called by the UI before doing a revert/ restore operation
+	 */
+	public IStatus validatePendingRevert(IInstallConfiguration config) {
+		// check initial state
+		ArrayList beforeStatus = new ArrayList();
+		validateInitialState(beforeStatus);
+
+		// check proposed change
+		ArrayList status = new ArrayList();
+		checkPlatformWasModified(status);
+		validateRevert(config, status);
+
+		// report status
+		return createCombinedReportStatus(beforeStatus, status);
+	}
+
+	/*
+	 * Called by the UI before doing a batched processing of several pending
+	 * changes.
+	 */
+	public IStatus validatePendingChanges(IInstallFeatureOperation[] jobs) {
+		// check initial state
+		ArrayList beforeStatus = new ArrayList();
+		validateInitialState(beforeStatus);
+		checkPlatformWasModified(beforeStatus);
+
+		// check proposed change
+		ArrayList status = new ArrayList();
+		validatePendingChanges(jobs, status, beforeStatus);
+
+		// report status
+		return createCombinedReportStatus(beforeStatus, status);
+	}
+	
+	/*
+	 * Called by the UI before doing a batched processing of several pending
+	 * changes.
+	 */
+	public RequiredFeaturesResult getRequiredFeatures(IInstallFeatureOperation[] jobs) {
+		
+		RequiredFeaturesResult requiredFeaturesResult = new RequiredFeaturesResult();
+		// check initial state
+		ArrayList beforeStatus = new ArrayList();
+		validateInitialState(beforeStatus);
+		checkPlatformWasModified(beforeStatus);
+
+		// check proposed change
+		ArrayList status = new ArrayList();
+		Set requiredFeatures = validatePendingChanges(jobs, status, beforeStatus);
+
+		// report status
+		//return createCombinedReportStatus(beforeStatus, status);
+		requiredFeaturesResult.setRequiredFeatures(requiredFeatures);
+		requiredFeaturesResult.setStatus(createCombinedReportStatus(beforeStatus, status));
+		return requiredFeaturesResult;
+	}
+
+	/*
+	 * Check the current state.
+	 */
+	public IStatus validateCurrentState() {
+		// check the state
+		ArrayList status = new ArrayList();
+		checkPlatformWasModified(status);
+		validateInitialState(status);
+
+		// report status
+		if (status.size() > 0)
+			return createMultiStatus(Messages.ActivityConstraints_rootMessage, status, IStatus.ERROR);
+		return null;
+	}
+
+	/*
+	 * Check to see if we are not broken even before we start
+	 */
+	private static void validateInitialState(ArrayList status) {
+		try {
+			ArrayList features = computeFeatures();
+			// uncomment this when patch released in boot
+			//checkConfigurationLock(status);
+			checkConstraints(features, status);
+		} catch (CoreException e) {
+			status.add(e.getStatus());
+		}
+	}
+
+	/*
+	 * handle unconfigure
+	 */
+	private static void validateUnconfigure(
+		IFeature feature,
+		ArrayList status) {
+		try {
+			checkSiteReadOnly(feature,status);
+			ArrayList features = computeFeatures();
+			features = computeFeaturesAfterOperation(features, null, feature);
+			checkConstraints(features, status);
+		} catch (CoreException e) {
+			status.add(e.getStatus());
+		}
+	}
+
+
+	/*
+	 * handle configure
+	 */
+	private static void validateConfigure(IFeature feature, ArrayList status) {
+		try {
+			checkSiteReadOnly(feature,status);
+			ArrayList features = computeFeatures();
+			checkOptionalChildConfiguring(feature, status);
+			checkForCycles(feature, null, features);
+			features = computeFeaturesAfterOperation(features, feature, null);
+			checkConstraints(features, status);
+
+		} catch (CoreException e) {
+			status.add(e.getStatus());
+		}
+	}
+
+	/*
+	 * handle replace version
+	 */
+	private static void validateReplaceVersion(
+		IFeature feature,
+		IFeature anotherFeature,
+		ArrayList status) {
+		try {
+			checkSiteReadOnly(feature,status);
+			ArrayList features = computeFeatures();
+			checkForCycles(feature, null, features);
+			features =
+				computeFeaturesAfterOperation(
+					features,
+					anotherFeature,
+					feature);
+			checkConstraints(features, status);
+		} catch (CoreException e) {
+			status.add(e.getStatus());
+		}
+	}
+
+	/*
+	 * handle install and update
+	 */
+	private static void validateInstall(
+		IFeature oldFeature,
+		IFeature newFeature,
+		ArrayList status) {
+		try {
+			checkSiteReadOnly(oldFeature,status);
+			ArrayList features = computeFeatures();
+			checkForCycles(newFeature, null, features);
+			features =
+				computeFeaturesAfterOperation(features, newFeature, oldFeature);
+			checkConstraints(features, status);
+			checkLicense(newFeature, status);
+		} catch (CoreException e) {
+			status.add(e.getStatus());
+		}
+	}
+
+	/*
+	 * handle revert and restore
+	 */
+	private static void validateRevert(
+		IInstallConfiguration config,
+		ArrayList status) {
+		try {
+//			// check the timeline and don't bother
+//			// to check anything else if negative
+//			if (!checkTimeline(config, status))
+//				return;
+			ArrayList features = computeFeaturesAfterRevert(config);
+			checkConstraints(features, status);
+			checkRevertConstraints(features, status);
+
+		} catch (CoreException e) {
+			status.add(e.getStatus());
+		}
+	}
+
+
+	/*
+	 * Handle one-click changes as a batch
+	 */
+	private static Set validatePendingChanges(
+		IInstallFeatureOperation[] jobs,
+		ArrayList status,
+		ArrayList beforeStatus) {
+		try {
+			ArrayList features = computeFeatures();
+			ArrayList savedFeatures = features;
+			int nexclusives = 0;
+
+			// pass 1: see if we can process the entire "batch"
+			ArrayList tmpStatus = new ArrayList();
+			for (int i = 0; i < jobs.length; i++) {
+				IInstallFeatureOperation job = jobs[i];
+
+				IFeature newFeature = job.getFeature();
+				IFeature oldFeature = job.getOldFeature();
+				checkLicense(newFeature, status);
+				if (jobs.length > 1 && newFeature.isExclusive()) {
+					nexclusives++;
+					status.add(
+						createStatus(
+							newFeature,
+							FeatureStatus.CODE_EXCLUSIVE,
+							Messages.ActivityConstraints_exclusive));
+					continue;
+				}
+				checkForCycles(newFeature, null, features);
+				features =
+					computeFeaturesAfterOperation(
+						features,
+						newFeature,
+						oldFeature);
+			}
+			if (nexclusives > 0)
+				return Collections.EMPTY_SET;
+			checkConstraints(features, tmpStatus);
+			if (tmpStatus.size() == 0) // the whole "batch" is OK
+				return Collections.EMPTY_SET;
+
+			// pass 2: we have conflicts
+			features = savedFeatures;
+			for (int i = 0; i < jobs.length; i++) {
+				IInstallFeatureOperation job = jobs[i];
+				IFeature newFeature = job.getFeature();
+				IFeature oldFeature = job.getOldFeature();
+
+				features =
+					computeFeaturesAfterOperation(
+						features,
+						newFeature,
+						oldFeature);
+
+				Set result = checkConstraints(features, status);
+				if (status.size() > 0
+					&& !isBetterStatus(beforeStatus, status)) {
+// bug 75613
+//					IStatus conflict =
+//						createStatus(
+//							newFeature,
+//							FeatureStatus.CODE_OTHER,
+//							Policy.bind(KEY_CONFLICT));
+//					status.add(0, conflict);
+					return result;
+				}
+			}
+		} catch (CoreException e) {
+			status.add(e.getStatus());
+		}
+		
+		return Collections.EMPTY_SET;
+	}
+	
+	private static void checkPlatformWasModified(ArrayList status) {
+		try {
+			// checks if the platform has been modified outside this eclipse instance
+			IPlatformConfiguration platformConfig = ConfiguratorUtils.getCurrentPlatformConfiguration();
+			
+			long currentTimeStamp = platformConfig.getChangeStamp();
+			// get the last modified value for this config, from this process point of view
+			if (platformConfig instanceof PlatformConfiguration) 
+				currentTimeStamp = ((PlatformConfiguration)platformConfig).getConfiguration().lastModified();
+				
+			// get the real last modified value
+			URL platformXML = platformConfig.getConfigurationLocation();
+			long actualTimeStamp = currentTimeStamp;
+			if ("file".equals(platformXML.getProtocol())) //$NON-NLS-1$
+				actualTimeStamp = new File(platformXML.getFile()).lastModified();
+			else {
+				URLConnection connection = platformXML.openConnection();
+				actualTimeStamp = connection.getLastModified();
+			}
+			if (currentTimeStamp != actualTimeStamp)
+				status.add(createStatus(
+								null,
+								FeatureStatus.CODE_OTHER,
+								Messages.ActivityConstraints_platformModified)); 
+		} catch (IOException e) {
+			// ignore
+		}
+	}
+	
+	private static void checkSiteReadOnly(IFeature feature, ArrayList status) {
+		if(feature == null){
+			return;
+		}
+		IConfiguredSite csite = feature.getSite().getCurrentConfiguredSite();
+		if (csite != null && !csite.isUpdatable())
+			status.add(createStatus(feature, FeatureStatus.CODE_OTHER,
+					NLS.bind(Messages.ActivityConstraints_readOnly, (new String[] { csite.getSite().getURL().toExternalForm() }))));
+	}
+
+	/*
+	 * Compute a list of configured features
+	 */
+	private static ArrayList computeFeatures() throws CoreException {
+		return computeFeatures(true);
+	}
+	/*
+	 * Compute a list of configured features
+	 */
+	private static ArrayList computeFeatures(boolean configuredOnly)
+		throws CoreException {
+		ArrayList features = new ArrayList();
+		ILocalSite localSite = SiteManager.getLocalSite();
+		IInstallConfiguration config = localSite.getCurrentConfiguration();
+		IConfiguredSite[] csites = config.getConfiguredSites();
+
+		for (int i = 0; i < csites.length; i++) {
+			IConfiguredSite csite = csites[i];
+
+			IFeatureReference[] crefs;
+
+			if (configuredOnly)
+				crefs = csite.getConfiguredFeatures();
+			else
+				crefs = csite.getSite().getFeatureReferences();
+			for (int j = 0; j < crefs.length; j++) {
+				IFeatureReference cref = crefs[j];
+				IFeature cfeature = cref.getFeature(null);
+				features.add(cfeature);
+			}
+		}
+
+		return features;
+	}
+
+	/*
+	 * Compute the nested feature subtree starting at the specified base
+	 * feature
+	 */
+	public static ArrayList computeFeatureSubtree(
+			IFeature top,
+			IFeature feature,
+			ArrayList features,
+			boolean tolerateMissingChildren,
+			ArrayList configuredFeatures,
+			ArrayList visitedFeatures)
+	throws CoreException {
+
+		// check arguments
+		if (top == null)
+			return features;
+		if (feature == null)
+			feature = top;
+		if (features == null)
+			features = new ArrayList();
+		if (visitedFeatures == null)
+			visitedFeatures = new ArrayList();
+
+		// check for <includes> cycle
+		if (visitedFeatures.contains(feature)) {
+			IStatus status =
+			createStatus(top, FeatureStatus.CODE_CYCLE, Messages.ActivityConstraints_cycle);
+			throw new CoreException(status);
+		} else {
+			// keep track of visited features so we can detect cycles
+			visitedFeatures.add(feature);
+		}
+
+		// return specified base feature and all its children
+		if (!features.contains(feature))
+			features.add(feature);
+		IIncludedFeatureReference[] children =
+		feature.getIncludedFeatureReferences();
+		for (int i = 0; i < children.length; i++) {
+			try {
+				IFeature child = UpdateUtils.getIncludedFeature(feature, children[i]);
+				features =
+				computeFeatureSubtree(
+						top,
+						child,
+						features,
+						tolerateMissingChildren,
+						null,
+						visitedFeatures);
+			} catch (CoreException e) {
+				if (!children[i].isOptional() && !tolerateMissingChildren)
+					throw e;
+			}
+		}
+		// no cycles for this feature during DFS
+		visitedFeatures.remove(feature);
+		return features;
+	}
+
+	private static void checkLicense(IFeature feature, ArrayList status) {
+		IURLEntry licenseEntry = feature.getLicense();
+		if (licenseEntry != null) {
+			String license = licenseEntry.getAnnotation();
+			if (license != null && license.trim().length() > 0)
+				return;
+		}
+		status.add(
+			createStatus(feature, FeatureStatus.CODE_OTHER, Messages.ActivityConstraints_noLicense));
+	}
+
+	/*
+	 * Compute a list of features that will be configured after the operation
+	 */
+	private static ArrayList computeFeaturesAfterOperation(
+		ArrayList features,
+		IFeature add,
+		IFeature remove)
+		throws CoreException {
+
+		ArrayList addTree = computeFeatureSubtree(add, null, null, false,
+		/* do not tolerate missing children */
+		features, null);
+		ArrayList removeTree =
+			computeFeatureSubtree(
+				remove,
+				null,
+				null,
+				true /* tolerate missing children */,
+				null,
+				null
+		);
+		if (remove != null) {
+			// Patches to features are removed together with
+			// those features. Include them in the list.
+			contributePatchesFor(removeTree, features, removeTree);
+		}
+
+		if (remove != null)
+			features.removeAll(removeTree);
+
+		if (add != null)
+			features.addAll(addTree);
+
+		return features;
+	}
+
+	private static void contributePatchesFor(
+		ArrayList removeTree,
+		ArrayList features,
+		ArrayList result)
+		throws CoreException {
+
+		for (int i = 0; i < removeTree.size(); i++) {
+			IFeature feature = (IFeature) removeTree.get(i);
+			contributePatchesFor(feature, features, result);
+		}
+	}
+
+	private static void contributePatchesFor(
+		IFeature feature,
+		ArrayList features,
+		ArrayList result)
+		throws CoreException {
+		for (int i = 0; i < features.size(); i++) {
+			IFeature candidate = (IFeature) features.get(i);
+			if (UpdateUtils.isPatch(feature, candidate)) {
+				ArrayList removeTree =
+					computeFeatureSubtree(candidate, null, null, true,null,null);
+				result.addAll(removeTree);
+			}
+		}
+	}
+
+	/*
+	 * Compute a list of features that will be configured after performing the
+	 * revert
+	 */
+	private static ArrayList computeFeaturesAfterRevert(IInstallConfiguration config)
+		throws CoreException {
+
+		ArrayList list = new ArrayList();
+		IConfiguredSite[] csites = config.getConfiguredSites();
+		for (int i = 0; i < csites.length; i++) {
+			IConfiguredSite csite = csites[i];
+			IFeatureReference[] features = csite.getConfiguredFeatures();
+			for (int j = 0; j < features.length; j++) {
+				list.add(features[j].getFeature(null));
+			}
+		}
+		return list;
+	}
+
+
+
+	/*
+	 * Compute a list of plugin entries for the specified features.
+	 */
+	private static ArrayList computePluginsForFeatures(ArrayList features)
+		throws CoreException {
+		if (features == null)
+			return new ArrayList();
+
+		HashMap plugins = new HashMap();
+		for (int i = 0; i < features.size(); i++) {
+			IFeature feature = (IFeature) features.get(i);
+			IPluginEntry[] entries = feature.getPluginEntries();
+			for (int j = 0; j < entries.length; j++) {
+				IPluginEntry entry = entries[j];
+				plugins.put(entry.getVersionedIdentifier(), entry);
+			}
+		}
+		ArrayList result = new ArrayList();
+		result.addAll(plugins.values());
+		return result;
+	}
+
+
+	/**
+	 * Check for feature cycles:
+	 * - visit feature
+	 * - if feature is in the cycle candidates list, then cycle found, else add it to candidates list
+	 * - DFS children 
+	 * - when return from DFS remove the feature from the candidates list
+	 */
+	private static void checkForCycles(
+			IFeature feature,
+			ArrayList candidates,
+			ArrayList configuredFeatures)
+	throws CoreException {
+
+		// check arguments
+		if (feature == null)
+			return;
+		if (configuredFeatures == null)
+			configuredFeatures = new ArrayList();
+		if (candidates == null)
+			candidates = new ArrayList();
+		
+		// check for <includes> cycle
+		if (candidates.contains(feature)) {
+			String msg = NLS.bind(Messages.ActivityConstraints_cycle, (new String[] {feature.getLabel(), 
+            feature.getVersionedIdentifier().toString()}));
+			IStatus status = createStatus(feature, FeatureStatus.CODE_CYCLE, msg);
+			throw new CoreException(status);
+		}
+
+		// potential candidate
+		candidates.add(feature);
+		
+		// recursively, check cycles with children
+		IIncludedFeatureReference[] children =
+		feature.getIncludedFeatureReferences();
+		for (int i = 0; i < children.length; i++) {
+			try {
+				IFeature child = UpdateUtils.getIncludedFeature(feature, children[i]);
+				checkForCycles(child, candidates, configuredFeatures);
+			} catch (CoreException e) {
+				if (!children[i].isOptional())
+					throw e;
+			}
+		}
+		// no longer a candidate, because no cycles with children
+		candidates.remove(feature);
+	}
+	
+	/*
+	 * validate constraints
+	 */
+	private static Set  checkConstraints(ArrayList features, ArrayList status)
+		throws CoreException {
+		if (features == null)
+			return Collections.EMPTY_SET;
+
+		ArrayList plugins = computePluginsForFeatures(features);
+
+		checkEnvironment(features, status);
+		checkPlatformFeature(features, plugins, status);
+		checkPrimaryFeature(features, plugins, status);
+		return checkPrereqs(features, plugins, status);
+	}
+
+	/*
+	 * Verify all features are either portable, or match the current
+	 * environment
+	 */
+	private static void checkEnvironment(
+		ArrayList features,
+		ArrayList status) {
+
+		String os = Platform.getOS();
+		String ws = Platform.getWS();
+		String arch = Platform.getOSArch();
+
+		for (int i = 0; i < features.size(); i++) {
+			IFeature feature = (IFeature) features.get(i);
+			ArrayList fos = createList(feature.getOS());
+			ArrayList fws = createList(feature.getWS());
+			ArrayList farch = createList(feature.getOSArch());
+
+			if (fos.size() > 0) {
+				if (!fos.contains(os)) {
+					IStatus s =
+						createStatus(feature, FeatureStatus.CODE_ENVIRONMENT, Messages.ActivityConstraints_os);
+					if (!status.contains(s))
+						status.add(s);
+					continue;
+				}
+			}
+
+			if (fws.size() > 0) {
+				if (!fws.contains(ws)) {
+					IStatus s =
+						createStatus(feature, FeatureStatus.CODE_ENVIRONMENT, Messages.ActivityConstraints_ws);
+					if (!status.contains(s))
+						status.add(s);
+					continue;
+				}
+			}
+
+			if (farch.size() > 0) {
+				if (!farch.contains(arch)) {
+					IStatus s =
+						createStatus(feature, FeatureStatus.CODE_ENVIRONMENT, Messages.ActivityConstraints_arch);
+					if (!status.contains(s))
+						status.add(s);
+					continue;
+				}
+			}
+		}
+	}
+
+	/*
+	 * Verify we end up with a version of platform configured
+	 */
+	private static void checkPlatformFeature(
+		ArrayList features,
+		ArrayList plugins,
+		ArrayList status) {
+
+		// find the plugin that defines the product
+		IProduct product = Platform.getProduct();
+		if (product == null)
+			return; // normally this shouldn't happen
+		Bundle primaryBundle = product.getDefiningBundle();
+		// check if that plugin is among the resulting plugins
+		boolean found = false;
+		for (int j = 0; j < plugins.size(); j++) {
+			IPluginEntry plugin = (IPluginEntry) plugins.get(j);
+			if (primaryBundle.getSymbolicName().equals(plugin.getVersionedIdentifier().getIdentifier())) {
+				found = true;
+				break;
+			}
+		}
+		
+		if (!found) {
+			IStatus s =
+				createStatus(null, FeatureStatus.CODE_OTHER, Messages.ActivityConstraints_platform);
+			if (!status.contains(s))
+				status.add(s);
+		}
+	}
+
+	/*
+	 * Verify we end up with a version of primary feature configured
+	 */
+	private static void checkPrimaryFeature(
+		ArrayList features,
+		ArrayList plugins,
+		ArrayList status) {
+
+		String featureId =
+			ConfiguratorUtils
+				.getCurrentPlatformConfiguration()
+				.getPrimaryFeatureIdentifier();
+		
+		if (featureId != null) {
+			// primary feature is defined
+			for (int i = 0; i < features.size(); i++) {
+				IFeature feature = (IFeature) features.get(i);
+				if (featureId
+					.equals(feature.getVersionedIdentifier().getIdentifier()))
+					return;
+			}
+	
+			IStatus s = createStatus(null, FeatureStatus.CODE_OTHER, Messages.ActivityConstraints_primary);
+			if (!status.contains(s))
+				status.add(s);
+		} else {
+			// check if the product still ends up contributed
+			// find the plugin that defines the product
+			IProduct product = Platform.getProduct();
+			if (product == null)
+				return; // normally this shouldn't happen
+			Bundle primaryBundle = product.getDefiningBundle();
+			// check if that plugin is among the resulting plugins
+
+			for (int j = 0; j < plugins.size(); j++) {
+				IPluginEntry plugin = (IPluginEntry) plugins.get(j);
+				if (primaryBundle.getSymbolicName().equals(plugin.getVersionedIdentifier().getIdentifier())) {
+					return; // product found
+				}
+			}
+			IStatus s =
+				createStatus(null, FeatureStatus.CODE_OTHER, Messages.ActivityConstraints_primary);
+			if (!status.contains(s))
+				status.add(s);
+		}
+	}
+
+	/*
+	 * Verify we do not break prereqs
+	 */
+	private static Set checkPrereqs(
+		ArrayList features,
+		ArrayList plugins,
+		ArrayList status) {
+		
+		HashSet result = new HashSet();
+
+		for (int i = 0; i < features.size(); i++) {
+			IFeature feature = (IFeature) features.get(i);
+			IImport[] imports = feature.getImports();
+
+			for (int j = 0; j < imports.length; j++) {
+				IImport iimport = imports[j];
+				// for each import determine plugin or feature, version, match
+				// we need
+				VersionedIdentifier iid = iimport.getVersionedIdentifier();
+				String id = iid.getIdentifier();
+				PluginVersionIdentifier version = iid.getVersion();
+				boolean featurePrereq =
+					iimport.getKind() == IImport.KIND_FEATURE;
+				boolean ignoreVersion =
+					version.getMajorComponent() == 0
+						&& version.getMinorComponent() == 0
+						&& version.getServiceComponent() == 0;
+				int rule = iimport.getRule();
+				if (rule == IUpdateConstants.RULE_NONE)
+					rule = IUpdateConstants.RULE_COMPATIBLE;
+
+				boolean found = false;
+
+				ArrayList candidates;
+
+				if (featurePrereq)
+					candidates = features;
+				else
+					candidates = plugins;
+				for (int k = 0; k < candidates.size(); k++) {
+					VersionedIdentifier cid;
+					if (featurePrereq) {
+						// the candidate is a feature
+						IFeature candidate = (IFeature) candidates.get(k);
+						// skip self
+						if (feature.equals(candidate))
+							continue;
+						cid = candidate.getVersionedIdentifier();
+					} else {
+						// the candidate is a plug-in
+						IPluginEntry plugin = (IPluginEntry) candidates.get(k);
+						cid = plugin.getVersionedIdentifier();
+					}
+					PluginVersionIdentifier cversion = cid.getVersion();
+					if (id.equals(cid.getIdentifier())) {
+						// have a candidate
+						if (ignoreVersion)
+							found = true;
+						else if (
+							rule == IUpdateConstants.RULE_PERFECT
+								&& cversion.isPerfect(version))
+							found = true;
+						else if (
+							rule == IUpdateConstants.RULE_EQUIVALENT
+								&& cversion.isEquivalentTo(version))
+							found = true;
+						else if (
+							rule == IUpdateConstants.RULE_COMPATIBLE
+								&& cversion.isCompatibleWith(version))
+							found = true;
+						else if (
+							rule == IUpdateConstants.RULE_GREATER_OR_EQUAL
+								&& cversion.isGreaterOrEqualTo(version))
+							found = true;
+					}
+					if (found)
+						break;
+				}
+				
+				// perhaps the bundle that we are looking for was installed
+				// but isn't a part of a feature
+				if (!found && !featurePrereq)
+					found = isInstalled(iid, rule, ignoreVersion);
+
+				if (!found) {
+					// report status
+					String target =
+						featurePrereq
+							? Messages.ActivityConstaints_prereq_feature
+							: Messages.ActivityConstaints_prereq_plugin;
+					int errorCode = featurePrereq
+							? FeatureStatus.CODE_PREREQ_FEATURE
+							: FeatureStatus.CODE_PREREQ_PLUGIN;
+					String msg =
+						NLS.bind(Messages.ActivityConstraints_prereq, (new String[] { target, id }));
+
+					if (!ignoreVersion) {
+						if (rule == IUpdateConstants.RULE_PERFECT)
+							msg =
+								NLS.bind(Messages.ActivityConstraints_prereqPerfect, (new String[] {
+                                target,
+                                id,
+                                version.toString()}));
+						else if (rule == IUpdateConstants.RULE_EQUIVALENT)
+							msg =
+								NLS.bind(Messages.ActivityConstraints_prereqEquivalent, (new String[] {
+                                target,
+                                id,
+                                version.toString()}));
+						else if (rule == IUpdateConstants.RULE_COMPATIBLE)
+							msg =
+								NLS.bind(Messages.ActivityConstraints_prereqCompatible, (new String[] {
+                                target,
+                                id,
+                                version.toString()}));
+						else if (rule == IUpdateConstants.RULE_GREATER_OR_EQUAL)
+							msg =
+								NLS.bind(Messages.ActivityConstraints_prereqGreaterOrEqual, (new String[] {
+                                target,
+                                id,
+                                version.toString()}));
+					}
+					IStatus s = createStatus(feature, errorCode, msg);
+					result.add(new InternalImport(iimport));
+					if (!status.contains(s))
+						status.add(s);
+				}
+			}
+		}
+		
+		return result;
+	}
+	
+	/*
+	 * Return a boolean value indicating whether or not the bundle with the given id and version
+	 * is installed in the system.
+	 */
+	private static boolean isInstalled(VersionedIdentifier vid, int rule, boolean ignoreVersion) {
+		BundleContext context = UpdateCore.getPlugin().getBundleContext();
+		if (context == null)
+			return false;
+		ServiceReference reference = context.getServiceReference(PlatformAdmin.class.getName());
+		if (reference == null)
+			return false;
+		PlatformAdmin admin = (PlatformAdmin) context.getService(reference);
+		try {
+			State state = admin.getState(false);
+			String id = vid.getIdentifier();
+			PluginVersionIdentifier version = vid.getVersion();
+			BundleDescription[] bundles = state.getBundles(id);
+			if (bundles == null || bundles.length == 0)
+				return false;
+			for (int i=0; i<bundles.length; i++) {
+				BundleDescription bundle = bundles[i];
+				PluginVersionIdentifier cversion = new PluginVersionIdentifier(bundle.getVersion().toString());
+				// have a candidate
+				if (ignoreVersion)
+					return true;
+				if (rule == IUpdateConstants.RULE_PERFECT && cversion.isPerfect(version))
+					return true;
+				else if (rule == IUpdateConstants.RULE_EQUIVALENT && cversion.isEquivalentTo(version))
+					return true;
+				else if (rule == IUpdateConstants.RULE_COMPATIBLE && cversion.isCompatibleWith(version))
+					return true;
+				else if (rule == IUpdateConstants.RULE_GREATER_OR_EQUAL && cversion.isGreaterOrEqualTo(version))
+					return true;
+			}
+			return false;
+		} finally {
+			context.ungetService(reference);
+		}
+	}
+
+	/*
+	 * Verify we end up with valid nested features after revert
+	 */
+	private static void checkRevertConstraints(
+		ArrayList features,
+		ArrayList status) {
+
+		for (int i = 0; i < features.size(); i++) {
+			IFeature feature = (IFeature) features.get(i);
+			try {
+				computeFeatureSubtree(
+					feature,
+					null,
+					null,
+					false /* do not tolerate missing children */,
+					null,
+					null
+				);
+			} catch (CoreException e) {
+				status.add(e.getStatus());
+			}
+		}
+	}
+
+	/*
+	 * Verify that a parent of an optional child is configured before we allow
+	 * the child to be configured as well
+	 */
+
+	private static void checkOptionalChildConfiguring(
+		IFeature feature,
+		ArrayList status)
+		throws CoreException {
+		ILocalSite localSite = SiteManager.getLocalSite();
+		IInstallConfiguration config = localSite.getCurrentConfiguration();
+		IConfiguredSite[] csites = config.getConfiguredSites();
+
+		boolean included = false;
+		for (int i = 0; i < csites.length; i++) {
+			IConfiguredSite csite = csites[i];
+			ISiteFeatureReference[] crefs =
+				csite.getSite().getFeatureReferences();
+			for (int j = 0; j < crefs.length; j++) {
+				IFeatureReference cref = crefs[j];
+				IFeature cfeature = null;
+				try {
+					cfeature = cref.getFeature(null);
+				} catch (CoreException e) {
+					//FIXME: cannot ask 'isOptional' here
+					// Ignore missing optional feature.
+					/*
+					 * if (cref.isOptional()) continue;
+					 */
+					throw e;
+				}
+				if (isParent(cfeature, feature, true)) {
+					// Included in at least one feature as optional
+					included = true;
+					if (csite.isConfigured(cfeature)) {
+						// At least one feature parent
+						// is enabled - it is OK to
+						// configure optional child.
+						return;
+					}
+				}
+			}
+		}
+		if (included) {
+			// feature is included as optional but
+			// no parent is currently configured.
+			String msg = Messages.ActivityConstraints_optionalChild;
+			status.add(createStatus(feature, FeatureStatus.CODE_OPTIONAL_CHILD, msg));
+		} else {
+			//feature is root - can be configured
+		}
+	}
+//
+//	/**
+//	 * Checks if the configuration is locked by other instances
+//	 * 
+//	 * @param status
+//	 */
+//	private static void checkConfigurationLock(ArrayList status) {
+//		IPlatformConfiguration config =
+//			BootLoader.getCurrentPlatformConfiguration();
+//		URL configURL = config.getConfigurationLocation();
+//		if (!"file".equals(configURL.getProtocol())) {
+//			status.add(
+//				createStatus(
+//					null,
+//					"Configuration location is not writable:" + configURL));
+//			return;
+//		}
+//		String locationString = configURL.getFile();
+//		File configDir = new File(locationString);
+//		if (!configDir.isDirectory())
+//			configDir = configDir.getParentFile();
+//		if (!configDir.exists()) {
+//			status.add(
+//				createStatus(null, "Configuration location does not exist"));
+//			return;
+//		}
+//		File locksDir = new File(configDir, "locks");
+//		// check all the possible lock files
+//		File[] lockFiles = locksDir.listFiles();
+//		File configLock = BootLoader.getCurrentPlatformConfiguration().getLockFile();
+//		for (int i = 0; i < lockFiles.length; i++) {
+//			if (lockFiles[i].equals(configLock))
+//				continue;
+//			try {
+//				RandomAccessFile raf = new RandomAccessFile(lockFiles[i], "rw");
+//				FileChannel channel = raf.getChannel();
+//				System.out.println(channel.isOpen());
+//				FileLock lock = channel.tryLock();
+//				if (lock == null){
+//					// there is another eclipse instance running
+//					raf.close();
+//					status.add(
+//						createStatus(
+//							null,
+//							"Another instance is running, please close it before performing any configuration operations"));
+//					return;
+//				}
+//
+//			} catch (Exception e) {
+//				status.add(createStatus(null, "Failed to create lock:"+lockFiles[i]));
+//				return;
+//			} 
+//		}
+//	}
+
+	private static boolean isParent(
+		IFeature candidate,
+		IFeature feature,
+		boolean optionalOnly)
+		throws CoreException {
+		IIncludedFeatureReference[] refs =
+			candidate.getIncludedFeatureReferences();
+		for (int i = 0; i < refs.length; i++) {
+			IIncludedFeatureReference child = refs[i];
+			VersionedIdentifier fvid = feature.getVersionedIdentifier();
+			VersionedIdentifier cvid = child.getVersionedIdentifier();
+
+			if (fvid.getIdentifier().equals(cvid.getIdentifier()) == false)
+				continue;
+			// same ID
+			PluginVersionIdentifier fversion = fvid.getVersion();
+			PluginVersionIdentifier cversion = cvid.getVersion();
+
+			if (fversion.equals(cversion)) {
+				// included and matched; return true if optionality is not
+				// important or it is and the inclusion is optional
+				return optionalOnly == false || child.isOptional();
+			}
+		}
+		return false;
+	}
+
+//	private static boolean checkTimeline(
+//		IInstallConfiguration config,
+//		ArrayList status) {
+//		try {
+//			ILocalSite lsite = SiteManager.getLocalSite();
+//			IInstallConfiguration cconfig = lsite.getCurrentConfiguration();
+//			if (cconfig.getTimeline() != config.getTimeline()) {
+//				// Not the same timeline - cannot revert
+//				String msg =
+//					UpdateUtils.getFormattedMessage(
+//						KEY_WRONG_TIMELINE,
+//						config.getLabel());
+//				status.add(createStatus(null, FeatureStatus.CODE_OTHER, msg));
+//				return false;
+//			}
+//		} catch (CoreException e) {
+//			status.add(e.getStatus());
+//		}
+//		return true;
+//	}
+
+	private static IStatus createMultiStatus(
+		String message,
+		ArrayList children,
+		int code) {
+		IStatus[] carray =
+			(IStatus[]) children.toArray(new IStatus[children.size()]);
+		return new MultiStatus(
+			UpdateCore.getPlugin().getBundle().getSymbolicName(),
+			code,
+			carray,
+			message,
+			null);
+	}
+
+	private static IStatus createStatus(IFeature feature, int errorCode, String message) {
+
+		String fullMessage;
+		if (feature == null)
+			fullMessage = message;
+		else {
+			PluginVersionIdentifier version =
+				feature.getVersionedIdentifier().getVersion();
+			fullMessage =
+				NLS.bind(Messages.ActivityConstraints_childMessage, (new String[] {
+                feature.getLabel(),
+                version.toString(),
+                message }));
+		}
+
+		return new FeatureStatus(
+			feature,
+			IStatus.ERROR,
+			UpdateCore.getPlugin().getBundle().getSymbolicName(),
+			errorCode,
+			fullMessage,
+			null);
+	}
+
+	//	private static IStatus createReportStatus(ArrayList beforeStatus,
+	// ArrayList status) {
+	//		// report status
+	//		if (status.size() > 0) {
+	//			if (beforeStatus.size() > 0)
+	//				return createMultiStatus(KEY_ROOT_MESSAGE_INIT,
+	// beforeStatus,IStatus.ERROR);
+	//			else
+	//				return createMultiStatus(KEY_ROOT_MESSAGE, status,IStatus.ERROR);
+	//		}
+	//		return null;
+	//	}
+
+	private static IStatus createCombinedReportStatus(
+		ArrayList beforeStatus,
+		ArrayList status) {
+		if (beforeStatus.size() == 0) { // good initial config
+			if (status.size() == 0) {
+				return null; // all fine
+			} else {
+				return createMultiStatus(Messages.ActivityConstraints_rootMessage,
+					status,
+					IStatus.ERROR);
+				// error after operation
+			}
+		} else { // beforeStatus.size() > 0 : initial config errors
+			if (status.size() == 0) {
+				return null; // errors will be fixed
+			} else {
+				if (isBetterStatus(beforeStatus, status)) {
+					return createMultiStatus(
+						Messages.ActivityConstraints_warning,
+						beforeStatus,
+						IStatus.WARNING);
+					// errors may be fixed
+				} else {
+					ArrayList combined = new ArrayList();
+					combined.add(
+						createMultiStatus(
+							Messages.ActivityConstraints_beforeMessage,
+							beforeStatus,
+							IStatus.ERROR));
+					combined.add(
+						createMultiStatus(
+							Messages.ActivityConstraints_afterMessage,
+							status,
+							IStatus.ERROR));
+					return createMultiStatus(
+						Messages.ActivityConstraints_rootMessageInitial,
+						combined,
+						IStatus.ERROR);
+				}
+			}
+		}
+	}
+
+	private static ArrayList createList(String commaSeparatedList) {
+		ArrayList list = new ArrayList();
+		if (commaSeparatedList != null) {
+			StringTokenizer t =
+				new StringTokenizer(commaSeparatedList.trim(), ","); //$NON-NLS-1$
+			while (t.hasMoreTokens()) {
+				String token = t.nextToken().trim();
+				if (!token.equals("")) //$NON-NLS-1$
+					list.add(token);
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * Returns true if status is a subset of beforeStatus
+	 * 
+	 * @param beforeStatus
+	 * @param status
+	 * @return
+	 */
+	private static boolean isBetterStatus(
+		ArrayList beforeStatus,
+		ArrayList status) {
+		// if no status at all, then it's a subset
+		if (status == null || status.size() == 0)
+			return true;
+		// there is some status, so if there is no initial status, then it's
+		// not a subset
+		if (beforeStatus == null || beforeStatus.size() == 0)
+			return false;
+		// quick check
+		if (beforeStatus.size() < status.size())
+			return false;
+
+		// check if all the status elements appear in the original status
+		for (int i = 0; i < status.size(); i++) {
+			IStatus s = (IStatus) status.get(i);
+			// if this is not a feature status, something is wrong, so return
+			// false
+			if (!(s instanceof FeatureStatus))
+				return false;
+			FeatureStatus fs = (FeatureStatus) s;
+			// check against all status elements
+			boolean found = false;
+			for (int j = 0; !found && j < beforeStatus.size(); j++) {
+				if (fs.equals(beforeStatus.get(j)))
+					found = true;
+			}
+			if (!found)
+				return false;
+		}
+		return true;
+	}
+	
+	public class RequiredFeaturesResult {
+		
+		private IStatus status;
+		private Set requiredFeatures;
+		
+		public Set getRequiredFeatures() {
+			return requiredFeatures;
+		}
+		public void setRequiredFeatures(Set requiredFeatures) {
+			this.requiredFeatures = requiredFeatures;
+		}
+		public void addRequiredFeatures(Set requiredFeatures) {
+			if (requiredFeatures == null) {
+				requiredFeatures = new HashSet();
+			}
+			this.requiredFeatures.addAll(requiredFeatures);
+		}
+		public IStatus getStatus() {
+			return status;
+		}
+		public void setStatus(IStatus status) {
+			this.status = status;
+		}
+		
+		
+	}
+	
+	public static class InternalImport {
+		
+		private IImport iimport;
+
+		public InternalImport(IImport iimport) {
+			this.iimport = iimport;
+		}
+		
+		public IImport getImport() {
+			return iimport;
+		}
+
+		public void setImport(IImport iimport) {
+			this.iimport = iimport;
+		}
+		
+		public boolean equals(Object object) {
+
+			if ( ( object == null) || !(object instanceof InternalImport))
+				return false;
+			
+			if ( object == this)
+				return true;
+			
+			return iimport.getVersionedIdentifier().equals( ((InternalImport)object).getImport().getVersionedIdentifier()) && (getImport().getRule() == ((InternalImport)object).getImport().getRule());
+
+		}
+
+		public int hashCode() {
+			return iimport.getVersionedIdentifier().hashCode() * iimport.getRule();
+		}
+		
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/PatchCleaner.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/PatchCleaner.java
new file mode 100644
index 0000000..91f419c
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/PatchCleaner.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+
+/**
+ * This utility class attaches as a listened to the provided
+ * configured site, and for every unconfigured feature 
+ * it tests if it is a patch and cleans up its backup configuration.
+ */
+
+public class PatchCleaner {
+	private IConfiguredSite csite;
+	private SiteListener listener;
+	class SiteListener implements IConfiguredSiteChangedListener {
+		public void featureInstalled(IFeature feature) {
+		}
+		public void featureRemoved(IFeature feature) {
+			cleanSavedConfigs(feature);
+		}
+		public void featureConfigured(IFeature feature) {
+		}
+		public void featureUnconfigured(IFeature feature) {
+			cleanSavedConfigs(feature);
+		}
+	}
+	public PatchCleaner(IConfiguredSite csite, IFeature root) {
+		this.csite = csite;
+		listener = new SiteListener();
+		csite.addConfiguredSiteChangedListener(listener);
+	}
+
+	public void dispose() {
+		csite.removeConfiguredSiteChangedListener(listener);
+	}
+	private void cleanSavedConfigs(IFeature feature) {
+		if (feature.isPatch()) {
+			IInstallConfiguration backupConfig = UpdateUtils.getBackupConfigurationFor(feature);
+			if (backupConfig!=null) {
+				// clean it
+				remove(backupConfig);
+			}
+		}
+	}
+	private void remove(IInstallConfiguration config) {
+		try {
+			ILocalSite localSite = SiteManager.getLocalSite();
+			localSite.removeFromPreservedConfigurations(config);
+		}
+		catch (CoreException e) {
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ReplaceFeatureVersionOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ReplaceFeatureVersionOperation.java
new file mode 100644
index 0000000..6d5858b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ReplaceFeatureVersionOperation.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Swaps a feature.
+ * ReplaceFeatureVersionOperation
+ */
+public class ReplaceFeatureVersionOperation
+	extends FeatureOperation
+	implements IConfigFeatureOperation {
+
+	private IFeature anotherFeature;
+	
+	public ReplaceFeatureVersionOperation(
+		IFeature feature,
+		IFeature anotherFeature) {
+		super(feature.getSite().getCurrentConfiguredSite(), feature);
+		this.anotherFeature = anotherFeature;
+	}
+
+	public boolean execute(IProgressMonitor pm, IOperationListener listener)
+		throws CoreException {
+
+		IStatus status =
+			OperationsManager.getValidator().validatePendingReplaceVersion(feature, anotherFeature);
+		if (status != null) {
+			throw new CoreException(status);
+		}
+
+		// unconfigure current feature first, then configure the other one
+		
+		PatchCleaner cleaner = new PatchCleaner(targetSite, feature);
+		targetSite.unconfigure(feature);
+		cleaner.dispose();
+		
+		targetSite.configure(anotherFeature);
+//		ensureUnique();
+
+		try {
+			// Restart not needed
+			boolean restartNeeded = false;
+
+			// Check if this operation is cancelling one that's already pending
+			IOperation pendingOperation =
+				OperationsManager.findPendingOperation(feature);
+
+			if (pendingOperation instanceof IConfigFeatureOperation) {
+				// no need to do either pending change
+				OperationsManager.removePendingOperation(pendingOperation);
+			} else {
+				OperationsManager.addPendingOperation(this);
+				restartNeeded = true;
+			}
+			
+			pendingOperation =
+				OperationsManager.findPendingOperation(anotherFeature);
+				
+			if (pendingOperation instanceof IUnconfigFeatureOperation) {
+				// no need to do either pending change
+				OperationsManager.removePendingOperation(pendingOperation);
+			} else {
+				OperationsManager.addPendingOperation(this);
+				restartNeeded = true;
+			}
+			
+			markProcessed();
+			if (listener != null)
+				listener.afterExecute(this, null);
+
+			restartNeeded = SiteManager.getLocalSite().save() && restartNeeded;
+
+			// notify the model
+			OperationsManager.fireObjectChanged(feature, null);
+
+			return restartNeeded;
+		} catch (CoreException e) {
+			undo();
+			UpdateUtils.logException(e);
+			throw e;
+		}
+	}
+
+	public void undo() throws CoreException {
+		targetSite.unconfigure(anotherFeature);
+		targetSite.configure(feature);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/RevertConfigurationOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/RevertConfigurationOperation.java
new file mode 100644
index 0000000..83e9702
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/RevertConfigurationOperation.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import java.lang.reflect.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+public class RevertConfigurationOperation extends Operation implements IRevertConfigurationOperation {
+
+	private IInstallConfiguration config;
+	private IProblemHandler problemHandler;
+
+	public RevertConfigurationOperation(
+		IInstallConfiguration config,
+		IProblemHandler problemHandler) {
+		super();
+		this.config = config;
+		this.problemHandler = problemHandler;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.operations.IOperation#execute(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public boolean execute(IProgressMonitor monitor, IOperationListener listener)
+		throws CoreException, InvocationTargetException {
+		IStatus status =
+			OperationsManager.getValidator().validatePendingRevert(config);
+		if (status != null && status.getCode() == IStatus.ERROR) {
+			throw new CoreException(status);
+		}
+
+		try {
+			ILocalSite localSite = SiteManager.getLocalSite();
+			localSite.revertTo(config, monitor, problemHandler);
+			localSite.save();
+			return true;
+		} catch (CoreException e) {
+			UpdateUtils.logException(e);
+			throw e;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ToggleSiteOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ToggleSiteOperation.java
new file mode 100644
index 0000000..d9fcd21
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/ToggleSiteOperation.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+public class ToggleSiteOperation
+	extends Operation
+	implements IToggleSiteOperation {
+
+	private IConfiguredSite site;
+
+	public ToggleSiteOperation(IConfiguredSite site) {
+		super();
+		this.site = site;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.operations.IOperation#execute(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public boolean execute(
+		IProgressMonitor monitor,
+		IOperationListener listener)
+		throws CoreException {
+		if (site == null)
+			return false;
+		boolean oldValue = site.isEnabled();
+		site.setEnabled(!oldValue);
+		IStatus status = OperationsManager.getValidator().validateCurrentState();
+		if (status != null) {
+			// revert
+			site.setEnabled(oldValue);
+			throw new CoreException(status);
+		} else {
+			try {
+				boolean restartNeeded = SiteManager.getLocalSite().save();
+				OperationsManager.fireObjectChanged(site, ""); //$NON-NLS-1$
+				return restartNeeded; // will restart only if changes could not be applied to current config
+			} catch (CoreException e) {
+				//revert
+				site.setEnabled(oldValue);
+				UpdateUtils.logException(e);
+				throw e;
+			}
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigOperation.java
new file mode 100644
index 0000000..f0c8808
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigOperation.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Unconfigure a feature.
+ * UnconfigOperation
+ */
+public class UnconfigOperation
+	extends FeatureOperation
+	implements IUnconfigFeatureOperation {
+
+	public UnconfigOperation(
+		IConfiguredSite site,
+		IFeature feature) {
+		super(site, feature);
+	}
+
+	public boolean execute(IProgressMonitor pm, IOperationListener listener)
+		throws CoreException {
+
+		IStatus status =
+			OperationsManager.getValidator().validatePendingUnconfig(feature);
+		if (status != null && status.getCode() == IStatus.ERROR) {
+			throw new CoreException(status);
+		}
+
+		PatchCleaner cleaner = new PatchCleaner(targetSite, feature);
+		targetSite.unconfigure(feature);
+		cleaner.dispose();
+
+		try {
+			// Restart not needed
+			boolean restartNeeded = false;
+
+			// Check if this operation is cancelling one that's already pending
+			IOperation pendingOperation =
+				OperationsManager.findPendingOperation(feature);
+
+			if (pendingOperation instanceof IConfigFeatureOperation) {
+				// no need to do either pending change
+				OperationsManager.removePendingOperation(pendingOperation);
+			} else {
+				OperationsManager.addPendingOperation(this);
+				restartNeeded = true;
+			}
+
+			markProcessed();
+			if (listener != null)
+				listener.afterExecute(this, null);
+
+			restartNeeded = SiteManager.getLocalSite().save() && restartNeeded;
+
+			// notify the model
+			OperationsManager.fireObjectChanged(feature, null);
+
+			return restartNeeded;
+		} catch (CoreException e) {
+			undo();
+			UpdateUtils.logException(e);
+			throw e;
+		}
+	}
+
+	public void undo() throws CoreException {
+		targetSite.configure(feature);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureAndUninstallFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureAndUninstallFeatureOperation.java
new file mode 100644
index 0000000..ed22c60
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureAndUninstallFeatureOperation.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.configurator.ConfiguratorUtils;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.operations.IOperationListener;
+import org.eclipse.update.operations.IUnconfigFeatureOperation;
+import org.eclipse.update.operations.OperationsManager;
+
+public class UnconfigureAndUninstallFeatureOperation extends FeatureOperation
+		implements IUnconfigureAndUninstallFeatureOperation {
+
+	public UnconfigureAndUninstallFeatureOperation(IConfiguredSite targetSite, IFeature feature) {
+		super(targetSite, feature);
+	}
+
+	public boolean execute(IProgressMonitor pm, IOperationListener listener)
+			throws CoreException, InvocationTargetException {
+		
+		IUnconfigFeatureOperation unconfigOperation = OperationsManager.getOperationFactory().createUnconfigOperation(targetSite, feature);
+
+		/*boolean isRestartNeeded = */unconfigOperation.execute(pm, listener);
+		
+
+		URL platformXML = ConfiguratorUtils.getCurrentPlatformConfiguration().getConfigurationLocation();
+		
+		File f = new File(platformXML.getFile());
+
+		f = new File(f.getParentFile(), "toBeUninstalled"); //$NON-NLS-1$
+		try {
+			if (!f.exists()) {
+				f.createNewFile();
+			}
+			FileInputStream fis = new FileInputStream(f);
+			Properties toBeUninstalled = new Properties();
+			toBeUninstalled.load(fis);
+			toBeUninstalled.put(new Integer(toBeUninstalled.size()+1).toString(), targetSite.getSite().getURL() + ";" + feature.getVersionedIdentifier().toString()); //$NON-NLS-1$
+			fis.close();
+			FileOutputStream fos = new FileOutputStream(f);
+			toBeUninstalled.store(fos, "to be uninstalled on start-up"); //$NON-NLS-1$
+			fos.close();
+			
+		} catch (IOException ioe) {
+			
+		}
+
+		
+		return true;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureAndUninstallFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureAndUninstallFeaturesOperation.java
new file mode 100644
index 0000000..a96108f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureAndUninstallFeaturesOperation.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.operations.IOperation;
+import org.eclipse.update.operations.OperationsManager;
+
+public class UnconfigureAndUninstallFeaturesOperation extends
+		BatchFeatureOperation implements
+		IUnconfigureAndUninstallFeaturesOperation {
+
+	public UnconfigureAndUninstallFeaturesOperation(IConfiguredSite[] targetSites,
+			IFeature[] features) {
+		super(targetSites, features);
+	}
+
+	protected IOperation createOperation(IConfiguredSite targetSite, IFeature feature) {
+		return ((OperationFactory)OperationsManager.getOperationFactory()).createUnconfigureAndUninstallFeatureOperation(targetSite, feature);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureFeaturesOperation.java
new file mode 100644
index 0000000..184c8b8
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UnconfigureFeaturesOperation.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.operations.IOperation;
+import org.eclipse.update.operations.OperationsManager;
+
+public class UnconfigureFeaturesOperation 
+	extends BatchFeatureOperation implements IUnconfigureFeaturesOperation {
+
+	public UnconfigureFeaturesOperation(IConfiguredSite[] targetSites, IFeature[] features) {
+		super(targetSites, features);
+	}
+	
+	protected IOperation createOperation(IConfiguredSite targetSite, IFeature feature) {
+		return OperationsManager.getOperationFactory().createUnconfigOperation(targetSite, feature);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UninstallFeaturesOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UninstallFeaturesOperation.java
new file mode 100644
index 0000000..c6879de
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UninstallFeaturesOperation.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.update.internal.operations;
+
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.operations.IOperation;
+import org.eclipse.update.operations.OperationsManager;
+
+public class UninstallFeaturesOperation extends BatchFeatureOperation implements
+		IUninstallFeaturesOperation {
+
+	public UninstallFeaturesOperation(IConfiguredSite[] targetSites, IFeature[] features) {
+		super(targetSites, features);
+	}
+
+
+	protected IOperation createOperation(IConfiguredSite targetSite, IFeature feature) {
+		return OperationsManager.getOperationFactory().createUninstallOperation(targetSite, feature);
+	}
+
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UninstallOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UninstallOperation.java
new file mode 100644
index 0000000..136b7a4
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UninstallOperation.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.operations.*;
+
+/**
+ * Configure a feature.
+ * ConfigOperation
+ */
+public class UninstallOperation extends FeatureOperation implements IUninstallFeatureOperation{
+
+	public UninstallOperation(IConfiguredSite site, IFeature feature) {
+		super(site, feature);
+	}
+
+	public void setTargetSite(IConfiguredSite targetSite) {
+		this.targetSite = targetSite;
+	}
+
+	public boolean execute(IProgressMonitor pm, IOperationListener listener) throws CoreException {
+		
+		if (targetSite == null)
+			targetSite = UpdateUtils.getConfigSite(feature, SiteManager.getLocalSite().getCurrentConfiguration());
+
+		// Restart not needed
+		boolean restartNeeded = false;
+
+		if (targetSite != null) {
+			// if needed, unconfigure the feature first
+			if (targetSite.isConfigured(feature)) {
+				IStatus status = OperationsManager.getValidator().validatePendingUnconfig(feature);
+				if (status != null && status.getCode() == IStatus.ERROR)
+					throw new CoreException(status);
+				if (unconfigure(feature, targetSite))
+					restartNeeded = true;
+				else
+					throw Utilities.newCoreException(NLS.bind(Messages.OperationsManager_error_uninstall, (new String[] { feature.getVersionedIdentifier().toString() })), null);
+			}
+			targetSite.remove(feature, pm);
+		} else {
+			// we should do something here
+			String message =
+				NLS.bind(Messages.OperationsManager_error_uninstall, (new String[] { feature.getLabel() }));
+			IStatus status =
+				new Status(
+					IStatus.ERROR,
+					UpdateUtils.getPluginId(),
+					IStatus.OK,
+					message,
+					null);
+			throw new CoreException(status);
+		}
+
+		markProcessed();
+		if (listener != null)
+			listener.afterExecute(this, null);
+
+		restartNeeded = SiteManager.getLocalSite().save() && restartNeeded;
+
+		// notify the model
+		OperationsManager.fireObjectChanged(feature, UNINSTALL);
+		
+		return restartNeeded;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UpdateUtils.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UpdateUtils.java
new file mode 100644
index 0000000..6914a0a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/operations/UpdateUtils.java
@@ -0,0 +1,692 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ *     James D Miles (IBM Corp.) - bug 191368, Policy URL doesn't support UTF-8 characters
+ *******************************************************************************/
+package org.eclipse.update.internal.operations;
+
+import java.lang.reflect.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.core.model.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.internal.core.URLEncoder;
+import org.eclipse.update.internal.search.*;
+import org.eclipse.update.operations.*;
+import org.eclipse.update.search.*;
+
+
+/**
+ * Helper class for performing update related tasks, queries, etc.
+ * All the methods are static and this class should be a singleton.
+ */
+public class UpdateUtils {
+	public static final String P_UPDATE_POLICY_URL = "updatePolicyURL"; //$NON-NLS-1$
+
+	/**
+	 * Private constructor
+	 */
+	private UpdateUtils() {
+	}
+	
+
+	public static String getPluginId() {
+		return UpdateCore.getPlugin().getBundle().getSymbolicName();
+	}
+
+
+	public static void logException(Throwable e) {
+		
+		if (e instanceof InvocationTargetException) {
+			e = ((InvocationTargetException) e).getTargetException();
+		}
+
+		IStatus status = null;
+		if (e instanceof CoreException) {
+			status = ((CoreException) e).getStatus();
+		} else {
+			String message = e.getMessage();
+			if (message == null)
+				message = e.toString();
+			status = new Status(IStatus.ERROR, getPluginId(), IStatus.OK, message, e);
+		}
+		log(status);
+	}
+
+	public static void log(IStatus status) {
+		if (status.getSeverity() != IStatus.INFO) {
+			UpdateCore.getPlugin().getLog().log(status);
+		} 
+	}
+
+	public static IFeature[] searchSite(String featureId, IConfiguredSite site, boolean onlyConfigured) throws CoreException {
+		IFeatureReference[] references = null;
+
+		if (onlyConfigured)
+			references = site.getConfiguredFeatures();
+		else
+			references = site.getSite().getFeatureReferences();
+		Vector result = new Vector();
+
+		for (int i = 0; i < references.length; i++) {
+			IFeature feature = references[i].getFeature(null);
+			String id = feature.getVersionedIdentifier().getIdentifier();
+			if (featureId.equals(id)) {
+				result.add(feature);
+			}
+		}
+		return (IFeature[]) result.toArray(new IFeature[result.size()]);
+	}
+
+	public static IFeature[] getInstalledFeatures(IFeature feature) {
+		return getInstalledFeatures(feature, true);
+	}
+
+	/**
+	 * 
+	 * @param feature
+	 * @param onlyConfigured
+	 * @return IFeature[] with features matching feature ID
+	 */
+	public static IFeature[] getInstalledFeatures(IFeature feature, boolean onlyConfigured) {
+		return getInstalledFeatures(feature.getVersionedIdentifier(), onlyConfigured);
+	}
+	/**
+	 * @param vid
+	 * @param onlyConfigured
+	 * @return IFeature[] with features matching feature ID
+	 */
+	public static IFeature[] getInstalledFeatures(VersionedIdentifier vid, boolean onlyConfigured) {
+		Vector features = new Vector();
+		try {
+			ILocalSite localSite = SiteManager.getLocalSite();
+			IInstallConfiguration config = localSite.getCurrentConfiguration();
+			IConfiguredSite[] isites = config.getConfiguredSites();
+			String id = vid.getIdentifier();
+
+			for (int i = 0; i < isites.length; i++) {
+				IConfiguredSite isite = isites[i];
+				IFeature[] result = UpdateUtils.searchSite(id, isite, onlyConfigured);
+				for (int j = 0; j < result.length; j++) {
+					IFeature installedFeature = result[j];
+					features.add(installedFeature);
+				}
+			}
+		} catch (CoreException e) {
+			UpdateUtils.logException(e);
+		}
+		return (IFeature[]) features.toArray(new IFeature[features.size()]);
+	}
+	
+	/**
+	 * @param patch
+	 * @return IFeature or null
+	 */
+	public static IFeature getPatchedFeature(IFeature patch) {
+		IImport[] imports = patch.getImports();
+		for (int i = 0; i < imports.length; i++) {
+			IImport iimport = imports[i];
+			if (iimport.isPatch()) {
+				VersionedIdentifier patchedVid = iimport
+						.getVersionedIdentifier();
+				// features with matching id
+				IFeature[] features = getInstalledFeatures(patchedVid, false);
+				for (int f = 0; f < features.length; f++) {
+					// check if version match
+					if (patchedVid.equals(features[f].getVersionedIdentifier())) {
+						return features[f];
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	public static boolean isPatch(IFeature candidate) {
+		IImport[] imports = candidate.getImports();
+
+		for (int i = 0; i < imports.length; i++) {
+			IImport iimport = imports[i];
+			if (iimport.isPatch()) return true;
+		}
+		return false;
+	}
+
+	public static boolean isPatch(IFeature target, IFeature candidate) {
+		VersionedIdentifier vid = target.getVersionedIdentifier();
+		IImport[] imports = candidate.getImports();
+
+		for (int i = 0; i < imports.length; i++) {
+			IImport iimport = imports[i];
+			if (iimport.isPatch()) {
+				VersionedIdentifier ivid = iimport.getVersionedIdentifier();
+				if (vid.equals(ivid)) {
+					// Bingo.
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	public static IInstallConfiguration getBackupConfigurationFor(IFeature feature) {
+		VersionedIdentifier vid = feature.getVersionedIdentifier();
+		String key = "@" + vid.getIdentifier() + "_" + vid.getVersion(); //$NON-NLS-1$ //$NON-NLS-2$
+		try {
+			ILocalSite lsite = SiteManager.getLocalSite();
+			IInstallConfiguration[] configs = lsite.getPreservedConfigurations();
+			for (int i = 0; i < configs.length; i++) {
+				IInstallConfiguration config = configs[i];
+				if (config.getLabel().startsWith(key))
+					return config;
+			}
+		} catch (CoreException e) {
+		}
+		return null;
+	}
+	
+	
+	public static boolean hasLicense(IFeature feature) {
+		IURLEntry info = feature.getLicense();
+		if (info == null)
+			return false;
+		String licenseTxt = info.getAnnotation();
+		if (licenseTxt == null)
+			return false;
+		return licenseTxt.trim().length() > 0;
+	}
+	public static boolean hasOptionalFeatures(IFeature feature) {
+		try {
+			IIncludedFeatureReference[] irefs = feature.getIncludedFeatureReferences();
+			for (int i = 0; i < irefs.length; i++) {
+				IIncludedFeatureReference iref = irefs[i];
+				if (iref.isOptional())
+					return true;
+				// see if it has optional children
+				IFeature child = getIncludedFeature( feature, iref);
+				if (hasOptionalFeatures(child))
+					return true;
+			}
+		} catch (CoreException e) {
+		}
+		return false;
+	}
+
+	public static IFeature getLocalFeature(
+		IConfiguredSite csite,
+		IFeature feature)
+		throws CoreException {
+		IFeatureReference[] refs = csite.getConfiguredFeatures();
+		for (int i = 0; i < refs.length; i++) {
+			IFeatureReference ref = refs[i];
+			VersionedIdentifier refVid = ref.getVersionedIdentifier();
+			if (feature.getVersionedIdentifier().equals(refVid))
+				return ref.getFeature(null);
+		}
+		return null;
+	}
+	
+	public static IConfiguredSite getConfigSite(
+		IFeature feature,
+		IInstallConfiguration config)
+		throws CoreException {
+		IConfiguredSite[] configSites = config.getConfiguredSites();
+		for (int i = 0; i < configSites.length; i++) {
+			IConfiguredSite site = configSites[i];
+			if (site.getSite().equals(feature.getSite())) {
+				return site;
+			}
+		}
+		return null;
+	}
+	
+	public static IConfiguredSite getDefaultTargetSite(
+		IInstallConfiguration config,
+		IInstallFeatureOperation pendingChange) {
+		return getDefaultTargetSite(config, pendingChange, true);
+	}
+
+	public  static IConfiguredSite getDefaultTargetSite(
+		IInstallConfiguration config,
+		IInstallFeatureOperation pendingChange,
+		boolean checkAffinityFeature) {
+		IFeature oldFeature = pendingChange.getOldFeature();
+		IFeature newFeature = pendingChange.getFeature();
+		if (oldFeature != null) {
+			// We should install into the same site as
+			// the old feature
+			try {
+				return getConfigSite(oldFeature, config);
+			} catch (CoreException e) {
+				logException(e);
+				return null;
+			}
+		}
+
+		// This is a new install. Check if there is 
+		// a disabled feature with the same ID
+		String newFeatureID =
+			newFeature.getVersionedIdentifier().getIdentifier();
+		IConfiguredSite sameSite = getSiteWithFeature(config, newFeatureID);
+		if (sameSite != null) {
+			return sameSite;
+		}
+
+		if (checkAffinityFeature) {
+			return getAffinitySite(config, newFeature);
+		}
+		return null;
+	}
+	
+	public static IConfiguredSite getAffinitySite(
+		IInstallConfiguration config,
+		IFeature newFeature) {
+		// check if the affinity feature is installed
+		String affinityID = newFeature.getAffinityFeature();
+		if (affinityID != null) {
+			IConfiguredSite affinitySite =
+				getSiteWithFeature(config, affinityID);
+			if (affinitySite != null)
+				return affinitySite;
+		} else {
+			// if this is a patch, collocate with the feature
+			IFeature patchedFeature = getPatchedFeature(newFeature);
+			if (patchedFeature != null)
+				return getSiteWithFeature(config, patchedFeature.getVersionedIdentifier().getIdentifier());
+		}
+		return null;
+	}
+
+	public static IConfiguredSite getSiteWithFeature(
+		IInstallConfiguration config,
+		String featureID) {
+		if (featureID == null)
+			return null;
+		IConfiguredSite[] sites = config.getConfiguredSites();
+		for (int i = 0; i < sites.length; i++) {
+			IConfiguredSite site = sites[i];
+			IFeatureReference[] refs = site.getFeatureReferences();
+			for (int j = 0; j < refs.length; j++) {
+				IFeatureReference ref = refs[j];
+				try {
+					IFeature feature = ref.getFeature(null);
+					if (featureID
+						.equals(
+							feature
+								.getVersionedIdentifier()
+								.getIdentifier())) {
+						// found it
+						return site;
+					}
+				} catch (CoreException e) {
+					logException(e);
+				}
+			}
+		}
+		return null;
+	}
+	
+	public static void collectOldFeatures(
+		IFeature feature,
+		IConfiguredSite targetSite,
+		ArrayList result)
+		throws CoreException {
+		IIncludedFeatureReference[] included =
+			feature.getIncludedFeatureReferences();
+		for (int i = 0; i < included.length; i++) {
+			IIncludedFeatureReference iref = included[i];
+
+			IFeature ifeature;
+
+			try {
+				ifeature = iref.getFeature(null);
+			} catch (CoreException e) {
+				if (iref.isOptional())
+					continue;
+				throw e;
+			}
+			// find other features and unconfigure
+			String id = iref.getVersionedIdentifier().getIdentifier();
+			IFeature[] sameIds = UpdateUtils.searchSite(id, targetSite, true);
+			for (int j = 0; j < sameIds.length; j++) {
+				IFeature sameId = sameIds[j];
+				// Ignore self.
+				if (sameId.equals(ifeature))
+					continue;
+				result.add(sameId);
+			}
+			collectOldFeatures(ifeature, targetSite, result);
+		}
+	}
+
+//
+//	public static IInstallConfiguration createInstallConfiguration() throws CoreException{
+//		try {
+//			ILocalSite localSite = SiteManager.getLocalSite();
+//			IInstallConfiguration config =
+//				localSite.cloneCurrentConfiguration();
+//			config.setLabel(Utilities.format(config.getCreationDate()));
+//			return config;
+//		} catch (CoreException e) {
+//			// Let callers handle logging
+//			//logException(e);
+//			throw e;
+//		}
+//	}
+	
+	public static UpdateSearchRequest createNewUpdatesRequest(IFeature [] features) {
+		return createNewUpdatesRequest(features, true);
+	}
+	
+	public static UpdateSearchRequest createNewUpdatesRequest(IFeature [] features, boolean automatic) {
+		UpdateSearchScope scope = new UpdateSearchScope();
+		scope.setUpdateMapURL(UpdateUtils.getUpdateMapURL());
+		UpdatesSearchCategory category = new UpdatesSearchCategory(automatic);
+		if (features!=null)
+			category.setFeatures(features);
+		UpdateSearchRequest searchRequest = new UpdateSearchRequest(category, scope);
+		searchRequest.addFilter(new EnvironmentFilter());
+		searchRequest.addFilter(new BackLevelFilter());
+		return searchRequest;
+	}
+
+	public static boolean isNestedChild(IInstallConfiguration config, IFeature feature) {
+		IConfiguredSite[] csites = config.getConfiguredSites();
+		try {
+			for (int i = 0; csites != null && i < csites.length; i++) {
+				IFeatureReference[] refs = csites[i].getConfiguredFeatures();
+				for (int j = 0; refs != null && j < refs.length; j++) {
+					IFeature parent = refs[j].getFeature(null);
+					IFeatureReference[] children =
+						parent.getIncludedFeatureReferences();
+					for (int k = 0;
+						children != null && k < children.length;
+						k++) {
+						IFeature child = children[k].getFeature(null);
+						if (feature.equals(child))
+							return true;
+					}
+				}
+			}
+		} catch (CoreException e) {
+			// will return false
+		}
+		return false;
+	}
+	
+	
+	public static boolean hasObsoletePatches(IFeature feature) {
+		// Check all the included features that
+		// are unconfigured, and see if their patch 
+		// references are better than the original.
+		try {
+			IFeatureReference[] irefs = feature.getIncludedFeatureReferences();
+			for (int i = 0; i < irefs.length; i++) {
+				IFeatureReference iref = irefs[i];
+				IFeature ifeature = iref.getFeature(null);
+				IConfiguredSite csite = ifeature.getSite().getCurrentConfiguredSite();
+				if (!csite.isConfigured(ifeature)) {
+					if (!isPatchHappy(ifeature))
+						return false;
+				}
+			}
+		} catch (CoreException e) {
+			return false;
+		}
+		// All checks went well
+		return true;
+	}
+	
+	public static boolean isPatchHappy(IFeature feature) throws CoreException {
+		// If this is a patch and it includes 
+		// another patch and the included patch
+		// is disabled but the feature it was declared
+		// to patch is now newer (and is presumed to
+		// contain the now disabled patch), and
+		// the newer patched feature is enabled,
+		// a 'leap of faith' assumption can be
+		// made:
+
+		// Although the included patch is disabled,
+		// the feature it was designed to patch
+		// is now newer and most likely contains
+		// the equivalent fix and more. Consequently,
+		// we can claim that the status and the error
+		// icon overlay are misleading because
+		// all the right plug-ins are configured.
+		IImport[] imports = feature.getImports();
+		IImport patchReference = null;
+		for (int i = 0; i < imports.length; i++) {
+			IImport iimport = imports[i];
+			if (iimport.isPatch()) {
+				patchReference = iimport;
+				break;
+			}
+		}
+		if (patchReference == null)
+			return false;
+		VersionedIdentifier refVid = patchReference.getVersionedIdentifier();
+
+		// Find the patched feature and 
+		IConfiguredSite csite = feature.getSite().getCurrentConfiguredSite();
+		if (csite == null)
+			return false;
+
+		IFeatureReference[] crefs = csite.getConfiguredFeatures();
+		for (int i = 0; i < crefs.length; i++) {
+			IFeatureReference cref = crefs[i];
+			VersionedIdentifier cvid = cref.getVersionedIdentifier();
+			if (cvid.getIdentifier().equals(refVid.getIdentifier())) {
+				// This is the one.
+				if (cvid.getVersion().isGreaterThan(refVid.getVersion())) {
+					// Bingo: we found the referenced feature
+					// and its version is greater - 
+					// we can assume that it contains better code
+					// than the patch that referenced the
+					// older version.
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	public static URL getUpdateMapURL() {
+		Preferences pref = UpdateCore.getPlugin().getPluginPreferences();
+		String mapFile = pref.getString(UpdateUtils.P_UPDATE_POLICY_URL);
+		if (mapFile!=null && mapFile.length()>0) {
+			try {
+				URL url = new URL(mapFile);
+				URL resolvedURL = URLEncoder.encode(url);
+				return resolvedURL;
+			}
+			catch (MalformedURLException e) {
+				UpdateUtils.logException(e);
+			}
+		}
+		return null;
+	}
+	
+	/*
+	 * Load the update map using the map URL found in the scope.
+	 */	
+	public static IStatus loadUpdatePolicy(UpdatePolicy map, URL url, IProgressMonitor monitor) throws CoreException {
+		monitor.subTask(Messages.UpdateSearchRequest_loadingPolicy); 
+		try {
+			map.load(url, monitor);
+			monitor.worked(1);
+		}
+		catch (CoreException e) {
+			IStatus status = e.getStatus();
+			if (status == null
+				|| status.getCode() != ISite.SITE_ACCESS_EXCEPTION)
+				throw e;
+			monitor.worked(1);
+			return status;
+		}
+		return null;
+	}
+
+	public static void downloadFeatureContent(
+        IConfiguredSite targetSite,
+		IFeature feature,
+		IFeatureReference[] optionalChildren, // null when feature has no optional features
+		IProgressMonitor progress)
+		throws InstallAbortedException, CoreException {
+		
+		//DEBUG
+		if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL) {
+			UpdateCore.debug(
+				"Downloading...:" + feature.getURL().toExternalForm()); //$NON-NLS-1$
+		}
+
+		// Get source feature provider and verifier.
+		// Initialize target variables.
+		final IFeatureContentProvider provider =
+			feature.getFeatureContentProvider();
+		IPluginEntry[] targetSitePluginEntries = null;
+
+		// determine list of plugins to install
+		// find the intersection between the plugin entries already contained
+		// on the target site, and plugin entries packaged in source feature
+		IPluginEntry[] sourceFeaturePluginEntries = feature.getPluginEntries();
+
+        boolean featureAlreadyInstalled = false;
+        if (targetSite == null)
+            targetSite = getSiteWithFeature(SiteManager.getLocalSite()
+                    .getCurrentConfiguration(), ((Feature) feature)
+                    .getFeatureIdentifier());
+		if (targetSite == null) {
+			if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL) {
+				UpdateCore.debug("The site to install in is null"); //$NON-NLS-1$
+			}
+
+			targetSitePluginEntries = new IPluginEntry[0];
+		} else {
+			targetSitePluginEntries = targetSite.getSite().getPluginEntries();
+            featureAlreadyInstalled = UpdateUtils.getLocalFeature(targetSite,feature) != null;
+		}
+		IPluginEntry[] pluginsToInstall =
+			UpdateManagerUtils.diff(
+				sourceFeaturePluginEntries,
+				targetSitePluginEntries);
+		INonPluginEntry[] nonPluginsToInstall = feature.getNonPluginEntries();
+		
+		IFeatureReference[] children = feature.getIncludedFeatureReferences();
+		if (optionalChildren != null) {
+			children =
+				UpdateManagerUtils.optionalChildrenToInstall(
+					children,
+					optionalChildren);
+		}
+
+		// make sure we have an InstallMonitor		
+		InstallMonitor monitor;
+		if (progress == null)
+			monitor = new InstallMonitor(new NullProgressMonitor());
+		else if (progress instanceof InstallMonitor)
+			monitor = (InstallMonitor) progress;
+		else
+			monitor = new InstallMonitor(progress);
+
+		try {
+			// determine number of monitor tasks
+			//   1 task1 for the feature jar (download)
+			// + n tasks for plugin entries (download for each)
+			// + m tasks per non-plugin data entry (download for each)
+			// TODO custom install handler  + 1 task for custom non-plugin entry handling (1 for all combined)
+			// + 3*x tasks for children features (3 subtasks per install)
+			int taskCount =
+					1
+					+ pluginsToInstall.length
+					+ nonPluginsToInstall.length
+//				+ 1
+					+ 3 * children.length;
+			monitor.beginTask("", taskCount); //$NON-NLS-1$
+			
+			// Download feature archive(s)
+			provider.getFeatureEntryArchiveReferences(monitor);
+			monitorWork(monitor,1);
+			
+			// Download plugin archives
+			for (int i = 0; i < pluginsToInstall.length; i++) {
+				provider.getPluginEntryArchiveReferences(pluginsToInstall[i], monitor);
+				monitorWork(monitor,1);
+			}
+			
+			
+
+			// Download non-plugin archives. Verification handled by optional install handler
+            // Note: do not download non-plugin archives for installed features
+            if (nonPluginsToInstall.length > 0) {
+                // Setup optional install handler
+                InstallHandlerProxy handler = null;
+                if (feature.getInstallHandlerEntry()!=null)
+                    handler = new InstallHandlerProxy(
+                        IInstallHandler.HANDLER_ACTION_INSTALL,
+                        feature,
+                        feature.getInstallHandlerEntry(),
+                        monitor);
+            	
+                if (!featureAlreadyInstalled)
+                    for (int i = 0; i < nonPluginsToInstall.length; i++) {
+                        if (handler==null || handler.acceptNonPluginData(nonPluginsToInstall[i]))
+                        	provider.getNonPluginEntryArchiveReferences(
+                                nonPluginsToInstall[i], monitor);
+                        monitorWork(monitor, 1);
+                    }
+                else
+                    monitorWork(monitor, nonPluginsToInstall.length);
+            }
+           
+			// Download child features
+			for (int i = 0; i < children.length; i++) {
+				IFeature childFeature = null;
+				try {
+					childFeature = children[i].getFeature(null);
+				} catch (CoreException e) {
+					UpdateCore.warn(null, e);
+				}
+				if (childFeature != null) {
+					SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 3);
+					downloadFeatureContent(targetSite, childFeature, optionalChildren, subMonitor);
+				}
+			}
+		} finally {
+			if (monitor != null)
+				monitor.done();
+		}
+	}
+	
+	private static void monitorWork(IProgressMonitor monitor, int tick)
+	throws CoreException {
+	if (monitor != null) {
+		monitor.worked(tick);
+		if (monitor.isCanceled()) {
+			String msg = "download cancelled";//Policy.bind("Feature.InstallationCancelled"); //$NON-NLS-1$
+			throw new InstallAbortedException(msg, null);
+		}
+	}
+}
+	public static IFeature getIncludedFeature(IFeature feature, IFeatureReference iref) throws CoreException {
+		IFeature ifeature = null;
+		if (feature.getSite() instanceof ExtendedSite) {
+			ifeature = ((ExtendedSite)feature.getSite()).getLiteFeature(iref.getVersionedIdentifier());
+		}
+		if (ifeature == null) {
+			ifeature = iref.getFeature(new NullProgressMonitor());
+		}
+		return ifeature;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/provisional/SiteOptimizerApplication.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/provisional/SiteOptimizerApplication.java
new file mode 100644
index 0000000..1a979a2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/provisional/SiteOptimizerApplication.java
@@ -0,0 +1,907 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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
+ *     Chris Aniszczyk (IBM Corp.) - NL-enabled the site optimizer
+ *******************************************************************************/
+package org.eclipse.update.internal.provisional;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPlatformRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.IIncludedFeatureReference;
+import org.eclipse.update.core.IncludedFeatureReference;
+import org.eclipse.update.core.model.DefaultSiteParser;
+import org.eclipse.update.core.model.FeatureModel;
+import org.eclipse.update.core.model.FeatureModelFactory;
+import org.eclipse.update.core.model.FeatureReferenceModel;
+import org.eclipse.update.core.model.ImportModel;
+import org.eclipse.update.core.model.PluginEntryModel;
+import org.eclipse.update.core.model.SiteModel;
+import org.eclipse.update.core.model.URLEntryModel;
+import org.eclipse.update.internal.core.ExtendedSiteURLFactory;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.internal.jarprocessor.JarProcessor;
+import org.eclipse.update.internal.jarprocessor.JarProcessorExecutor;
+import org.eclipse.update.internal.jarprocessor.Main;
+import org.xml.sax.SAXException;
+
+/**
+ * The application class used to perform update site optimizations.
+ * <p>
+ * This class can only be referenced from <code>org.eclipse.runtime.applications</code>
+ * extension point. It should not be extended or instantiated.
+ * <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>
+ * 
+ * @since 3.2
+ */
+public class SiteOptimizerApplication implements IPlatformRunnable {
+	public final static Integer EXIT_ERROR = new Integer(1);
+
+	public final static String JAR_PROCESSOR = "-jarProcessor"; //$NON-NLS-1$
+
+	public final static String DIGEST_BUILDER = "-digestBuilder"; //$NON-NLS-1$
+
+	public final static String INPUT = "input"; //$NON-NLS-1$
+
+	public final static String OUTPUT_DIR = "-outputDir"; //$NON-NLS-1$
+
+	public final static String VERBOSE = "-verbose"; //$NON-NLS-1$
+
+	public final static String JAR_PROCESSOR_PACK = "-pack"; //$NON-NLS-1$
+
+	public final static String JAR_PROCESSOR_UNPACK = "-unpack"; //$NON-NLS-1$
+
+	public final static String JAR_PROCESSOR_REPACK = "-repack"; //$NON-NLS-1$
+
+	public final static String JAR_PROCESSOR_SIGN = "-sign"; //$NON-NLS-1$
+
+	public final static String JAR_PROCESSOR_PROCESS_ALL = "-processAll"; //$NON-NLS-1$
+
+	public final static String SITE_XML = "-siteXML"; //$NON-NLS-1$
+
+	public final static String SITE_ATTRIBUTES_FILE = "siteAttributes.txt"; //$NON-NLS-1$
+
+	public final static String DIGEST_OUTPUT_DIR = "-digestOutputDir"; //$NON-NLS-1$
+
+	/*
+	 * private final static String DESCRIPTION = "DESCRIPTION"; private final
+	 * static String LICENCE = "LICENCE"; private final static String COPYRIGHT =
+	 * "COPYRIGHT"; private final static String FEATURE_LABEL = "FEATURE_LABEL";
+	 */
+
+	/**
+	 * Parses the command line in the form: [-key [value]]* [inputvalue] If the
+	 * last argument does not start with a "-" then it is taken as the input
+	 * value and not the value for a preceding -key
+	 * 
+	 * @param args
+	 * @return
+	 */
+	private Map parseCmdLine(String[] args) {
+		Map cmds = new HashMap();
+		for (int i = 0; i < args.length; i++) {
+			if (i == args.length - 1 && !args[i].startsWith("-")) { //$NON-NLS-1$
+				cmds.put(INPUT, args[i]);
+			} else {
+				String key = args[i];
+				String val = null;
+				if (i < args.length - 2 && !args[i + 1].startsWith("-")) { //$NON-NLS-1$
+					val = args[++i];
+				}
+
+				if (key.startsWith(SITE_XML)) {
+					// System.out.println(val.indexOf(":null"));
+					val = key.substring(key.indexOf("=") + 1); //$NON-NLS-1$
+					// System.out.println(key + ":" + val);
+					cmds.put(SITE_XML, val);
+				} else if (key.startsWith(DIGEST_OUTPUT_DIR)) {
+					val = key.substring(key.indexOf("=") + 1); //$NON-NLS-1$
+					// System.out.println(key + ":" + val);
+					cmds.put(DIGEST_OUTPUT_DIR, val);
+				} else {
+
+					// System.out.println(key + ":" + val);
+					cmds.put(key, val);
+				}
+			}
+		}
+		return cmds;
+	}
+
+	private boolean runJarProcessor(Map params) {
+		Main.Options options = new Main.Options();
+		options.pack = params.containsKey(JAR_PROCESSOR_PACK);
+		options.unpack = params.containsKey(JAR_PROCESSOR_UNPACK);
+		options.repack = params.containsKey(JAR_PROCESSOR_REPACK);
+		options.processAll = params.containsKey(JAR_PROCESSOR_PROCESS_ALL);
+		options.verbose = params.containsKey(VERBOSE);
+		options.signCommand = (String) params.get(JAR_PROCESSOR_SIGN);
+		options.outputDir = (String) params.get(OUTPUT_DIR);
+
+		String problem = null;
+
+		String input = (String) params.get(INPUT);
+		if (input == null)
+			problem = Messages.SiteOptimizer_inputNotSpecified;
+		else {
+			File inputFile = new File(input);
+			if (inputFile.exists())
+				options.input = inputFile;
+			else
+				problem = NLS.bind(Messages.SiteOptimizer_inputFileNotFound,
+						new String[] { input });
+		}
+
+		if (options.unpack) {
+			if (!JarProcessor.canPerformUnpack()) {
+				problem = Messages.JarProcessor_unpackNotFound;
+			} else if (options.pack || options.repack
+					|| options.signCommand != null) {
+				problem = Messages.JarProcessor_noPackUnpack;
+			}
+		} else if ((options.pack || options.repack)
+				&& !JarProcessor.canPerformPack()) {
+			problem = Messages.JarProcessor_packNotFound;
+		}
+
+		if (problem != null) {
+			System.out.println(problem);
+			return false;
+		}
+
+		new JarProcessorExecutor().runJarProcessor(options);
+		return true;
+	}
+
+	private boolean runDigestBuilder(Map params) {
+
+		List featureList = getFeatureList(params);
+
+		if ((featureList == null) || featureList.isEmpty()) {
+			System.out.println("no features to process"); //$NON-NLS-1$
+			return false;
+		}
+		Map perFeatureLocales = new HashMap();
+		Map availableLocales = getAvailableLocales(featureList,
+				perFeatureLocales);
+		try {
+			openInputStremas(availableLocales);
+		} catch (IOException e1) {
+			e1.printStackTrace();
+			System.out.println("Can not create file in output direcotry"); //$NON-NLS-1$
+			return false;
+		}
+
+		for(int i = 0; i < featureList.size(); i++) {
+
+			String featureJarFileName = (String) featureList.get(i);
+
+			if (featureJarFileName.endsWith("jar")) { //$NON-NLS-1$
+				System.out.println("Processing... " + featureJarFileName); //$NON-NLS-1$
+			} else {
+				System.out.println("Skipping... " + featureJarFileName); //$NON-NLS-1$
+				continue;
+			}
+
+			JarFile featureJar = null;
+			try {
+				featureJar = new JarFile(featureJarFileName);
+			} catch (IOException e) {
+				System.out.println("Problem with opening jar: " //$NON-NLS-1$
+						+ featureJarFileName);
+				e.printStackTrace();
+				return false;
+			}
+			FeatureModelFactory fmf = new FeatureModelFactory();
+
+			try {
+				ZipEntry featureXMLEntry = featureJar.getEntry("feature.xml"); //$NON-NLS-1$
+				Map featureProperties = loadProperties(featureJar,
+						featureJarFileName, perFeatureLocales);
+
+				FeatureModel featureModel = fmf.parseFeature(featureJar
+						.getInputStream(featureXMLEntry));
+
+				featureList = addFeaturesToList( (String) params.get(SITE_XML), featureList, featureModel.getFeatureIncluded(), availableLocales, perFeatureLocales);
+
+				Iterator availableLocalesIterator = availableLocales.values()
+				.iterator();
+				while (availableLocalesIterator.hasNext()) {
+					((AvailableLocale) availableLocalesIterator.next())
+					.writeFeatureDigests(featureModel,
+							featureProperties);
+				}
+
+			} catch (SAXException e) {
+				e.printStackTrace();
+				return false;
+			} catch (IOException e) {
+				e.printStackTrace();
+				return false;
+			} catch (CoreException e) {
+				e.printStackTrace();
+				return false;
+			}
+		}
+		Iterator availableLocalesIterator = availableLocales.values()
+		.iterator();
+		String outputDirectory = (String) params.get(DIGEST_OUTPUT_DIR);
+
+		outputDirectory = outputDirectory.substring(outputDirectory
+				.indexOf("=") + 1); //$NON-NLS-1$
+		if (!outputDirectory.endsWith(File.separator)) {
+			outputDirectory = outputDirectory + File.separator;
+		}
+		while (availableLocalesIterator.hasNext()) {
+			try {
+				((AvailableLocale) availableLocalesIterator.next())
+				.finishDigest(outputDirectory);
+			} catch (IOException e) {
+				System.out.println("Can not write in digest output directory: " //$NON-NLS-1$
+						+ outputDirectory);
+				e.printStackTrace();
+				return false;
+			}
+		}
+		System.out.println("Done"); //$NON-NLS-1$
+		return true;
+	}
+
+	private List addFeaturesToList( String siteXML, List featureList, IIncludedFeatureReference[] iIncludedFeatureReferences, Map availableLocales, Map perFeatureLocales ) throws CoreException {
+
+		String directoryName = (new File(siteXML)).getParent();
+		if (!directoryName.endsWith(File.separator)) {
+			directoryName = directoryName + File.separator;
+		}
+		directoryName = directoryName + "features" + File.separator; //$NON-NLS-1$
+
+		for (int i = 0; i < iIncludedFeatureReferences.length; i++) {
+			String featureURL = directoryName + iIncludedFeatureReferences[i].getVersionedIdentifier() + ".jar"; //$NON-NLS-1$
+			if (!(isFeatureAlreadyInList(featureList, featureURL))) {
+				try {
+					System.out.println("Extracting locales from included feature " + featureURL); //$NON-NLS-1$
+					processLocalesInJar(availableLocales, featureURL, perFeatureLocales, true);
+				} catch (IOException e) {
+					if (iIncludedFeatureReferences[i].isOptional()) 
+						continue;
+					System.out.println("Error while extracting locales from included feature " + featureURL);//$NON-NLS-1$	
+					e.printStackTrace();
+					throw new CoreException( new Status( IStatus.ERROR, UpdateCore.getPlugin().getBundle().getSymbolicName(), IStatus.OK, "Error while extracting locales from included feature " + featureURL, e)); //$NON-NLS-1$ 
+				}
+				featureList.add(featureURL);
+			}
+		}
+
+		return featureList;
+	}
+
+	private boolean isFeatureAlreadyInList(List featureList, String featureURL) {
+		for (int i = 0; i < featureList.size(); i++) {
+			String currentFeatureURL = (String)featureList.get(i);
+			if (currentFeatureURL.equals(featureURL)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private Map loadProperties(JarFile featureJar, String featureJarFileName,
+			Map perFeatureLocales) {
+		// System.out.println(
+		// ((List)perFeatureLocales.get(featureJarFileName)).size());
+		Iterator it = ((List) perFeatureLocales.get(featureJarFileName))
+		.iterator();
+		Map result = new HashMap();
+		while (it.hasNext()) {
+			String propertyFileName = (String) it.next();
+
+			ZipEntry featurePropertiesEntry = featureJar
+			.getEntry(propertyFileName);
+			Properties featureProperties = new Properties();
+			if (featurePropertiesEntry != null) {
+				try {
+					featureProperties.load(featureJar
+							.getInputStream(featurePropertiesEntry));
+					String localeString = null;
+					if (propertyFileName.endsWith("feature.properties")) { //$NON-NLS-1$
+						localeString = ""; //$NON-NLS-1$
+					} else {
+						localeString = propertyFileName.substring(8,
+								propertyFileName.indexOf('.'));
+					}
+					result.put(localeString, featureProperties);
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return result;
+	}
+
+	private void openInputStremas(Map availableLocales) throws IOException {
+		Iterator locales = availableLocales.values().iterator();
+		while (locales.hasNext()) {
+			AvailableLocale availableLocale = (AvailableLocale) locales.next();
+			availableLocale.openLocalizedOutputStream();
+		}
+	}
+
+	private Map getAvailableLocales(List featureList, Map perFeatureLocales) {
+		Iterator features = featureList.iterator();
+		Map locales = new HashMap();
+		while (features.hasNext()) {
+			String feature = (String) features.next();
+			try {
+				System.out.println("Extracting locales from " + feature); //$NON-NLS-1$
+				processLocalesInJar(locales, feature, perFeatureLocales, false);
+			} catch (IOException e) {
+				System.out.println("Error while extracting locales from " //$NON-NLS-1$
+						+ feature);
+				e.printStackTrace();
+				return null;
+			}
+		}
+		return locales;
+	}
+
+	private void processLocalesInJar(Map locales, String feature,
+			Map perFeatureLocales, boolean ignoreNewLocales) throws IOException {
+
+		JarFile jar = new JarFile(feature);
+		// System.out.println(feature);
+		Enumeration files = jar.entries();
+
+		List localesTemp = new ArrayList();
+		perFeatureLocales.put(feature, localesTemp);
+
+		while (files.hasMoreElements()) {
+			ZipEntry file = (ZipEntry) files.nextElement();
+			String localeString = null;
+			String name = file.getName();
+			// System.out.println("processLocalesInJar:"+name);
+			if (name.startsWith("feature") && name.endsWith(".properties")) { //$NON-NLS-1$ //$NON-NLS-2$
+				// System.out.println(name);
+				localesTemp.add(name);
+				// System.out.println(name);
+				if (name.endsWith("feature.properties")) { //$NON-NLS-1$
+					localeString = ""; //$NON-NLS-1$
+				} else {
+					localeString = name.substring(8, name.indexOf('.'));
+				}
+				// System.out.println(name +"::::\"" + localeString + "\"");
+				if ( !ignoreNewLocales && !locales.containsKey(localeString)) {
+					locales.put(localeString, new AvailableLocale(localeString));
+				}
+				if (locales.containsKey(localeString)) {
+					AvailableLocale currentLocale = (AvailableLocale) locales.get(localeString);
+					currentLocale.addFeatures(feature);
+				}
+			}
+		}
+
+	}
+
+	private List getFeatureList(Map params) {
+		if (params.containsKey(SITE_XML)
+				&& (fileExists((String) params.get(SITE_XML)))) {
+			return getFeatureListFromSiteXML((String) params.get(SITE_XML));
+		} else if (params.containsKey(INPUT)
+				&& isDirectory((String) params
+						.get(SiteOptimizerApplication.INPUT))) {
+			return getFeatureListFromDirectory((String) params.get(INPUT));
+		}
+		return null;
+	}
+
+	private boolean fileExists(String fileName) {
+		// System.out.println("fileExists:"+fileName);
+		File file = new File(fileName);
+		if ((file != null) && file.exists())
+			return true;
+		return false;
+	}
+
+	private List getFeatureListFromDirectory(String directoryName) {
+		List featuresURLs = new ArrayList();
+		File directory = new File(directoryName);
+		String[] featureJarFileNames = directory.list();
+		for (int i = 0; i < featureJarFileNames.length; i++) {
+			featuresURLs.add(directoryName + File.separator
+					+ featureJarFileNames[i]);
+		}
+		return featuresURLs;
+	}
+
+	private boolean isDirectory(String fileName) {
+
+		File directory = new File(fileName);
+		if ((directory != null) && directory.exists()
+				&& directory.isDirectory())
+			return true;
+		return false;
+	}
+
+	private List getFeatureListFromSiteXML(String siteXML) {
+
+		List featuresURLs = new ArrayList();
+		String directoryName = (new File(siteXML)).getParent();
+		if (!directoryName.endsWith(File.separator)) {
+			directoryName = directoryName + File.separator;
+		}
+
+		DefaultSiteParser siteParser = new DefaultSiteParser();
+		siteParser.init(new ExtendedSiteURLFactory());
+
+		try {
+			SiteModel site = siteParser.parse(new FileInputStream(siteXML));
+			if(site.getFeatureReferenceModels().length > 0) {
+				site.getFeatureReferenceModels()[0].getURLString();
+				FeatureReferenceModel[] featureReferenceModel = site
+				.getFeatureReferenceModels();
+				for (int i = 0; i < featureReferenceModel.length; i++) {
+					featuresURLs.add(directoryName
+							+ featureReferenceModel[i].getURLString());
+				}
+			}
+			return featuresURLs;
+		} catch (FileNotFoundException e) {
+			System.out.println("File not found: " + e.getMessage()); //$NON-NLS-1$
+			e.printStackTrace();
+		} catch (SAXException e) {
+			System.out.println("Parsing problem: " + e.getMessage()); //$NON-NLS-1$
+			e.printStackTrace();
+		} catch (IOException e) {
+			System.out.println("Problem while parsing: " + e.getMessage()); //$NON-NLS-1$
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.runtime.IPlatformRunnable#run(java.lang.Object)
+	 */
+	public Object run(Object args) throws Exception {
+		Platform.endSplash();
+		if (args == null)
+			return EXIT_ERROR;
+		if (args instanceof String[]) {
+			Map params = parseCmdLine((String[]) args);
+			if (params.containsKey(JAR_PROCESSOR)) {
+				if (!runJarProcessor(params))
+					return EXIT_ERROR;
+			}
+
+			if (params.containsKey(DIGEST_BUILDER)) {
+				if (!runDigestBuilder(params))
+					return EXIT_ERROR;
+			}
+		}
+		return IPlatformRunnable.EXIT_OK;
+	}
+
+	private class AvailableLocale {
+
+		private String PREFIX = "temp"; //$NON-NLS-1$
+
+		private String locale;
+
+		private Map /* VersionedIdentifier */features = new HashMap();
+
+		private PrintWriter localizedPrintWriter;
+
+		private File tempDigestDirectory;
+
+		public void finishDigest(String outputDirectory) throws IOException {
+			localizedPrintWriter.println("</digest>"); //$NON-NLS-1$
+			if (localizedPrintWriter != null) {
+				localizedPrintWriter.close();
+			}
+
+			File digest = new File(outputDirectory + File.separator + "digest" //$NON-NLS-1$
+					+ (locale == null || locale.equals("") ? "" : "_"+locale) + ".zip"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			System.out.println(digest.getAbsolutePath());
+			System.out.println(digest.getName());
+			if (digest.exists()) {
+				digest.delete();
+			}
+			digest.createNewFile();
+			OutputStream os = new FileOutputStream(digest);
+			JarOutputStream jos = new JarOutputStream(os);
+			jos.putNextEntry(new ZipEntry("digest.xml")); //$NON-NLS-1$
+			InputStream is = new FileInputStream(tempDigestDirectory);
+			byte[] b = new byte[4096];
+			int bytesRead = 0;
+			do {
+				bytesRead = is.read(b);
+				if (bytesRead > 0) {
+					jos.write(b, 0, bytesRead);
+				}
+			} while (bytesRead > 0);
+
+			jos.closeEntry();
+			jos.close();
+			os.close();
+			is.close();
+			tempDigestDirectory.delete();
+
+		}
+
+		public AvailableLocale(String locale) {
+			this.locale = locale;
+		}
+
+		public void addFeatures(String feature) {
+			features.put(feature, feature);
+		}
+
+		public void openLocalizedOutputStream() throws IOException {
+			tempDigestDirectory = File.createTempFile(PREFIX, null);
+			FileOutputStream fstream = new FileOutputStream(tempDigestDirectory);
+			localizedPrintWriter = new PrintWriter(new OutputStreamWriter(fstream, "UTF-8")); //$NON-NLS-1$
+			localizedPrintWriter
+			.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <digest>"); //$NON-NLS-1$
+			tempDigestDirectory.deleteOnExit();
+		}
+
+		public int hashCode() {
+			return locale.hashCode();
+		}
+
+		public boolean equals(Object obj) {
+
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			final AvailableLocale other = (AvailableLocale) obj;
+			if (locale == null) {
+				if (other.locale != null)
+					return false;
+			} else if (!locale.equals(other.locale))
+				return false;
+			return true;
+		}
+
+		public void writeFeatureDigests(FeatureModel featureModel,
+				Map featureProperties) {
+
+			if (this.locale.equals("")) { //$NON-NLS-1$
+				writeFeatureDigest(localizedPrintWriter, featureModel,
+						(Properties) featureProperties.get("")); //$NON-NLS-1$
+				return;
+			}
+			Properties temp = new Properties();
+			if (locale.indexOf("_") < 0) { //$NON-NLS-1$
+				temp = combineProperties(
+						(Properties) featureProperties.get(""), //$NON-NLS-1$
+						(Properties) featureProperties.get(locale), null);
+				writeFeatureDigest(localizedPrintWriter, featureModel, temp);
+			} else {
+				temp = combineProperties(
+						(Properties) featureProperties.get(""), //$NON-NLS-1$
+						(Properties) featureProperties.get(locale.substring(0, locale.indexOf("_"))), //$NON-NLS-1$
+						(Properties) featureProperties.get(locale) );
+				writeFeatureDigest(localizedPrintWriter, featureModel, temp);
+			}
+
+		}
+		
+		/**
+		 * @param defaults	Should be lang_country
+		 * @param secondary	Should be lang
+		 * @param preferred	Should be default
+		 * @return
+		 */
+
+		private Properties combineProperties(Properties defaults,
+				Properties secondary, Properties preferred) {
+			return new CombinedProperties(preferred, secondary, defaults);
+
+		}
+
+	}
+
+	public static void writeFeatureDigest(PrintWriter digest,
+			FeatureModel featureModel, Properties featureProperties) {
+
+		String label = null;
+		String provider = null;
+		String description = null;
+		String license = null;
+		String copyright = null;
+
+		if ((featureProperties != null)
+				&& featureModel.getLabel().startsWith("%")) { //$NON-NLS-1$
+			label = featureProperties.getProperty(featureModel.getLabel()
+					.substring(1));
+		} else {
+			label = featureModel.getLabel();
+		}
+		if ((featureProperties != null)
+				&& (featureModel.getDescriptionModel() != null)
+				&& (featureModel.getDescriptionModel().getAnnotation() != null)
+				&& featureModel.getDescriptionModel().getAnnotation()
+				.startsWith("%")) { //$NON-NLS-1$
+			// System.out.println(featureProperties.getProperty(featureModel.getDescriptionModel().getAnnotation().substring(1)));
+			description = featureProperties.getProperty(featureModel
+					.getDescriptionModel().getAnnotation().substring(1));
+		} else {
+			URLEntryModel descriptionModel = featureModel.getDescriptionModel();
+			if( descriptionModel == null )
+				description = "";
+			else
+				description = descriptionModel.getAnnotation();
+		}
+		String pvalue = featureModel.getProvider();
+		if ((featureProperties != null)
+				&& pvalue!=null && pvalue.startsWith("%")) { //$NON-NLS-1$
+			provider = featureProperties.getProperty(featureModel.getProvider()
+					.substring(1));
+		} else {
+			provider = pvalue;
+		}
+		if (provider==null) provider = "";
+
+		if (((featureProperties != null) && featureModel.getCopyrightModel() != null)
+				&& featureModel.getCopyrightModel().getAnnotation().startsWith(
+				"%")) { //$NON-NLS-1$
+			copyright = featureProperties.getProperty(featureModel
+					.getCopyrightModel().getAnnotation().substring(1));
+		} else {
+			if (featureModel.getCopyrightModel() != null) {
+				copyright = featureModel.getCopyrightModel().getAnnotation();
+			} else {
+				copyright = null;
+			}
+		}
+
+		if ((featureProperties != null)
+				&& (featureModel.getLicenseModel() != null)
+				&& featureModel.getLicenseModel().getAnnotation().startsWith(
+				"%")) { //$NON-NLS-1$
+			license = featureProperties.getProperty(featureModel
+					.getLicenseModel().getAnnotation().substring(1));
+		} else {
+			license = featureModel.getLicenseModel().getAnnotation();
+		}
+
+		digest.print("<feature "); //$NON-NLS-1$
+		digest.print("label=\"" + getUTF8String(label) + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+		digest.print("provider-name=\"" + getUTF8String(provider) + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+		digest.print("id=\"" + featureModel.getFeatureIdentifier() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+		digest.print("version=\"" + featureModel.getFeatureVersion() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+		if (featureModel.getOS() != null)
+			digest.print("os=\"" + featureModel.getOS() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+		if (featureModel.getNL() != null)
+			digest.print("nl=\"" + featureModel.getNL() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+		if (featureModel.getWS() != null)
+			digest.print("ws=\"" + featureModel.getWS() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+		if (featureModel.getOSArch() != null)
+			digest.print("arch=\"" + featureModel.getOSArch() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+		if (featureModel.isExclusive())
+			digest.print("exclusive=\"" + featureModel.isExclusive() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+
+		if (((featureModel.getImportModels() == null) || (featureModel
+				.getImportModels().length == 0))
+				&& ((featureModel.getDescriptionModel() == null)
+						|| (featureModel.getDescriptionModel().getAnnotation() == null) || (featureModel
+								.getDescriptionModel().getAnnotation().trim().length() == 0))
+								&& ((featureModel.getCopyrightModel() == null)
+										|| (featureModel.getCopyrightModel().getAnnotation() == null) || (featureModel
+												.getCopyrightModel().getAnnotation().trim().length() == 0))
+												&& ((featureModel.getLicenseModel() == null)
+														|| (featureModel.getLicenseModel().getAnnotation() == null) || (featureModel
+																.getLicenseModel().getAnnotation().trim().length() == 0)) 
+																&& ((featureModel.getFeatureIncluded() == null) || (featureModel
+																		.getFeatureIncluded().length == 0))){
+			digest.println("/> "); //$NON-NLS-1$
+		} else {
+			digest.println("> "); //$NON-NLS-1$
+			if (featureModel.getImportModels().length > 0) {
+
+				digest.println("\t<requires> "); //$NON-NLS-1$
+				ImportModel[] imports = featureModel.getImportModels();
+				for (int j = 0; j < imports.length; j++) {
+					digest.print("\t\t<import "); //$NON-NLS-1$
+					if (imports[j].isFeatureImport()) {
+						digest.print("feature=\""); //$NON-NLS-1$
+					} else {
+						digest.print("plugin=\""); //$NON-NLS-1$
+					}
+					digest.print(imports[j].getIdentifier() + "\" "); //$NON-NLS-1$
+					digest.print("version=\""); //$NON-NLS-1$
+					digest.print(imports[j].getVersion() + "\" "); //$NON-NLS-1$
+					digest.print("match=\""); //$NON-NLS-1$
+					digest.print(imports[j].getMatchingRuleName() + "\" "); //$NON-NLS-1$
+					if (imports[j].isPatch()) {
+						digest.print("patch=\"true\" "); //$NON-NLS-1$
+					}
+					digest.println(" />"); //$NON-NLS-1$
+				}
+
+				digest.println("\t</requires>"); //$NON-NLS-1$
+
+			}
+
+			if ((featureModel.getDescriptionModel() != null)
+					&& (featureModel.getDescriptionModel().getAnnotation() != null)
+					&& (featureModel.getDescriptionModel().getAnnotation()
+							.trim().length() != 0)) {
+				digest.println("\t<description>"); //$NON-NLS-1$
+				digest.println("\t\t" + UpdateManagerUtils.getWritableXMLString(description)); //$NON-NLS-1$
+				digest.println("\t</description>"); //$NON-NLS-1$
+			}
+
+			if (featureModel.getCopyrightModel() != null) {
+				if (featureModel.getCopyrightModel().getAnnotation() != null) {
+					// if
+					// (featureModel.getDescriptionModel().getAnnotation().length()
+					// != 0) {
+					digest.println("\t<copyright>"); //$NON-NLS-1$
+					digest.println("\t\t" + UpdateManagerUtils.getWritableXMLString(copyright)); //$NON-NLS-1$
+					digest.println("\t</copyright>"); //$NON-NLS-1$
+					// }
+				}
+			}
+
+			if ((featureModel.getLicenseModel() != null)
+					&& (featureModel.getLicenseModel().getAnnotation() != null)
+					&& (featureModel.getLicenseModel().getAnnotation()
+							.trim().length() != 0)) {
+				digest.println("\t<license>"); //$NON-NLS-1$
+				digest.println("\t\t" + UpdateManagerUtils.getWritableXMLString(license)); //$NON-NLS-1$
+				digest.println("\t</license>"); //$NON-NLS-1$
+			}
+
+			PluginEntryModel[] plugins = featureModel.getPluginEntryModels();
+			if ((plugins != null) && (plugins.length != 0)) {
+				for (int i = 0; i < plugins.length; i++) {
+					digest.print("\t<plugin "); //$NON-NLS-1$
+					digest.print("id=\"" + plugins[i].getPluginIdentifier() //$NON-NLS-1$
+							+ "\" "); //$NON-NLS-1$
+					digest.print("version=\"" + plugins[i].getPluginVersion() //$NON-NLS-1$
+							+ "\" "); //$NON-NLS-1$
+					if (plugins[i].getOS() != null)
+						digest.print("os=\"" + plugins[i].getOS() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+					if (plugins[i].getNL() != null)
+						digest.print("nl=\"" + plugins[i].getNL() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+					if (plugins[i].getWS() != null)
+						digest.print("ws=\"" + plugins[i].getWS() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+					if (plugins[i].getOSArch() != null)
+						digest
+						.print("arch=\"" + plugins[i].getOSArch() //$NON-NLS-1$
+								+ "\" "); //$NON-NLS-1$
+					if (plugins[i].getDownloadSize() > 0)
+						digest.print("download-size=\"" //$NON-NLS-1$
+								+ plugins[i].getDownloadSize() + "\" "); //$NON-NLS-1$
+					if (plugins[i].getInstallSize() > 0)
+						digest.print("install-size=\"" //$NON-NLS-1$
+								+ plugins[i].getInstallSize() + "\" "); //$NON-NLS-1$
+					if (!plugins[i].isUnpack())
+						digest.print("unpack=\"" + plugins[i].isUnpack() //$NON-NLS-1$
+								+ "\" "); //$NON-NLS-1$
+
+					digest.println("/> "); //$NON-NLS-1$
+				}
+			}	
+
+			IIncludedFeatureReference[] includedFeatures = featureModel.getFeatureIncluded();
+
+			if ((includedFeatures != null) && (includedFeatures.length != 0)) {
+				for (int i = 0; i < includedFeatures.length; i++) {
+					try {
+						digest.print("\t<includes "); //$NON-NLS-1$
+
+						digest.print("id=\"" + includedFeatures[i].getVersionedIdentifier().getIdentifier() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+						digest.print("version=\"" + includedFeatures[i].getVersionedIdentifier().getVersion() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+						if (includedFeatures[i].getOS() != null)
+							digest.print("os=\"" + includedFeatures[i].getOS() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+						if (includedFeatures[i].getNL() != null)
+							digest.print("nl=\"" + includedFeatures[i].getNL() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+						if (includedFeatures[i].getWS() != null)
+							digest.print("ws=\"" + includedFeatures[i].getWS() + "\" ");  //$NON-NLS-1$//$NON-NLS-2$
+						if (includedFeatures[i].getOSArch() != null)
+							digest.print("arch=\"" + includedFeatures[i].getOSArch() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$											
+						if ( (includedFeatures[i] instanceof IncludedFeatureReference) && (((IncludedFeatureReference)includedFeatures[i]).getLabel() != null))
+							digest.print("name=\"" + includedFeatures[i].getName() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$									
+						if (includedFeatures[i].isOptional())
+							digest.print("optional=\"true\" "); //$NON-NLS-1$
+						digest.print("search-location=\"" + includedFeatures[i].getSearchLocation() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
+
+						digest.println("/> "); //$NON-NLS-1$
+					} catch (CoreException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					}
+				}
+			}
+			digest.println("</feature>"); //$NON-NLS-1$
+		}
+	}
+
+	private class CombinedProperties extends Properties {
+
+		private Properties properties1;
+
+		private Properties properties2;
+
+		private Properties properties3;
+
+		/**
+		 * @param preferred preferred, such as lang_country
+		 * @param secondary secondary, such as lang
+		 * @param defaults default,
+		 */
+		public CombinedProperties(Properties preferred,
+				Properties secondary, Properties defaults) {
+			this.properties1 = preferred;
+			this.properties2 = secondary;
+			this.properties3 = defaults;
+		}
+
+		/**
+		 * 
+		 */
+		private static final long serialVersionUID = 1L;
+
+		public String getProperty(String key) {
+			String result = null;
+			if (properties3 != null && properties3.containsKey(key))
+				result = properties3.getProperty(key);
+			if (properties2 != null && properties2.containsKey(key))
+				result = properties2.getProperty(key);
+			if (properties1 != null && properties1.containsKey(key))
+				result = properties1.getProperty(key);
+			return result;
+		}
+
+	}
+	
+	public static final String getUTF8String(String s) {
+		if(s == null)
+			return "";
+		return UpdateManagerUtils.getWritableXMLString(s);
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/BaseSearchCategory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/BaseSearchCategory.java
new file mode 100644
index 0000000..0ee1501
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/BaseSearchCategory.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.search;
+
+import org.eclipse.update.search.*;
+
+public abstract class BaseSearchCategory implements IUpdateSearchCategory {
+	private String id;
+	
+	protected BaseSearchCategory(String id) {
+		setId(id);
+	}
+	
+	public String getId() {
+		return id;
+	}
+	
+	public void setId(String id) {
+		if (this.id==null)
+			this.id = id;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/OptionalFeatureSearchCategory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/OptionalFeatureSearchCategory.java
new file mode 100644
index 0000000..fa463d0
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/OptionalFeatureSearchCategory.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.search;
+
+import java.net.*;
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.search.*;
+
+/**
+ * Searches for optional features
+ */
+public class OptionalFeatureSearchCategory extends BaseSearchCategory {
+	private IUpdateSearchQuery[] queries;
+	private ArrayList vids;
+	private static final String CATEGORY_ID =
+		"org.eclipse.update.core.unified-search"; //$NON-NLS-1$
+
+	private class OptionalQuery implements IUpdateSearchQuery {
+		public void run(
+			ISite site,
+			String[] categoriesToSkip,
+			IUpdateSearchFilter filter,
+			IUpdateSearchResultCollector collector,
+			IProgressMonitor monitor) {
+
+			monitor.beginTask("", vids.size()); //$NON-NLS-1$
+			for (int i = 0; i < vids.size(); i++) {
+				VersionedIdentifier vid = (VersionedIdentifier) vids.get(i);
+				monitor.subTask(vid.toString());
+				IFeature feature =
+					createFeature(
+						site,
+						vid,
+						new SubProgressMonitor(monitor, 1));
+				if (feature!=null && filter.accept(feature))
+					collector.accept(feature);
+			}
+		}
+
+		private IFeature createFeature(
+			ISite site,
+			VersionedIdentifier vid,
+			IProgressMonitor monitor) {
+			try {
+				URL siteURL = site.getURL();
+				//TODO This assumption stands only in the default case
+				// In general, feature archive URL may be mapped on site.
+				// Also, feature type may be something else (not packaged).
+				// We may need additional information (not only id and version)
+				// in order to create a feature on a site.
+				String relative = vid.toString();
+				URL featureURL = new URL(siteURL, "features/" + relative+".jar"); //$NON-NLS-1$ //$NON-NLS-2$
+				return site.createFeature(
+					"org.eclipse.update.core.packaged", //$NON-NLS-1$
+					featureURL,
+					monitor);
+			} catch (Exception e) {
+				return null;
+			}
+		}
+
+		/* (non-Javadoc)
+		 * @see org.eclipse.update.internal.ui.search.ISearchQuery#getSearchSite()
+		 */
+		public IQueryUpdateSiteAdapter getQuerySearchSite() {
+			return null;
+		}
+	}
+
+	public void addVersionedIdentifier(VersionedIdentifier vid) {
+		vids.add(vid);
+	}
+
+	public void clear() {
+		vids.clear();
+	}
+
+	public OptionalFeatureSearchCategory() {
+		super(CATEGORY_ID);
+		vids = new ArrayList();
+		queries = new IUpdateSearchQuery[] { new OptionalQuery()};
+	}
+
+	public IUpdateSearchQuery[] getQueries() {
+		return queries;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/QueryUpdateSiteAdapter.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/QueryUpdateSiteAdapter.java
new file mode 100644
index 0000000..e30d391
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/QueryUpdateSiteAdapter.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.search;
+
+import java.net.*;
+
+import org.eclipse.update.search.*;
+
+public class QueryUpdateSiteAdapter extends UpdateSiteAdapter implements IQueryUpdateSiteAdapter {
+	private String mappingId;
+
+	public QueryUpdateSiteAdapter(String label, URL url, String mappingId) {
+		super(label, url);
+		this.mappingId = mappingId;
+	}
+	
+	public String getMappingId() {
+		return mappingId;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/SiteSearchCategory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/SiteSearchCategory.java
new file mode 100644
index 0000000..78bd7af
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/SiteSearchCategory.java
@@ -0,0 +1,249 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2009 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
+ *     James D Miles (IBM Corp.) - bug 181375, ArrayIndexOutOfBoundsException in SiteSearchCategory$Query
+ *     James D Miles (IBM Corp.) - bug 191783, NullPointerException in FeatureDownloader
+ *******************************************************************************/
+package org.eclipse.update.internal.search;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.update.core.ICategory;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFeatureReference;
+import org.eclipse.update.internal.core.ExtendedSite;
+import org.eclipse.update.internal.core.LiteFeature;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.search.IQueryUpdateSiteAdapter;
+import org.eclipse.update.search.IUpdateSearchFilter;
+import org.eclipse.update.search.IUpdateSearchQuery;
+import org.eclipse.update.search.IUpdateSearchResultCollector;
+
+/**
+ * Searches an update site
+ */
+public class SiteSearchCategory extends BaseSearchCategory {
+	private IUpdateSearchQuery[] queries;
+	private boolean liteFeaturesAreOK;
+	private static final String CATEGORY_ID =
+		"org.eclipse.update.core.unified-search"; //$NON-NLS-1$
+
+	private static class Query implements IUpdateSearchQuery {
+		
+		private boolean liteFeaturesAreOK;
+		
+		
+		public Query() {
+			liteFeaturesAreOK = false;
+		}
+		
+		public Query(boolean liteFeaturesAreOK) {
+			this.liteFeaturesAreOK = liteFeaturesAreOK;
+		}
+		
+		public void setLiteFeaturesAreOK(boolean liteFeaturesAreOK) {
+			this.liteFeaturesAreOK = liteFeaturesAreOK;
+		}
+		
+		public void run(
+			ISite site,
+			String[] categoriesToSkip,
+			IUpdateSearchFilter filter,
+			IUpdateSearchResultCollector collector,
+			IProgressMonitor monitor) {
+			
+			ISiteFeatureReference[] refs = site.getFeatureReferences();
+			HashSet ignores = new HashSet();			
+			Map liteFeatures = new HashMap();
+			
+			if (categoriesToSkip != null) {
+				for (int i = 0; i < categoriesToSkip.length; i++) {
+					ignores.add(categoriesToSkip[i]);
+				}
+			}
+			List siteFeatureReferences = new ArrayList(Arrays.asList(refs));
+			
+			if (liteFeaturesAreOK && (site instanceof ExtendedSite) ) {
+				
+				ExtendedSite extendedSite = (ExtendedSite)site;
+				LiteFeature[] liteFeaturesArray =  extendedSite.getLiteFeatures();
+				if ( (liteFeaturesArray != null) && ( liteFeaturesArray.length != 0)) {
+					for(int i = 0; i < liteFeaturesArray.length; i++) {
+						liteFeatures.put(liteFeaturesArray[i].getVersionedIdentifier(), liteFeaturesArray[i]);					
+					}
+					(new FeatureDownloader(siteFeatureReferences, collector, filter, ignores, monitor, true, liteFeatures)).run();
+					return;
+				} else {
+					liteFeaturesAreOK = false;
+				}
+			}
+			
+
+			
+			monitor.beginTask("", refs.length); //$NON-NLS-1$
+			ThreadGroup featureDownloaders = new ThreadGroup("FeatureDownloader"); //$NON-NLS-1$
+			int numberOfThreads = (refs.length > 5)? 5: refs.length;
+
+			Thread[] featureDownloader = new Thread[numberOfThreads];
+			for( int i = 0; i < numberOfThreads; i++) {
+				featureDownloader[i] = new Thread(featureDownloaders, new FeatureDownloader(siteFeatureReferences, collector, filter, ignores, monitor));
+				featureDownloader[i].start();
+			}
+			
+			int i =0;
+			while(i < numberOfThreads){
+				if (monitor.isCanceled()) {
+					synchronized(siteFeatureReferences) { 
+						siteFeatureReferences.clear();
+					}
+				}
+				try	{
+					featureDownloader[i].join(250);
+					if(!featureDownloader[i].isAlive()){
+						i++;
+					}
+				} catch (InterruptedException ie) {
+				}
+			}
+		}
+
+		/* (non-Javadoc)
+		 * @see org.eclipse.update.internal.ui.search.ISearchQuery#getSearchSite()
+		 */
+		public IQueryUpdateSiteAdapter getQuerySearchSite() {
+			return null;
+		}
+	}
+
+	public SiteSearchCategory() {
+		super(CATEGORY_ID);
+		queries = new IUpdateSearchQuery[] { new Query()};
+	}
+
+	public SiteSearchCategory(boolean liteFeaturesAreOK) {
+		this();
+		this.liteFeaturesAreOK = liteFeaturesAreOK;
+		queries = new IUpdateSearchQuery[] { new Query(liteFeaturesAreOK)};
+	}
+
+	public IUpdateSearchQuery[] getQueries() {
+		return queries;
+	}
+	
+	
+	private static class FeatureDownloader implements Runnable {
+		
+		private List siteFeatureReferences;
+		
+		private IProgressMonitor monitor;
+		
+		private IUpdateSearchFilter filter;
+		
+		private IUpdateSearchResultCollector collector;
+		
+		private HashSet ignores;
+
+		private boolean liteFeaturesAreOK;
+
+		private Map liteFeatures;
+
+		public FeatureDownloader(List siteFeatureReferences, IUpdateSearchResultCollector collector, IUpdateSearchFilter filter, HashSet ignores, IProgressMonitor monitor) {
+			super();
+
+			this.collector = collector;
+			this.filter = filter;
+			this.ignores = ignores;
+			this.monitor = monitor;
+			this.siteFeatureReferences = siteFeatureReferences;
+		}
+		
+		public FeatureDownloader(List siteFeatureReferences, IUpdateSearchResultCollector collector, IUpdateSearchFilter filter, HashSet ignores, IProgressMonitor monitor, boolean liteFeaturesAreOK, Map liteFeatures) {
+			this(siteFeatureReferences, collector, filter, ignores, monitor);
+			this.liteFeaturesAreOK = liteFeaturesAreOK && (liteFeatures != null);
+			this.liteFeatures = liteFeatures;
+		}
+
+		public void run() {
+			
+			ISiteFeatureReference siteFeatureReference = null;
+			
+			while (siteFeatureReferences.size() != 0) {
+				
+				synchronized(siteFeatureReferences) { 
+					try{
+						siteFeatureReference = (ISiteFeatureReference)siteFeatureReferences.remove(0);
+					}catch(IndexOutOfBoundsException e){
+						siteFeatureReference = null;
+						break;
+					}
+				}
+				if (siteFeatureReference != null) {
+					boolean skipFeature = false;
+					if (monitor.isCanceled())
+						break;
+					if (ignores.size() > 0) {
+						ICategory[] categories = siteFeatureReference.getCategories();
+						
+						for (int j = 0; j < categories.length; j++) {
+							ICategory category = categories[j];
+							if (ignores.contains(category.getName())) {
+								skipFeature = true;
+								break;
+							}
+						}
+					}
+					try {
+						if (!skipFeature) {
+							if (filter.accept(siteFeatureReference)) {
+								IFeature feature = null;
+								if(liteFeaturesAreOK) {
+									feature = (IFeature)liteFeatures.get(siteFeatureReference.getVersionedIdentifier());
+								}
+								if (feature == null){
+									feature = siteFeatureReference.getFeature(null);
+								}
+								synchronized(siteFeatureReferences) {
+									if ( (feature != null) && (filter.accept(siteFeatureReference)) ) {								
+										collector.accept(feature);							    
+										monitor.subTask(feature.getLabel());
+									}
+								}
+							}
+						}
+					} catch (CoreException e) {
+						UpdateCore.log(e);
+					} finally {
+						monitor.worked(1);
+					}
+				}
+			}
+			
+		}
+	}
+
+	public boolean isLiteFeaturesAreOK() {
+		return liteFeaturesAreOK;
+	}
+
+	public void setLiteFeaturesAreOK(boolean liteFeaturesAreOK) {
+		this.liteFeaturesAreOK = liteFeaturesAreOK;
+		for( int i = 0; i < queries.length; i++) {
+			((Query)queries[i]).setLiteFeaturesAreOK(liteFeaturesAreOK);
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdatePolicy.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdatePolicy.java
new file mode 100644
index 0000000..6e62a86
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdatePolicy.java
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ *     James D Miles (IBM Corp.) - bug 191368, Policy URL doesn't support UTF-8 characters
+ *******************************************************************************/
+package org.eclipse.update.internal.search;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.URLEncoder;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+import org.eclipse.update.internal.core.connection.IResponse;
+import org.eclipse.update.search.IUpdateSiteAdapter;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * 
+ * This class opens connection to the update map resource
+ * and parses the file to load update URL mappings.
+ * These mappings are used to redirect new updates search
+ * when the redirection pattern matches.
+ */
+
+public class UpdatePolicy {
+	private static final String TAG_POLICY = "update-policy"; //$NON-NLS-1$
+	private static final String TAG_URL_MAP = "url-map"; //$NON-NLS-1$
+	private static final String ATT_URL = "url"; //$NON-NLS-1$
+	private static final String ATT_PATTERN = "pattern"; //$NON-NLS-1$
+	private static final String ATT_TYPE = "url-type"; //$NON-NLS-1$
+	private static final String ATT_TYPE_VALUE_UPDATE = "update"; //$NON-NLS-1$
+	//private static final String ATT_TYPE_VALUE_BOTH = "both"; //$NON-NLS-1$
+	private static final String ATT_TYPE_VALUE_DISCOVERY = "discovery"; //$NON-NLS-1$
+
+	private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+
+	private static class MapSite implements IUpdateSiteAdapter {
+		private URL url;
+		public MapSite(URL url) {
+			this.url = url;
+		}
+		public String getLabel() {
+			if (url == null) {
+				return ""; //$NON-NLS-1$
+			}
+			return url.toString();
+		}
+		public URL getURL() {
+			return url;
+		}
+	}
+
+	private static class UpdateMapEntry {
+		private IUpdateSiteAdapter site;
+		private String pattern;
+
+		public UpdateMapEntry(String pattern, URL url) {
+			this.pattern = pattern;
+			this.site = new MapSite(url);
+		}
+		public IUpdateSiteAdapter getSite() {
+			return site;
+		}
+		public boolean matches(String id) {
+			return id.startsWith(pattern);
+		}
+		public String getPattern() {
+			return pattern;
+		}
+	}
+
+	private ArrayList entries;
+	private ArrayList discoveryEntries;
+	private IUpdateSiteAdapter defaultSite;
+	private IUpdateSiteAdapter defaultDiscoverySite;
+	private boolean loaded = false;
+	private boolean fallbackAllowed = true;
+
+	public UpdatePolicy() {
+		entries = new ArrayList();
+		discoveryEntries = new ArrayList();
+	}
+
+	public void load(URL mapFile, IProgressMonitor monitor)
+		throws CoreException {
+		InputStream policyStream = null;
+		try {
+			IResponse response = ConnectionFactory.get(mapFile);
+			UpdateManagerUtils.checkConnectionResult(response, mapFile);
+			policyStream = response.getInputStream(monitor);
+			// the stream can be null if the user cancels the connection
+			if (policyStream == null)
+				return;
+			
+			documentBuilderFactory.setNamespaceAware(true);
+			DocumentBuilder parser = documentBuilderFactory.newDocumentBuilder();
+			Document doc = parser.parse(new InputSource(policyStream));
+
+			processUpdatePolicy(doc);
+			loaded = true;
+		} catch (IOException e) {
+			throw Utilities.newCoreException(
+				NLS.bind(Messages.SiteURLFactory_UnableToAccessSiteStream, (new String[] { mapFile == null ? "" : mapFile.toExternalForm() })), //$NON-NLS-1$
+				ISite.SITE_ACCESS_EXCEPTION,
+				e);
+		} catch (SAXException e) {
+			throw Utilities.newCoreException(
+				Messages.UpdatePolicy_parsePolicy, 
+				0,
+				e);
+
+		} catch(ParserConfigurationException e) {
+			throw Utilities.newCoreException(
+				Messages.UpdatePolicy_parsePolicy, 
+				0,
+				e);
+		} finally {
+			if (policyStream != null) {
+				try {
+					policyStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+	}
+
+	public boolean isLoaded() {
+		return loaded;
+	}
+	
+	/*
+	 * Given the feature ID, returns the mapped update URL if
+	 * found in the mappings. This URL will be used INSTEAD of
+	 * the update URL encoded in the feature itself during the
+	 * new update search.
+	 * <p>In case of multiple matches (e.g. org.eclipse and org.eclipse.platform)
+	 * the URL for the longer pattern will be picked (i.e. org.eclipse.platform).
+	 */
+	public IUpdateSiteAdapter getMappedSite(String id) {
+		UpdateMapEntry lastEntry = null;
+		for (int i = 0; i < entries.size(); i++) {
+			UpdateMapEntry entry = (UpdateMapEntry) entries.get(i);
+			if (entry.matches(id)) {
+				if (lastEntry == null)
+					lastEntry = entry;
+				else {
+					// Choose the match with longer pattern.
+					// For example, if two matches are found:
+					// 'org.eclipse' and 'org.eclipse.platform',
+					// pick 'org.eclipse.platform'.
+					String pattern = entry.getPattern();
+					String lastPattern = lastEntry.getPattern();
+					if (pattern.length() > lastPattern.length())
+						lastEntry = entry;
+				}
+			}
+		}
+		if (lastEntry != null)
+			return lastEntry.getSite();
+		else
+			return defaultSite;
+	}
+	
+	/*
+	 * Given the feature ID, returns the mapped discovery URL if
+	 * found in the mappings. This URL will be used INSTEAD of
+	 * the discovery URL encoded in the feature itself during the
+	 * new search.
+	 * <p>In case of multiple matches (e.g. org.eclipse and org.eclipse.platform)
+	 * the URL for the longer pattern will be picked (i.e. org.eclipse.platform).
+	 */
+	public IUpdateSiteAdapter getMappedDiscoverySite(String id) {
+		UpdateMapEntry lastEntry = null;
+		for (int i = 0; i < discoveryEntries.size(); i++) {
+			UpdateMapEntry entry = (UpdateMapEntry) discoveryEntries.get(i);
+			if (entry.matches(id)) {
+				if (lastEntry == null)
+					lastEntry = entry;
+				else {
+					// Choose the match with longer pattern.
+					// For example, if two matches are found:
+					// 'org.eclipse' and 'org.eclipse.platform',
+					// pick 'org.eclipse.platform'.
+					String pattern = entry.getPattern();
+					String lastPattern = lastEntry.getPattern();
+					if (pattern.length() > lastPattern.length())
+						lastEntry = entry;
+				}
+			}
+		}
+		if (lastEntry != null)
+			return lastEntry.getSite();
+		else
+			return defaultDiscoverySite;
+	}
+
+	public boolean isFallbackAllowed() {
+		return fallbackAllowed;
+	}
+	
+	private void reset() {
+		if (!entries.isEmpty())
+			entries.clear();
+		if (!discoveryEntries.isEmpty())
+			discoveryEntries.clear();
+	}
+
+	private void processUpdatePolicy(Document document) throws CoreException {
+		Node root = document.getDocumentElement();
+		reset();
+		
+		if (root.getNodeName().equals(TAG_POLICY)==false)
+			throwCoreException("'"+TAG_POLICY+Messages.UpdatePolicy_policyExpected, null);  //$NON-NLS-1$
+				
+		NodeList nodes = root.getChildNodes();
+		
+		for (int i=0; i<nodes.getLength(); i++) {
+			Node child = nodes.item(i);
+			if (child.getNodeType() != Node.ELEMENT_NODE)
+				continue;
+			String tag = child.getNodeName();
+			if (tag.equals(TAG_URL_MAP))
+				processMapNode(child);
+		}
+	}
+	private void processMapNode(Node node) throws CoreException {
+		String pattern = getAttribute(node, ATT_PATTERN);
+		String urlName = getAttribute(node, ATT_URL);
+		String type = getAttribute(node, ATT_TYPE);
+		
+		assertNotNull(ATT_PATTERN, pattern);
+		assertNotNull(ATT_URL, urlName);
+		
+		// empty url means feature is not updateable
+		if (urlName.trim().length() == 0) {
+			addUpdateEntry(pattern, null, type);
+			return;
+		}
+
+		try {
+			URL url = new URL(urlName);
+			URL resolvedURL = URLEncoder.encode(url);
+			addUpdateEntry(pattern, resolvedURL, type);
+		} catch (MalformedURLException e) {
+			throwCoreException(Messages.UpdatePolicy_invalidURL+urlName, null); 
+		} 
+	}
+	
+	private void assertNotNull(String name, String value) throws CoreException {
+		if (value==null)
+			throwCoreException(name+Messages.UpdatePolicy_nameNoNull, null); 
+	}
+	
+	private String getAttribute(Node node, String name) {
+		NamedNodeMap attMap = node.getAttributes();
+		Node att = attMap.getNamedItem(name);
+		if (att==null) return null;
+		return att.getNodeValue();
+	}
+
+	private void addUpdateEntry(String pattern, URL url, String type) {
+		if (pattern.equalsIgnoreCase("*")) {//$NON-NLS-1$
+			if (type == null)
+				defaultSite = new MapSite(url);
+			else if (type.equals(ATT_TYPE_VALUE_UPDATE))
+				defaultSite = new MapSite(url);
+			else if (type.equals(ATT_TYPE_VALUE_DISCOVERY))
+				defaultDiscoverySite = new MapSite(url);
+			else {
+				defaultSite = new MapSite(url);
+				defaultDiscoverySite = new MapSite(url);
+			}
+		} else {
+			if (type == null )
+				entries.add(new UpdateMapEntry(pattern, url));
+			else if (type.equals(ATT_TYPE_VALUE_UPDATE))
+				entries.add(new UpdateMapEntry(pattern, url));
+			else if (type.equals(ATT_TYPE_VALUE_DISCOVERY))
+				discoveryEntries.add(new UpdateMapEntry(pattern, url));
+			else {
+				entries.add(new UpdateMapEntry(pattern, url));
+				discoveryEntries.add(new UpdateMapEntry(pattern, url));
+			}
+		}
+	}
+	
+	private void throwCoreException(String message, Throwable e) throws CoreException {
+		String fullMessage = Messages.UpdatePolicy_UpdatePolicy+message; 
+		throw Utilities.newCoreException(fullMessage, 0, e);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdateSiteAdapter.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdateSiteAdapter.java
new file mode 100644
index 0000000..85c1e59
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdateSiteAdapter.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.search;
+
+import java.net.*;
+
+import org.eclipse.update.search.*;
+
+/**
+ */
+public class UpdateSiteAdapter implements IUpdateSiteAdapter {
+	private String label;
+	private URL url;
+	
+	public UpdateSiteAdapter(String label, URL url) {
+		this.label = label;
+		this.url = url;
+	}
+	public URL getURL() {
+		return url;
+	}
+	public String getLabel() {
+		return label;
+	}
+/*
+	public ISite getSite(IProgressMonitor monitor) {
+		try {
+			return SiteManager.getSite(getURL(), monitor);
+		} catch (CoreException e) {
+			return null;
+		}
+	}
+*/
+	public String toString(){
+		return "" + getURL(); //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdatesSearchCategory.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdatesSearchCategory.java
new file mode 100644
index 0000000..c21db09
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/search/UpdatesSearchCategory.java
@@ -0,0 +1,571 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2009 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.update.internal.search;
+
+import java.util.ArrayList;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PluginVersionIdentifier;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.configuration.IInstallConfiguration;
+import org.eclipse.update.configuration.ILocalSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.IIncludedFeatureReference;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteFeatureReference;
+import org.eclipse.update.core.IURLEntry;
+import org.eclipse.update.core.IUpdateConstants;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.operations.FeatureHierarchyElement;
+import org.eclipse.update.internal.operations.UpdateUtils;
+import org.eclipse.update.operations.IInstallFeatureOperation;
+import org.eclipse.update.operations.OperationsManager;
+import org.eclipse.update.search.IQueryUpdateSiteAdapter;
+import org.eclipse.update.search.IUpdateSearchFilter;
+import org.eclipse.update.search.IUpdateSearchQuery;
+import org.eclipse.update.search.IUpdateSearchResultCollector;
+
+public class UpdatesSearchCategory extends BaseSearchCategory {
+	private static final String CATEGORY_ID =
+		"org.eclipse.update.core.new-updates"; //$NON-NLS-1$
+	private IFeature [] features;
+	private boolean automatic;
+
+	class Candidate {
+		ArrayList children;
+		Candidate parent;
+		IFeatureReference ref;
+		public Candidate(IFeatureReference ref) {
+			this.ref = ref;
+		}
+		public Candidate(IFeatureReference ref, Candidate parent) {
+			this(ref);
+			this.parent = parent;
+		}
+		public void add(Candidate child) {
+			if (children == null)
+				children = new ArrayList();
+			child.setParent(this);
+			children.add(child);
+		}
+		void setParent(Candidate parent) {
+			this.parent = parent;
+		}
+		public IFeatureReference getReference() {
+			return ref;
+		}
+		void setReference(IFeatureReference ref) {
+			this.ref = ref;
+		}
+		public IFeature getFeature(IProgressMonitor monitor) {
+			try {
+				return ref.getFeature(monitor);
+			} catch (CoreException e) {
+				return null;
+			}
+		}
+		public Candidate getParent() {
+			return parent;
+		}
+		public Candidate getRoot() {
+			Candidate root = this;
+
+			while (root.getParent() != null) {
+				root = root.getParent();
+			}
+			return root;
+		}
+		public IURLEntry getUpdateEntry() {
+			int location = IUpdateConstants.SEARCH_ROOT;
+
+			if (ref instanceof IIncludedFeatureReference)
+				location =
+					((IIncludedFeatureReference) ref).getSearchLocation();
+			if (parent == null || location == IUpdateConstants.SEARCH_SELF) {
+				return getFeature(null).getUpdateSiteEntry();
+			}
+			return getRoot().getUpdateEntry();
+		}
+		public String toString() {
+			return ref.toString();
+		}
+		public boolean equals(Object source) {
+			if (source instanceof Candidate) {
+				return this.ref.equals(((Candidate) source).getReference());
+			}
+			if (source instanceof IFeatureReference) {
+				return this.ref.equals(source);
+			}
+			return false;
+		}
+		public void addToFlatList(ArrayList list, boolean updatableOnly) {
+			// add itself
+			if (!updatableOnly || isUpdatable())
+				list.add(this);
+			// add children
+			if (children != null) {
+				for (int i = 0; i < children.size(); i++) {
+					Candidate child = (Candidate) children.get(i);
+					child.addToFlatList(list, updatableOnly);
+				}
+			}
+		}
+		public boolean isUpdatable() {
+			return (parent == null);
+		}
+	}
+
+	private static class Hit {
+		IFeatureReference ref;
+		IInstallFeatureOperation patchFor;
+		boolean patch;
+		IInstallFeatureOperation job;
+		
+		public Hit(IFeature candidate, IFeatureReference ref) {
+			this.ref = ref;
+		}
+		public Hit(IFeature candidate, IFeatureReference ref, boolean patch) {
+			this(candidate, ref);
+			this.patch = patch;
+		}
+
+		public Hit(IFeature candidate, IFeatureReference ref, IInstallFeatureOperation patchFor) {
+			this(candidate, ref, true);
+			this.patchFor = patchFor;
+		}
+
+		public IInstallFeatureOperation getJob() {
+			if (job == null) {
+				try {
+					IFeature feature = ref.getFeature(null);
+					job = OperationsManager.getOperationFactory().createInstallOperation(null, feature, null, null, null);
+				} catch (CoreException e) {
+					UpdateCore.log(e);
+				}
+			}
+			return job;
+		}
+
+		public boolean isPatch() {
+			return patch;
+		}
+		public IInstallFeatureOperation getPatchedJob() {
+			return patchFor;
+		}
+	}
+
+	public class UpdateQuery implements IUpdateSearchQuery {
+		IFeature candidate;
+		IQueryUpdateSiteAdapter adapter;
+
+		public UpdateQuery(
+			IFeature candidate,
+			IURLEntry updateEntry) {
+			this.candidate = candidate;
+			if (updateEntry != null && updateEntry.getURL() != null)
+				adapter =
+					new QueryUpdateSiteAdapter(
+						getLabelForEntry(updateEntry),
+						updateEntry.getURL(),
+						candidate.getVersionedIdentifier().getIdentifier());
+		}
+		private String getLabelForEntry(IURLEntry entry) {
+			String label = entry.getAnnotation();
+			if (label == null || label.length() == 0)
+				label = entry.getURL().toString();
+			return label;
+		}
+
+		public IQueryUpdateSiteAdapter getQuerySearchSite() {
+			return adapter;
+		}
+		private boolean isBroken() {
+			try {
+				IStatus status =
+					SiteManager.getLocalSite().getFeatureStatus(candidate);
+				return status.getSeverity() == IStatus.ERROR;
+			} catch (CoreException e) {
+				return false;
+			}
+		}
+		private boolean isMissingOptionalChildren(IFeature feature) {
+			try {
+				IIncludedFeatureReference[] children =
+					feature.getIncludedFeatureReferences();
+				for (int i = 0; i < children.length; i++) {
+					IIncludedFeatureReference ref = children[i];
+					try {
+						IFeature child = ref.getFeature(null);
+						// If we are here, the child is not missing.
+						// Check it's children recursively.
+						if (isMissingOptionalChildren(child))
+							return true;
+					} catch (CoreException e) {
+						// Missing child. Return true if optional,
+						// otherwise it is a broken feature that we 
+						// do not care about.
+						if (ref.isOptional()) {
+							return FeatureHierarchyElement.hasOlderVersion(ref);
+						}
+					}
+				}
+			} catch (CoreException e) {
+			}
+			return false;
+		}
+		public void run(
+			ISite site,
+			String[] categoriesToSkip,
+			IUpdateSearchFilter filter,
+			IUpdateSearchResultCollector collector,
+			IProgressMonitor monitor) {
+			ArrayList hits = new ArrayList();
+			boolean broken = isBroken();
+			boolean missingOptionalChildren = false;
+
+			// Don't bother to compute missing optional children
+			// if the feature is broken - all we want is to 
+			// see if we should allow same-version re-install.
+			if (!broken)
+				missingOptionalChildren = isMissingOptionalChildren(candidate);
+			ISiteFeatureReference[] refs = site.getFeatureReferences();
+			monitor.beginTask("", refs.length + 1); //$NON-NLS-1$
+			ArrayList updateJobs = new ArrayList();
+			for (int i = 0; i < refs.length; i++) {
+				ISiteFeatureReference ref = refs[i];
+				try {
+					if (isNewerVersion(candidate.getVersionedIdentifier(),ref.getVersionedIdentifier())) {
+						Hit h = new Hit(candidate, ref);
+						hits.add(h);
+						IInstallFeatureOperation job = h.getJob();
+						if (job != null)
+							updateJobs.add(job);
+					} else {
+						// accept the same feature if the installed
+						// feature is broken
+						if ((broken || missingOptionalChildren)
+							&& candidate.getVersionedIdentifier().equals(
+								ref.getVersionedIdentifier())){
+							hits.add(new Hit(candidate, ref));
+							continue;
+						}
+						else {
+							// check for patches
+							if (isPatch(candidate, ref)){
+								hits.add(new Hit(candidate, ref, true));
+								continue;
+							}
+						}
+					}
+				} catch (CoreException e) {
+				}
+				monitor.worked(1);
+				if (monitor.isCanceled()){
+					return;
+				}
+				
+			}
+			// accept patches for updated features
+			for (int n = 0; n < updateJobs.size(); n++) {
+				IInstallFeatureOperation job = (IInstallFeatureOperation) updateJobs
+						.get(n);
+					IFeature newCandidate = job.getFeature();
+					for (int i = 0; i < refs.length; i++) {
+						ISiteFeatureReference ref = refs[i];
+						if (isPatch(newCandidate, ref)) {
+							Hit h = new Hit(newCandidate, ref, job);
+							hits.add(h);
+							continue;
+						}
+						//monitor.worked(1);
+						if (monitor.isCanceled()) {
+							return;
+						}
+					}
+			}
+			if (hits.size() > 0) {
+				collectValidHits(hits, filter, collector);
+			}
+			monitor.worked(1);
+			monitor.done();
+		}
+		
+		/**
+		 * Returns IFeature associated with the IUpdateSearchQuery
+		 */
+		public IFeature getFeature() {
+			return candidate;
+		}
+	}
+
+	private ArrayList candidates;
+
+	public UpdatesSearchCategory() {
+		this(true);
+	}
+	
+	public UpdatesSearchCategory(boolean automatic) {
+		super(CATEGORY_ID);
+		this.automatic = automatic;
+	}
+
+	private void collectValidHits(
+		ArrayList hits,
+		IUpdateSearchFilter filter,
+		IUpdateSearchResultCollector collector) {
+		//Object[] array = hits.toArray();
+		IFeature topHit = null;
+		for (int i = 0; i < hits.size(); i++) {
+			Hit hit = (Hit) hits.get(i);
+			IInstallFeatureOperation job = hit.getJob();
+			if (job == null)
+				continue;
+			// do not accept updates without a license
+			if (!UpdateUtils.hasLicense(job.getFeature())) {
+				UpdateCore.log(job.getFeature().getVersionedIdentifier() + ": " + Messages.DefaultFeatureParser_NoLicenseText, null);  //$NON-NLS-1$
+				continue;
+			}
+			IStatus status = null;
+			
+			// Fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=132450
+			// Only validate for automatic updates because
+			// non-automatic once will arrive in the review wizard
+			// where additional validation will be performed
+			if (automatic) {
+				if( hit.getPatchedJob()==null){
+					status = OperationsManager.getValidator().validatePendingInstall(job.getOldFeature(), job.getFeature());
+				}else{
+					status = OperationsManager.getValidator().validatePendingChanges(new IInstallFeatureOperation[]{hit.getPatchedJob(), job});
+				}
+			}
+			if (status == null || status.getCode() == IStatus.WARNING) {
+				if (hit.isPatch()) {
+					IFeature patch = job.getFeature();
+					// Do not add the patch if already installed
+					IFeature[] sameId = UpdateUtils.getInstalledFeatures(patch, false);
+					if (sameId.length==0) {
+						if (filter.accept(patch))
+							collector.accept(patch);
+					}
+				}
+				else  {
+					topHit = job.getFeature();
+					if (filter.accept(topHit))
+						collector.accept(topHit);
+				}
+			}
+		}
+	}
+
+	private void initialize() {
+		candidates = new ArrayList();
+		try {
+			ILocalSite localSite = SiteManager.getLocalSite();
+			IInstallConfiguration config = localSite.getCurrentConfiguration();
+			IConfiguredSite[] isites = config.getConfiguredSites();
+			for (int i = 0; i < isites.length; i++) {
+				contributeCandidates(isites[i]);
+			}
+		} catch (CoreException e) {
+			UpdateCore.log(
+				Messages.UpdatesSearchCategory_errorSearchingForUpdates, 
+				e);
+		}
+	}
+
+	private void contributeCandidates(IConfiguredSite isite)
+		throws CoreException {
+		IFeatureReference[] refs = isite.getConfiguredFeatures();
+		ArrayList candidatesPerSite = new ArrayList();
+		for (int i = 0; i < refs.length; i++) {
+			IFeatureReference ref = refs[i];
+			// Don't waste time searching for updates to 
+			// patches.
+			try {
+				if (UpdateUtils.isPatch(ref.getFeature(null)))
+					continue;
+			}
+			catch (CoreException e) {
+				continue;
+			}
+			Candidate c = new Candidate(ref);
+			candidatesPerSite.add(c);
+		}
+		// Create a tree from a flat list
+		buildHierarchy(candidatesPerSite);
+		// Add the remaining root candidates to 
+		// the global list of candidates.
+		candidates.addAll(candidatesPerSite);
+	}
+
+	private void buildHierarchy(ArrayList candidates) throws CoreException {
+		Candidate[] array =
+			(Candidate[]) candidates.toArray(new Candidate[candidates.size()]);
+		// filter out included features so that only top-level features remain on the list
+		for (int i = 0; i < array.length; i++) {
+			Candidate parent = array[i];
+			IFeature feature = parent.getFeature(null);
+			IFeatureReference[] included =
+				feature.getIncludedFeatureReferences();
+			for (int j = 0; j < included.length; j++) {
+				IFeatureReference fref = included[j];
+				Candidate child = findCandidate(candidates, fref);
+				if (child != null) {
+					parent.add(child);
+					child.setReference(fref);
+					candidates.remove(child);
+				}
+			}
+		}
+	}
+	private Candidate findCandidate(ArrayList list, IFeatureReference ref) {
+		for (int i = 0; i < list.size(); i++) {
+			Candidate c = (Candidate) list.get(i);
+			if (c.ref.equals(ref))
+				return c;
+		}
+		return null;
+	}
+
+	public IUpdateSearchQuery[] getQueries() {
+		initialize();
+		ArrayList allCandidates = getAllCandidates();
+
+		IUpdateSearchQuery[] queries =
+			new IUpdateSearchQuery[allCandidates.size()];
+		for (int i = 0; i < queries.length; i++) {
+			Candidate candidate = (Candidate) allCandidates.get(i);
+			IFeature feature = candidate.getFeature(null);
+			IURLEntry updateEntry = candidate.getUpdateEntry();
+			if (feature == null) {
+				queries[i] = null;
+			} else {
+				queries[i] = new UpdateQuery(feature, updateEntry);
+			}
+		}
+		return queries;
+	}
+	
+/**
+ * Sets the features for which new updates need to be found. If
+ * not set, updates will be searched for all the installed
+ * and configured features.
+ * @param features the features to search updates for
+ */	
+	public void setFeatures(IFeature [] features) {
+		this.features = features;
+	}
+	
+/**
+ * Returns an array of features for which updates need to
+ * be found. 
+ * @return an array of features or <samp>null</samp> if not
+ * set.
+ */	
+	public IFeature [] getFeatures() {
+		return features;
+	}
+	/**
+	 * @param fvi
+	 * @param cvi
+	 * @return fvi < cvi
+	 */
+	private boolean isNewerVersion(
+		VersionedIdentifier fvi,
+		VersionedIdentifier cvi) {
+		if (!fvi.getIdentifier().equals(cvi.getIdentifier()))
+			return false;
+		PluginVersionIdentifier fv = fvi.getVersion();
+		PluginVersionIdentifier cv = cvi.getVersion();
+		String mode = getUpdateVersionsMode();
+		boolean greater = cv.isGreaterThan(fv);
+		if (!greater)
+			return false;
+		if (mode.equals(UpdateCore.EQUIVALENT_VALUE))
+			return cv.isEquivalentTo(fv);
+		else if (mode.equals(UpdateCore.COMPATIBLE_VALUE))
+			return cv.isCompatibleWith(fv);
+		else
+			return false;
+	}
+
+	private boolean isPatch(IFeature candidate, ISiteFeatureReference ref) {
+		if (ref.isPatch() == false)
+			return false;
+		try {
+			IFeature feature = ref.getFeature(null);
+			if( UpdateUtils.isPatch(candidate, feature))
+				return true;
+			// Check if patch is for children
+			try {
+				IIncludedFeatureReference[] children =
+					candidate.getIncludedFeatureReferences();
+				for (int i = 0; i < children.length; i++) {
+					IIncludedFeatureReference cref = children[i];
+					try {
+						IFeature child = cref.getFeature(null);
+						if (isPatch(child, ref))
+							return true;
+					} catch (CoreException e) {
+					}
+				}
+			} catch (CoreException e) {
+			}
+			return false;
+		} catch (CoreException e) {
+			return false;
+		}
+	}
+	
+	private String getUpdateVersionsMode() {
+		Preferences store = UpdateCore.getPlugin().getPluginPreferences();
+		return store.getString(UpdateCore.P_UPDATE_VERSIONS);
+	}
+/*
+ * This method recursively walks the list of candidates
+ * building the flat that starts with the roots but
+ * also includes all the children that are updatable
+ * (use 'include' clause with a match that is not 'perfect').
+ */
+	private ArrayList getAllCandidates() {
+		ArrayList selected = new ArrayList();
+		for (int i=0; i<candidates.size(); i++) {
+			Candidate c = (Candidate)candidates.get(i);
+			if (isOnTheList(c))
+				c.addToFlatList(selected, true);
+		}
+		return selected;
+	}
+	
+	private boolean isOnTheList(Candidate c) {
+		if (features==null) return true;
+		VersionedIdentifier vid;
+		try {
+			vid = c.getReference().getVersionedIdentifier();
+		}
+		catch (CoreException e) {
+			return false;
+		}
+		for (int i=0; i<features.length; i++) {
+			IFeature feature = features[i];
+			VersionedIdentifier fvid = feature.getVersionedIdentifier();
+			if (fvid.equals(vid))
+				return true;
+		}
+		return false;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/CertificatePair.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/CertificatePair.java
new file mode 100644
index 0000000..9b0c4c1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/CertificatePair.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.security;
+ 
+ import java.security.cert.*;
+ 
+/**
+ *
+ */
+public class CertificatePair {
+	private Certificate root;
+	private Certificate issuer;
+	
+	
+
+	/**
+	 * Gets the root.
+	 * @return Returns a Certificate
+	 */
+	public Certificate getRoot() {
+		return root;
+	}
+
+	/**
+	 * Sets the root.
+	 * @param root The root to set
+	 */
+	public void setRoot(Certificate root) {
+		this.root = root;
+	}
+
+	/**
+	 * Gets the issuer.
+	 * @return Returns a Certificate
+	 */
+	public Certificate getIssuer() {
+		return issuer;
+	}
+
+	/**
+	 * Sets the issuer.
+	 * @param issuer The issuer to set
+	 */
+	public void setIssuer(Certificate issuer) {
+		this.issuer = issuer;
+	}
+
+	/*
+	 * @see Object#equals(Object)
+	 */
+	public boolean equals(Object obj) {
+		
+		if (obj==null) return false;
+		
+		if (!(obj instanceof CertificatePair)) return false;
+		
+		if (root==null || issuer==null) return false;
+		
+		CertificatePair pair = (CertificatePair)obj;
+		
+		return (root.equals(pair.getRoot()) && issuer.equals(pair.getIssuer()));
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/JarVerificationResult.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/JarVerificationResult.java
new file mode 100644
index 0000000..87406f7
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/JarVerificationResult.java
@@ -0,0 +1,334 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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.update.internal.security;
+
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.Messages;
+
+/**
+ * Result of the service
+ */
+public class JarVerificationResult implements IVerificationResult {
+
+
+	private int resultCode;
+	private int verificationCode;
+	private Exception resultException;
+	private List /*of Certificates[] */
+	certificates;
+	private CertificatePair[] rootCertificates;
+	private CertificatePair foundCertificate; // certificate found in one keystore
+	
+	private String signerInfo;
+	private String verifierInfo;
+	private ContentReference contentReference;
+	private IFeature feature;
+	private boolean featureVerification;
+	private boolean alreadySeen;
+
+	public JarVerificationResult() {
+	}
+	
+	/*
+	 * 
+	 */
+	public int getResultCode() {
+		return resultCode;
+	}
+	
+	/*
+	 * 
+	 */
+	public Exception getVerificationException() {
+		return resultException;
+	}
+	
+	/*
+	 * 
+	 */
+	public void setResultCode(int newResultCode) {
+		resultCode = newResultCode;
+	}
+	
+	/*
+	 * 
+	 */
+	public void setResultException(Exception newResultException) {
+		resultException = newResultException;
+	}
+	
+	/*
+	 * 
+	 */
+	public int getVerificationCode() {
+		return verificationCode;
+	}
+
+	/*
+	 * 
+	 */
+	public void setVerificationCode(int verificationCode) {
+		this.verificationCode = verificationCode;
+	}
+
+	/*
+	 * adds an array of Certificates to the list
+	 * force recomputation of root cert
+	 */
+	public void addCertificates(Certificate[] certs) {
+		if (certificates == null)
+			certificates = new ArrayList();
+		certificates.add(certs);
+		rootCertificates = null;
+	}
+
+	/*
+	 * Returns the list of root certificates
+	 * The list of certificates we received is an array of certificates
+	 * we have to determine 
+	 * 1) how many chain do we have (a chain stops when verifier of a cert is 
+	 * not the signer of the next cert in the list 
+	 * 2) build a cert with the leaf signer and the root verifier for each chain
+	 */
+	public CertificatePair[] getRootCertificates() {
+		if (rootCertificates == null) {
+			rootCertificates = new CertificatePair[0];			
+			List rootCertificatesList = new ArrayList();
+			if (certificates != null && certificates.size() > 0) {
+				Iterator iter = certificates.iterator();
+				while (iter.hasNext()) {
+
+					Certificate[] certs = (Certificate[]) iter.next();
+					if (certs != null && certs.length > 0) {
+
+						CertificatePair pair = new CertificatePair();
+						pair.setIssuer(certs[0]);
+
+						for (int i = 0; i < certs.length - 1; i++) {
+							X509Certificate x509certRoot = (X509Certificate) certs[i];
+							X509Certificate x509certIssuer = (X509Certificate) certs[i+1];
+							if (!x509certRoot.getIssuerDN().equals(x509certIssuer.getSubjectDN())) {
+								pair.setRoot(x509certRoot);
+								if (!rootCertificatesList.contains(pair)) {
+									rootCertificatesList.add(pair);
+								}
+								pair = new CertificatePair();
+								pair.setIssuer(x509certIssuer);
+							}
+						}
+
+						// add the latest one
+						if (pair != null) {
+							pair.setRoot(certs[certs.length - 1]);
+							if (!rootCertificatesList.contains(pair)) {
+								rootCertificatesList.add(pair);
+							}
+						}
+					}
+				}
+
+			}
+			
+			if (rootCertificatesList.size() > 0) {
+				rootCertificates = new CertificatePair[rootCertificatesList.size()];				
+				rootCertificatesList.toArray(rootCertificates);
+			}
+		}
+		return rootCertificates;
+	}
+
+	/*
+	 * 
+	 */
+	private CertificatePair getFoundCertificate() {
+		return foundCertificate;
+	}
+
+	/*
+	 * 
+	 */
+	public void setFoundCertificate(CertificatePair foundCertificate) {
+		this.foundCertificate = foundCertificate;
+	}
+
+
+	/*
+	 * Initializes the signerInfo and the VerifierInfo from the Certificate Pair
+	 */
+	private void initializeCertificates(){
+		X509Certificate certRoot = null;
+		X509Certificate certIssuer = null;
+		CertificatePair trustedCertificate;
+		if (getFoundCertificate() == null) {
+			CertificatePair[] certs = getRootCertificates();
+			if (certs.length == 0)
+				return;
+			trustedCertificate = certs[0];
+		} else {
+			trustedCertificate = getFoundCertificate();
+		}
+		certRoot = (X509Certificate) trustedCertificate.getRoot();
+		certIssuer = (X509Certificate) trustedCertificate.getIssuer();
+
+		StringBuffer strb = new StringBuffer();
+		strb.append(issuerString(certIssuer.getSubjectDN()));
+		strb.append("\r\n"); //$NON-NLS-1$
+		strb.append(NLS.bind(Messages.JarVerificationResult_ValidBetween, (new String[] { dateString(certIssuer.getNotBefore()), dateString(certIssuer.getNotAfter()) })));
+		strb.append(checkValidity(certIssuer));
+		signerInfo = strb.toString();
+		if (certIssuer != null && !certIssuer.equals(certRoot)) {
+			strb = new StringBuffer();	
+			strb.append(issuerString(certIssuer.getIssuerDN()));
+			strb.append("\r\n"); //$NON-NLS-1$
+			strb.append(NLS.bind(Messages.JarVerificationResult_ValidBetween, (new String[] { dateString(certRoot.getNotBefore()), dateString(certRoot.getNotAfter()) }))); 
+			strb.append(checkValidity(certRoot));
+			verifierInfo = strb.toString();
+		}
+
+	}
+
+	/*
+	 * Returns a String to show if the certificate is valid
+	 */
+	private String checkValidity(X509Certificate cert) {
+
+		try {
+			cert.checkValidity();
+		} catch (CertificateExpiredException e) {
+			return ("\r\n" + Messages.JarVerificationResult_ExpiredCertificate);  //$NON-NLS-1$
+		} catch (CertificateNotYetValidException e) {
+			return ("\r\n" + Messages.JarVerificationResult_CertificateNotYetValid);  //$NON-NLS-1$
+		}
+		return ("\r\n" + Messages.JarVerificationResult_CertificateValid);  //$NON-NLS-1$
+	}
+
+	/*
+	 * Returns the label String from a X50name
+	 */
+	private String issuerString(Principal principal) {
+// 19902
+//		try {
+//			if (principal instanceof X500Name) {
+//				StringBuffer buf = new StringBuffer();
+//				X500Name name = (X500Name) principal;
+//				buf.append((name.getDNQualifier() != null) ? name.getDNQualifier() + ", " : "");
+//				buf.append(name.getCommonName());
+//				buf.append((name.getOrganizationalUnit() != null) ? ", " + name.getOrganizationalUnit() : "");
+//				buf.append((name.getOrganization() != null) ? ", " + name.getOrganization() : "");
+//				buf.append((name.getLocality() != null) ? ", " + name.getLocality() : "");
+//				buf.append((name.getCountry() != null) ? ", " + name.getCountry() : "");
+//				return new String(buf);
+//			}
+//		} catch (Exception e) {
+//			UpdateCore.warn("Error parsing X500 Certificate",e);
+//		}
+		return principal.toString();
+	}
+
+	/*
+	 * 
+	 */
+	private String dateString(Date date) {
+		return DateFormat.getDateInstance().format(date);
+	}
+
+	/*
+	 *
+	 */
+	public String getSignerInfo() {
+		if (signerInfo==null) initializeCertificates();
+		return signerInfo;
+	}
+
+	/*
+	 *
+	 */
+	public String getVerifierInfo() {
+		if (signerInfo==null) initializeCertificates();		
+		return verifierInfo;
+	}
+
+	/*
+	 *
+	 */
+	public ContentReference getContentReference() {
+		return contentReference;
+	}
+
+	/*
+	 * 
+	 */
+	public void setContentReference(ContentReference ref) {
+		this.contentReference = ref;
+	}
+
+
+	/*
+	 *
+	 */
+	public IFeature getFeature() {
+		return feature;
+	}
+
+	/*
+	 * 
+	 */
+	public void setFeature(IFeature feature) {
+		this.feature = feature;
+	}
+
+	/*
+	 * 
+	 */
+	public String getText() {
+		return null;
+	}
+
+
+	/*
+	 * 
+	 */
+	public boolean isFeatureVerification() {
+		return featureVerification;
+	}
+	
+	/*
+	 * 
+	 */
+	public void isFeatureVerification(boolean featureVerification) {
+		this.featureVerification = featureVerification;
+	}
+
+	/*
+	 *
+	 */
+	public boolean alreadySeen() {
+		return alreadySeen;
+	}
+
+	/*
+	 * 
+	 */
+	public boolean alreadySeen(boolean seen) {
+		return this.alreadySeen = seen;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/JarVerifier.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/JarVerifier.java
new file mode 100644
index 0000000..d0b25b2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/JarVerifier.java
@@ -0,0 +1,457 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.security;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.ContentReference;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IVerificationResult;
+import org.eclipse.update.core.IVerifier;
+import org.eclipse.update.core.InstallMonitor;
+import org.eclipse.update.core.JarContentReference;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.Verifier;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+
+/**
+ * The JarVerifier will check the integrity of the JAR.
+ * If the Jar is signed and the integrity is validated,
+ * it will check if one of the certificate of each file
+ * is in one of the keystore.
+ *
+ */
+
+public class JarVerifier extends Verifier {
+
+	private static final String MANIFEST = "META-INF"; //$NON-NLS-1$
+
+	private JarVerificationResult result;
+	private List /*of CertificatePair*/
+	trustedCertificates;
+	private boolean acceptUnsignedFiles;
+	private List /* of KeyStore */
+	listOfKeystores;
+	private IProgressMonitor monitor;
+	private File jarFile;
+
+	private static	byte[] buffer = new byte[8192];
+	
+	/*
+	 * Default Constructor
+	 */
+	public JarVerifier() {
+		initialize();
+	}
+
+	/*
+	 * Returns the list of the keystores.
+	 */
+	private List getKeyStores() throws CoreException {
+		if (listOfKeystores == null) {
+			listOfKeystores = new ArrayList(0);
+			KeyStores listOfKeystoreHandles = new KeyStores();
+			InputStream in = null;
+			KeyStore keystore = null;
+			KeystoreHandle handle = null;
+			while (listOfKeystoreHandles.hasNext()) {
+				try {
+					handle = listOfKeystoreHandles.next();
+					in = ConnectionFactory.get(handle.getLocation()).getInputStream();
+					try {
+						keystore = KeyStore.getInstance(handle.getType());
+						keystore.load(in, null); // no password
+					} catch (NoSuchAlgorithmException e) {
+						throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToFindEncryption, (new String[] { handle.getLocation().toExternalForm() })), e);
+					} catch (CertificateException e) {
+						throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToLoadCertificate, (new String[] { handle.getLocation().toExternalForm() })), e);
+					} catch (KeyStoreException e) {
+						throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToFindProviderForKeystore, (new String[] { handle.getType() })), e);
+					} finally {
+						if (in != null) {
+							try {
+								in.close();
+							} catch (IOException e) {
+							} // nothing
+						}
+					} // try loading a keyStore
+
+					// keystore was loaded
+					listOfKeystores.add(keystore);
+				} catch (IOException e) {
+					// nothing... if the keystore doesn't exist, continue	
+				}
+
+			} // while all key stores
+
+		}
+
+		return listOfKeystores;
+	}
+
+	/*
+	 * 
+	 */
+	private void initialize() {
+		result = null;
+		trustedCertificates = null;
+		acceptUnsignedFiles = false;
+		listOfKeystores = null;
+	}
+
+	/*
+	 * init
+	 */
+	private void init(IFeature feature, ContentReference contentRef) throws CoreException {
+		jarFile = null;
+		if (contentRef instanceof JarContentReference) {
+			JarContentReference jarReference = (JarContentReference) contentRef;
+			try {
+				jarFile = jarReference.asFile();
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
+					UpdateCore.debug("Attempting to read JAR file:"+jarFile); //$NON-NLS-1$
+			
+				// # of entries
+				if (!jarFile.exists()) throw new IOException();
+				JarFile jar = new JarFile(jarFile);
+				if (jar !=null){
+					try {
+						jar.close();
+					} catch (IOException ex) {
+						// unchecked
+					}
+				}
+			} catch (ZipException e){
+				throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_InvalidJar, (new String[] { jarReference.toString() })), e);				
+			} catch (IOException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToAccessJar, (new String[] { jarReference.toString() })), e);
+			}
+		}
+
+		result = new JarVerificationResult();
+		result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
+		result.setResultException(null);
+		result.setFeature(feature);
+		result.setContentReference(contentRef);
+	}
+
+	/*
+	 * Returns true if one of the certificate exists in the keystore
+	 */
+	private boolean existsInKeystore(Certificate cert) throws CoreException {
+		try {
+			List keyStores = getKeyStores();
+			if (!keyStores.isEmpty()) {
+				Iterator listOfKeystores = keyStores.iterator();
+				while (listOfKeystores.hasNext()) {
+					KeyStore keystore = (KeyStore) listOfKeystores.next();
+
+					if (keystore.getCertificateAlias(cert) != null) {
+						return true;
+					}
+				}
+			}
+		} catch (KeyStoreException e) {
+			throw Utilities.newCoreException(Messages.JarVerifier_KeyStoreNotLoaded, e); 
+		}
+		return false;
+	}
+
+	/*
+	 * 
+	 */
+	private List readJarFile(JarFile jarFile, String identifier)
+		throws IOException, InterruptedException {
+		List list = new ArrayList();
+
+		Enumeration entries = jarFile.entries();
+		JarEntry currentEntry = null;
+		InputStream in = null;
+		if (monitor != null)
+			monitor.setTaskName(NLS.bind(Messages.JarVerifier_Verify, (new String[] { identifier == null ? jarFile.getName(): identifier }))); 
+
+		try {
+			while (entries.hasMoreElements()) {
+				currentEntry = (JarEntry) entries.nextElement();
+				list.add(currentEntry);
+				in = jarFile.getInputStream(currentEntry);
+				while ((in.read(buffer, 0, buffer.length)) != -1) {
+					// Security error thrown if tempered
+				}
+				if (in!=null)
+					in.close();
+			}
+		} catch (IOException e) {
+			result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
+			result.setResultException(e);
+		} finally {
+			try {
+				if (in != null)
+					in.close();
+			} catch (IOException e1) {
+				// ignore
+			}
+		}
+
+		return list;
+	}
+
+	/*
+	 * @param newMonitor org.eclipse.core.runtime.IProgressMonitor
+	 */
+	public void setMonitor(IProgressMonitor newMonitor) {
+		monitor = newMonitor;
+	}
+
+	/*
+	 * @see IVerifier#verify(IFeature,ContentReference,boolean, InstallMonitor)
+	 */
+	public IVerificationResult verify(
+		IFeature feature,
+		ContentReference reference,
+		boolean isFeatureVerification,
+		InstallMonitor monitor)
+		throws CoreException {
+
+		if (reference == null)
+			return result;
+
+		// if parent knows how to verify, ask the parent first
+		if (getParent() != null) {
+			IVerificationResult vr =
+				getParent().verify(feature, reference, isFeatureVerification, monitor);
+			if (vr.getVerificationCode() != IVerificationResult.TYPE_ENTRY_UNRECOGNIZED)
+				return vr;
+		}
+
+		// the parent couldn't verify
+		setMonitor(monitor);
+		init(feature, reference);
+		result.isFeatureVerification(isFeatureVerification);
+
+		if (jarFile!=null) {
+				result = verify(jarFile.getAbsolutePath(), reference.getIdentifier());
+		} else {
+			result.setVerificationCode(IVerificationResult.TYPE_ENTRY_UNRECOGNIZED);
+		}
+
+		return result;
+	}
+
+	/*
+	 * 
+	 */
+	private JarVerificationResult verify(String file, String identifier) {
+
+		try {
+
+			// verify integrity
+			verifyIntegrity(file, identifier);
+
+			// do not close input stream
+			// as verifyIntegrity already did it
+
+			//if user already said yes
+			result.alreadySeen(alreadyValidated());
+
+			// verify source certificate
+			if (result.getVerificationCode()
+				== IVerificationResult.TYPE_ENTRY_SIGNED_UNRECOGNIZED) {
+				verifyAuthentication();
+			}
+
+			// save the fact the file is not signed, so the user will not be prompted again 
+			if (result.getVerificationCode()
+				== IVerificationResult.TYPE_ENTRY_NOT_SIGNED) {
+				acceptUnsignedFiles = true;
+			}
+
+		} catch (Exception e) {
+			result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
+			result.setResultException(e);
+		}
+
+		if (monitor != null) {
+			monitor.worked(1);
+			if (monitor.isCanceled()) {
+				result.setVerificationCode(IVerificationResult.VERIFICATION_CANCELLED);
+			}
+		}
+
+		return result;
+	}
+
+	/*
+	 * Verifies that each file has at least one certificate
+	 * valid in the keystore
+	 *
+	 * At least one certificate from each Certificate Array
+	 * of the Jar file must be found in the known Certificates
+	 */
+	private void verifyAuthentication() throws CoreException {
+
+		CertificatePair[] entries = result.getRootCertificates();
+		boolean certificateFound = false;
+
+		// If all the certificate of an entry are
+		// not found in the list of known certifcate
+		// the certificate is not trusted by any keystore.
+		for (int i = 0; i < entries.length; i++) {
+			certificateFound = existsInKeystore(entries[i].getRoot());
+			if (certificateFound) {
+				result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_RECOGNIZED);
+				result.setFoundCertificate(entries[i]);
+				return;
+			}
+		}
+	}
+
+	/*
+	 * Verifies the integrity of the JAR
+	 */
+	private void verifyIntegrity(String file, String identifier) {
+
+		JarFile jarFile = null;
+
+		try {
+			// If the JAR is signed and not valid
+			// a security exception will be thrown
+			// while reading it
+			jarFile = new JarFile(file, true);
+			List filesInJar = readJarFile(jarFile, identifier);
+
+			// you have to read all the files once
+			// before getting the certificates 
+			if (jarFile.getManifest() != null) {
+				Iterator iter = filesInJar.iterator();
+				boolean certificateFound = false;
+				while (iter.hasNext()) {
+					JarEntry currentJarEntry = (JarEntry) iter.next();
+					Certificate[] certs = currentJarEntry.getCertificates();
+					if ((certs != null) && (certs.length != 0)) {
+						certificateFound = true;
+						result.addCertificates(certs);
+					} else {
+						String jarEntryName = currentJarEntry.getName();
+						if (!jarEntryName.toUpperCase().startsWith(MANIFEST)
+							&& !currentJarEntry.isDirectory()) {
+							// if the jarEntry is not in MANIFEST, consider the whole file unsigned							
+							break;
+						}
+
+					}
+				}
+
+				if (certificateFound)
+					result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_UNRECOGNIZED);
+				else
+					result.setVerificationCode(IVerificationResult.TYPE_ENTRY_NOT_SIGNED);
+			} else {
+				Exception e = new Exception(NLS.bind(Messages.JarVerifier_InvalidFile, (new String[] { file })));
+				result.setResultException(e);
+				result.setVerificationCode(IVerificationResult.TYPE_ENTRY_NOT_SIGNED);
+				UpdateCore.warn(null,e);
+			}
+		} catch (SecurityException e) {
+			// Jar file is signed
+			// but content has changed since signed
+			result.setVerificationCode(IVerificationResult.TYPE_ENTRY_CORRUPTED);
+		} catch (InterruptedException e) {
+			result.setVerificationCode(IVerificationResult.VERIFICATION_CANCELLED);
+		} catch (Exception e) {
+			result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
+			result.setResultException(e);
+		} finally {
+			if (jarFile!=null){
+				try {jarFile.close();} catch (IOException e){}
+			}
+		}
+
+	}
+
+	/*
+	 * 
+	 */
+	private boolean alreadyValidated() {
+
+		if (result.getVerificationCode() == IVerificationResult.TYPE_ENTRY_NOT_SIGNED)
+			return (acceptUnsignedFiles);
+
+		if (getTrustedCertificates() != null) {
+			Iterator iter = getTrustedCertificates().iterator();
+			CertificatePair[] jarPairs = result.getRootCertificates();
+
+			// check if this is not a user accepted certificate for this feature	
+			while (iter.hasNext()) {
+				CertificatePair trustedCertificate = (CertificatePair) iter.next();
+				for (int i = 0; i < jarPairs.length; i++) {
+					if (trustedCertificate.equals(jarPairs[i])) {
+						return true;
+					}
+				}
+			}
+
+			// if certificate pair not found in trusted add it for next time
+			for (int i = 0; i < jarPairs.length; i++) {
+				addTrustedCertificate(jarPairs[i]);
+			}
+		}
+
+		return false;
+	}
+
+	/*
+	 * 
+	 */
+	private void addTrustedCertificate(CertificatePair pair) {
+		if (trustedCertificates == null)
+			trustedCertificates = new ArrayList();
+		if (pair != null)
+			trustedCertificates.add(pair);
+	}
+
+	/*
+	 * 
+	 */
+	private List getTrustedCertificates() {
+		if (trustedCertificates == null)
+			trustedCertificates = new ArrayList();
+		return trustedCertificates;
+	}
+
+	/**
+	 * @see IVerifier#setParent(IVerifier)
+	 */
+	public void setParent(IVerifier parentVerifier) {
+		super.setParent(parentVerifier);
+		initialize();
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/KeyStores.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/KeyStores.java
new file mode 100644
index 0000000..9907ca6
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/KeyStores.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.security;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.connection.ConnectionFactory;
+/**
+ * Class to manage the different KeyStores we should
+ * check for certificates of Signed JAR
+ */
+public class KeyStores {
+
+
+	/**
+	 * java.policy files properties of the java.security file
+	 */
+	private static final String JAVA_POLICY_URL = "policy.url."; //$NON-NLS-1$
+
+	/**
+	 * Default keystore type in java.security file
+	 */	
+    private static final String DEFAULT_KEYSTORE_TYPE = "keystore.type"; //$NON-NLS-1$
+
+	/**
+	 * List of KeystoreHandle pointing of valid KeyStores
+	 * the URL of the KeystoreHandle is not tested yet...
+	 */
+	private List /* of KeystoreHandle */ listOfKeyStores;
+
+	/**
+	 * Iterator
+	 */
+	private Iterator iterator;
+	/**
+	 * KeyStores constructor comment.
+	 */
+	public KeyStores() {
+		super();
+		initializeDefaultKeyStores();
+	}
+	/**
+	 * 
+	 */
+	private Iterator getIterator() {
+		if (iterator == null)
+			iterator = listOfKeyStores.iterator();
+		return iterator;
+	}
+	/**
+	 * returns trus if there is more Keystores in the list
+	 */
+	public boolean hasNext() {
+		return getIterator().hasNext();
+	}
+	/**
+	 * populate the list of Keystores
+	 * should be done with Dialog with Cancel/Skip button if
+	 * the connection to the URL is down...
+	 */
+	private void initializeDefaultKeyStores() {
+
+		listOfKeyStores = new ArrayList(5);
+
+		// get JRE cacerts
+		try {
+			URL url = new URL("file", null, 0, System.getProperty("java.home") + File.separator + "lib" + File.separator + "security" + File.separator + "cacerts");  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+			listOfKeyStores.add(new KeystoreHandle(url,Security.getProperty(DEFAULT_KEYSTORE_TYPE)));
+		}
+		catch (MalformedURLException e) {
+			// should not happen, hardcoded...
+		}
+
+		// get java.home .keystore
+		try {
+			URL url = new URL("file", null, 0, System.getProperty("user.home") + File.separator + ".keystore"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			listOfKeyStores.add(new KeystoreHandle(url,Security.getProperty(DEFAULT_KEYSTORE_TYPE)));
+		}
+		catch (MalformedURLException e) {
+			// should not happen, hardcoded...
+		}
+
+		// get KeyStores from policy files...
+		int index = 1;
+		String java_policy = Security.getProperty(JAVA_POLICY_URL+index);
+		while (java_policy!=null){
+			// retrieve keystore url from java.policy
+			// also retrieve keystore type
+			KeystoreHandle keystore = getKeystoreFromLocation(java_policy);
+			if (keystore!=null){
+				listOfKeyStores.add(keystore);
+			}
+			index++;	
+			java_policy = Security.getProperty(JAVA_POLICY_URL+index);			
+		}
+
+	}
+	/**
+	 * returns the URL for the Next KeystoreHandle
+	 */
+	public KeystoreHandle next() {
+		return (KeystoreHandle) getIterator().next();
+	}
+	
+	/**
+	 * retrieve the keystore from java.policy file
+	 */
+	private KeystoreHandle getKeystoreFromLocation(String location){
+		
+		InputStream in = null;
+		char[] buff = new char[4096];
+		
+		
+		int indexOf$ = location.indexOf("${"); //$NON-NLS-1$
+		int indexOfCurly = location.indexOf('}',indexOf$);
+		if (indexOf$!=-1 && indexOfCurly!=-1){
+			String prop = System.getProperty(location.substring(indexOf$+2,indexOfCurly));
+			String location2 = location.substring(0,indexOf$);
+			location2 += prop;
+			location2 += location.substring(indexOfCurly+1);
+			location = location2;
+		}
+		
+		
+		try {
+			URL url = new URL(location);
+			in = ConnectionFactory.get(url).getInputStream();
+			Reader reader = new InputStreamReader(in);
+			int result = reader.read(buff);
+			StringBuffer contentBuff = new StringBuffer();
+			while (result!=-1){
+				contentBuff.append(buff,0,result);
+				result = reader.read(buff);				
+			}
+
+			if (contentBuff.length()>0){			
+				String content = new String(contentBuff);
+				int indexOfKeystore = content.indexOf("keystore"); //$NON-NLS-1$
+				if (indexOfKeystore != -1){
+					int indexOfSemiColumn = content.indexOf(';',indexOfKeystore);
+					return getKeystoreFromString(content.substring(indexOfKeystore,indexOfSemiColumn),url);
+				}
+			}
+		} catch (MalformedURLException e){
+			log(e);
+		} catch (IOException e){
+			// url.openStream, reader.read (x2)
+			// only log, the keystore may not exist
+			log(e);	
+		} finally {
+			if (in!=null){
+				try {
+					in.close();
+				} catch (IOException e){}
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * retrieve the keystore from java.policy file
+	 */
+	private KeystoreHandle getKeystoreFromString(String content,URL rootURL){
+		KeystoreHandle handle = null;
+		String keyStoreType = Security.getProperty(DEFAULT_KEYSTORE_TYPE);
+		
+		
+		int indexOfSpace = content.indexOf(' ');
+		if (indexOfSpace==-1) return null;
+		
+		int secondSpace = content.lastIndexOf(',');
+		if (secondSpace==-1) {
+			secondSpace = content.length();
+		} else {
+			keyStoreType = content.substring(secondSpace+1,content.length()).trim();
+		}
+		
+		URL url = null;
+		try {
+			url = new URL(content.substring(indexOfSpace,secondSpace));
+		} catch (MalformedURLException e){
+			log(e);
+			// the url maybe relative
+			try {
+			url = new URL(rootURL,content.substring(indexOfSpace,secondSpace));				
+			} catch (MalformedURLException e1){
+				log(e1);			
+			}
+		}
+
+		if (url!=null)
+			handle = new KeystoreHandle(url,keyStoreType);				
+			
+		return handle;
+	}	
+	
+	private void log(Exception e){
+		UpdateCore.warn("Cannot retrieve a KeyStore",e); //$NON-NLS-1$
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/KeystoreHandle.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/KeystoreHandle.java
new file mode 100644
index 0000000..d1a7b29
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/security/KeystoreHandle.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.update.internal.security;
+
+import java.net.*;
+
+
+ 
+/**
+ * Manages a handle to a keystore
+ */
+public class KeystoreHandle {
+	
+	private URL location;
+	private String type;
+	
+	public KeystoreHandle(URL url, String type){
+		this.location = url;
+		this.type = type;
+	}
+
+	/**
+	 * Gets the location.
+	 * @return Returns a URL
+	 */
+	public URL getLocation() {
+		return location;
+	}
+
+	/**
+	 * Sets the location.
+	 * @param location The location to set
+	 */
+	public void setLocation(URL location) {
+		this.location = location;
+	}
+
+	/**
+	 * Gets the type.
+	 * @return Returns a String
+	 */
+	public String getType() {
+		return type;
+	}
+
+	/**
+	 * Sets the type.
+	 * @param type The type to set
+	 */
+	public void setType(String type) {
+		this.type = type;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/verifier/CertVerificationResult.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/verifier/CertVerificationResult.java
new file mode 100644
index 0000000..d8c3b9e
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/verifier/CertVerificationResult.java
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.verifier;
+
+import java.security.Principal;
+import java.security.cert.*;
+import java.text.DateFormat;
+import java.util.Date;
+import org.eclipse.osgi.signedcontent.SignedContent;
+import org.eclipse.osgi.signedcontent.SignerInfo;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.Messages;
+
+/**
+ * Result of the service
+ */
+public class CertVerificationResult implements IVerificationResult {
+
+
+	private int resultCode;
+	private int verificationCode;
+	private Exception resultException;
+
+	private SignedContent signedContent;
+	private String signerInfo;
+	private String verifierInfo;
+	private ContentReference contentReference;
+	private IFeature feature;
+	private boolean featureVerification;
+	private boolean alreadySeen;
+
+	public CertVerificationResult() {
+	}
+	
+	/*
+	 * 
+	 */
+	public int getResultCode() {
+		return resultCode;
+	}
+	
+	/*
+	 * 
+	 */
+	public Exception getVerificationException() {
+		return resultException;
+	}
+	
+	/*
+	 * 
+	 */
+	public void setResultCode(int newResultCode) {
+		resultCode = newResultCode;
+	}
+	
+	/*
+	 * 
+	 */
+	public void setResultException(Exception newResultException) {
+		resultException = newResultException;
+	}
+	
+	/*
+	 * 
+	 */
+	public int getVerificationCode() {
+		return verificationCode;
+	}
+
+	/*
+	 * 
+	 */
+	public void setVerificationCode(int verificationCode) {
+		this.verificationCode = verificationCode;
+	}
+
+	void setSignedContent(SignedContent signedContent) {
+		this.signedContent = signedContent;
+	}
+
+	public SignerInfo[] getSigners() {
+		return signedContent.getSignerInfos();
+	}
+
+	/*
+	 * Initializes the signerInfo and the VerifierInfo from the Certificate Pair
+	 */
+	private void initializeCertificates(){
+		X509Certificate certRoot = null;
+		X509Certificate certIssuer = null;
+		SignerInfo trustedSigner;
+		SignerInfo[] signers = getSigners();
+		if (signers.length == 0)
+			return;
+		trustedSigner = signers[0];
+		for (int i = 0; i < signers.length; i++) {
+			if (signers[i].isTrusted()) {
+				trustedSigner = signers[i];
+				break;
+			}
+		}
+		Certificate[] certs = trustedSigner.getCertificateChain();
+		if (certs == null || certs.length == 0)
+			return;
+		certRoot = (X509Certificate) certs[certs.length - 1];
+		certIssuer = (X509Certificate) certs[0];
+
+		StringBuffer strb = new StringBuffer();
+		strb.append(issuerString(certIssuer.getSubjectDN()));
+		strb.append("\r\n"); //$NON-NLS-1$
+		strb.append(NLS.bind(Messages.JarVerificationResult_ValidBetween, (new String[] { dateString(certIssuer.getNotBefore()), dateString(certIssuer.getNotAfter()) })));
+		strb.append(checkValidity(trustedSigner));
+		signerInfo = strb.toString();
+		if (certIssuer != null && !certIssuer.equals(certRoot)) {
+			strb = new StringBuffer();	
+			strb.append(issuerString(certIssuer.getIssuerDN()));
+			strb.append("\r\n"); //$NON-NLS-1$
+			strb.append(NLS.bind(Messages.JarVerificationResult_ValidBetween, (new String[] { dateString(certRoot.getNotBefore()), dateString(certRoot.getNotAfter()) }))); 
+			verifierInfo = strb.toString();
+		}
+
+	}
+
+	/*
+	 * Returns a String to show if the certificate is valid
+	 */
+	private String checkValidity(SignerInfo signer) {
+
+		try {
+			signedContent.checkValidity(signer);
+		} catch (CertificateExpiredException e) {
+			return ("\r\n" + Messages.JarVerificationResult_ExpiredCertificate);  //$NON-NLS-1$
+		} catch (CertificateNotYetValidException e) {
+			return ("\r\n" + Messages.JarVerificationResult_CertificateNotYetValid);  //$NON-NLS-1$
+		}
+		return ("\r\n" + Messages.JarVerificationResult_CertificateValid);  //$NON-NLS-1$
+	}
+
+	/*
+	 * Returns the label String from a X50name
+	 */
+	private String issuerString(Principal principal) {
+// 19902
+//		try {
+//			if (principal instanceof X500Name) {
+//				StringBuffer buf = new StringBuffer();
+//				X500Name name = (X500Name) principal;
+//				buf.append((name.getDNQualifier() != null) ? name.getDNQualifier() + ", " : "");
+//				buf.append(name.getCommonName());
+//				buf.append((name.getOrganizationalUnit() != null) ? ", " + name.getOrganizationalUnit() : "");
+//				buf.append((name.getOrganization() != null) ? ", " + name.getOrganization() : "");
+//				buf.append((name.getLocality() != null) ? ", " + name.getLocality() : "");
+//				buf.append((name.getCountry() != null) ? ", " + name.getCountry() : "");
+//				return new String(buf);
+//			}
+//		} catch (Exception e) {
+//			UpdateCore.warn("Error parsing X500 Certificate",e);
+//		}
+		return principal.toString();
+	}
+
+	/*
+	 * 
+	 */
+	private String dateString(Date date) {
+		return DateFormat.getDateInstance().format(date);
+	}
+
+	/*
+	 *
+	 */
+	public String getSignerInfo() {
+		if (signerInfo==null) initializeCertificates();
+		return signerInfo;
+	}
+
+	/*
+	 *
+	 */
+	public String getVerifierInfo() {
+		if (signerInfo==null) initializeCertificates();		
+		return verifierInfo;
+	}
+
+	/*
+	 *
+	 */
+	public ContentReference getContentReference() {
+		return contentReference;
+	}
+
+	/*
+	 * 
+	 */
+	public void setContentReference(ContentReference ref) {
+		this.contentReference = ref;
+	}
+
+
+	/*
+	 *
+	 */
+	public IFeature getFeature() {
+		return feature;
+	}
+
+	/*
+	 * 
+	 */
+	public void setFeature(IFeature feature) {
+		this.feature = feature;
+	}
+
+	/*
+	 * 
+	 */
+	public String getText() {
+		return null;
+	}
+
+
+	/*
+	 * 
+	 */
+	public boolean isFeatureVerification() {
+		return featureVerification;
+	}
+	
+	/*
+	 * 
+	 */
+	public void isFeatureVerification(boolean featureVerification) {
+		this.featureVerification = featureVerification;
+	}
+
+	/*
+	 *
+	 */
+	public boolean alreadySeen() {
+		return alreadySeen;
+	}
+
+	/*
+	 * 
+	 */
+	public boolean alreadySeen(boolean seen) {
+		return this.alreadySeen = seen;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/internal/verifier/CertVerifier.java b/update/org.eclipse.update.core/src/org/eclipse/update/internal/verifier/CertVerifier.java
new file mode 100644
index 0000000..50c8440
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/internal/verifier/CertVerifier.java
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.update.internal.verifier;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.jar.JarFile;
+import java.util.zip.ZipException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.signedcontent.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+
+/**
+ * The JarVerifier will check the integrity of the JAR.
+ * If the Jar is signed and the integrity is validated,
+ * it will check if one of the certificate of each file
+ * is in one of the keystore.
+ *
+ */
+
+public class CertVerifier extends Verifier {
+
+	private CertVerificationResult result;
+	private boolean acceptUnsignedFiles;
+	private IProgressMonitor monitor;
+	private File jarFile;
+	private SignedContentFactory factory;
+	private List trustedSignerInfos;
+
+	/*
+	 * Default Constructor
+	 */
+	public CertVerifier(SignedContentFactory factory) {
+		this.factory = factory;
+		initialize();
+	}
+
+
+	/*
+	 * 
+	 */
+	private void initialize() {
+		result = null;
+		acceptUnsignedFiles = false;
+	}
+
+	/*
+	 * init
+	 */
+	private void init(IFeature feature, ContentReference contentRef) throws CoreException {
+		jarFile = null;
+		if (contentRef instanceof JarContentReference) {
+			JarContentReference jarReference = (JarContentReference) contentRef;
+			try {
+				jarFile = jarReference.asFile();
+				if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
+					UpdateCore.debug("Attempting to read JAR file:"+jarFile); //$NON-NLS-1$
+			
+				// # of entries
+				if (!jarFile.exists()) throw new IOException();
+				JarFile jar = new JarFile(jarFile);
+				if (jar !=null){
+					try {
+						jar.close();
+					} catch (IOException ex) {
+						// unchecked
+					}
+				}
+			} catch (ZipException e){
+				throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_InvalidJar, (new String[] { jarReference.toString() })), e);				
+			} catch (IOException e) {
+				throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToAccessJar, (new String[] { jarReference.toString() })), e);
+			}
+		}
+
+		result = new CertVerificationResult();
+		result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
+		result.setResultException(null);
+		result.setFeature(feature);
+		result.setContentReference(contentRef);
+	}
+
+	/*
+	 * @param newMonitor org.eclipse.core.runtime.IProgressMonitor
+	 */
+	private void setMonitor(IProgressMonitor newMonitor) {
+		monitor = newMonitor;
+	}
+
+	/*
+	 * @see IVerifier#verify(IFeature,ContentReference,boolean, InstallMonitor)
+	 */
+	public IVerificationResult verify(
+		IFeature feature,
+		ContentReference reference,
+		boolean isFeatureVerification,
+		InstallMonitor monitor)
+		throws CoreException {
+
+		if (reference == null)
+			return result;
+
+		// if parent knows how to verify, ask the parent first
+		if (getParent() != null) {
+			IVerificationResult vr =
+				getParent().verify(feature, reference, isFeatureVerification, monitor);
+			if (vr.getVerificationCode() != IVerificationResult.TYPE_ENTRY_UNRECOGNIZED)
+				return vr;
+		}
+
+		// the parent couldn't verify
+		setMonitor(monitor);
+		init(feature, reference);
+		result.isFeatureVerification(isFeatureVerification);
+
+		if (jarFile!=null) {
+			result = verify(jarFile.getAbsolutePath(), reference.getIdentifier());
+		} else {
+			result.setVerificationCode(IVerificationResult.TYPE_ENTRY_UNRECOGNIZED);
+		}
+
+		return result;
+	}
+
+	/*
+	 * 
+	 */
+	private CertVerificationResult verify(String file, String identifier) {
+
+		try {
+			SignedContent verifier = factory.getSignedContent(new File(file));
+			// verify integrity
+			verifyIntegrity(verifier, identifier);
+
+			//if user already said yes
+			result.alreadySeen(alreadyValidated());
+
+			// save the fact the file is not signed, so the user will not be prompted again 
+			if (result.getVerificationCode()
+				== IVerificationResult.TYPE_ENTRY_NOT_SIGNED) {
+				acceptUnsignedFiles = true;
+			}
+
+		} catch (Exception e) {
+			result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
+			result.setResultException(e);
+		}
+
+		if (monitor != null) {
+			monitor.worked(1);
+			if (monitor.isCanceled()) {
+				result.setVerificationCode(IVerificationResult.VERIFICATION_CANCELLED);
+			}
+		}
+
+		return result;
+	}
+
+	/*
+	 * Verifies the integrity of the JAR
+	 */
+	private void verifyIntegrity(SignedContent verifier, String identifier) {
+		try {
+			if (verifier.isSigned()) {
+				// If the JAR is signed and invalid then mark as corrupted
+				if (hasValidContent(verifier.getSignedEntries())) {
+					result.setSignedContent(verifier);
+					SignerInfo[] signers = verifier.getSignerInfos();
+					for (int i = 0; i < signers.length; i++)
+						if (signers[i].isTrusted()) {
+							result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_RECOGNIZED);
+							break;
+						}
+					if (result.getVerificationCode() != IVerificationResult.TYPE_ENTRY_SIGNED_RECOGNIZED)
+						result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_UNRECOGNIZED);
+				} else
+					result.setVerificationCode(IVerificationResult.TYPE_ENTRY_CORRUPTED);
+			} else {
+				result.setVerificationCode(IVerificationResult.TYPE_ENTRY_NOT_SIGNED);
+				return;
+			}
+		} catch (Exception e) {
+			result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
+			result.setResultException(e);
+		}
+	}
+
+	private boolean hasValidContent(SignedContentEntry[] signedEntries) {
+		try {
+			for (int i = 0; i < signedEntries.length; i++)
+				signedEntries[i].verify();
+		} catch (InvalidContentException e) {
+			return false;
+		} catch (IOException e) {
+			return false;
+		}
+		return true;
+	}
+
+
+	/*
+	 * 
+	 */
+	private boolean alreadyValidated() {
+		int verifyCode = result.getVerificationCode();
+		if (verifyCode == IVerificationResult.TYPE_ENTRY_NOT_SIGNED)
+			return (acceptUnsignedFiles);
+		if (verifyCode == IVerificationResult.UNKNOWN_ERROR)
+			return false;
+		if (result.getSigners() != null) { //getTrustedCertificates() can't be null as it is lazy initialized
+			Iterator iter = getTrustedInfos().iterator();
+			SignerInfo[] signers = result.getSigners();
+
+			// check if this is not a user accepted certificate for this feature	
+			while (iter.hasNext()) {
+				SignerInfo chain = (SignerInfo) iter.next();
+				for (int i = 0; i < signers.length; i++)
+					if (chain.equals(signers[i]))
+						return true;
+			}
+
+			// if certificate pair not found in trusted add it for next time
+			for (int i = 0; i < signers.length; i++) {
+				addTrustedSignerInfo(signers[i]);
+			}
+		}
+
+		return false;
+	}
+
+	/*
+	 * 
+	 */
+	private void addTrustedSignerInfo(SignerInfo signer) {
+		if (trustedSignerInfos == null)
+			trustedSignerInfos = new ArrayList();
+		if (signer != null)
+			trustedSignerInfos.add(signer);
+	}
+
+	/*
+	 * 
+	 */
+	private List getTrustedInfos() {
+		if (trustedSignerInfos == null)
+			trustedSignerInfos = new ArrayList();
+		return trustedSignerInfos;
+	}
+
+	/**
+	 * @see IVerifier#setParent(IVerifier)
+	 */
+	public void setParent(IVerifier parentVerifier) {
+		super.setParent(parentVerifier);
+		initialize();
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IBatchOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IBatchOperation.java
new file mode 100644
index 0000000..db561f2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IBatchOperation.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * A batch operation is needed to execute a group of feature operations, such as installing a set of features.
+ * In fact, for installing features, it is recommended to wrap individual feature install operations as a 
+ * batch operation, to ensure proper validation and configuration saving.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IBatchOperation  extends IOperation {
+	/**
+	 * Returns the batched operations.
+	 * @return the batched operations
+	 */
+	public abstract IFeatureOperation[] getOperations();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IConfigFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IConfigFeatureOperation.java
new file mode 100644
index 0000000..4b5fcb2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IConfigFeatureOperation.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * Operation that enables a feature in a configuration.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+*/
+public interface IConfigFeatureOperation extends IFeatureOperation {
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IFeatureOperation.java
new file mode 100644
index 0000000..69ab740
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IFeatureOperation.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+
+/**
+ * An operation that applies to a feature, such as install, uninstall, etc.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IFeatureOperation extends IOperation {
+	/**
+	 * Returns the feature to operate on.
+	 * @return the feature to operate on.
+	 */
+	public abstract IFeature getFeature();
+	/**
+	 * Returns the site in which the operation is applied.
+	 * @return the site that owns or will own the feature.
+	 */
+	public abstract IConfiguredSite getTargetSite();
+	/**
+	 * Returns the previous version of the feature (if any).
+	 * @return the previous installed version of a feature (if any).
+	 */
+	public abstract IFeature getOldFeature();
+	/**
+	 * Sets the site in which the feature is being operated on.
+	 * @param targetSite the site in which the featre is being operated on.
+	 */
+	public abstract void setTargetSite(IConfiguredSite targetSite);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IInstallFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IInstallFeatureOperation.java
new file mode 100644
index 0000000..c946349
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IInstallFeatureOperation.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+import org.eclipse.update.core.*;
+
+/**
+ * An installation operation. This operation should not be executed by itself, it should be 
+ * aggregated into a IBatchOperation, together with other IInstallOperations, so that the
+ * validation checks are done on the group, not per installation job.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IInstallFeatureOperation extends IFeatureOperation {
+	/**
+	 * If the feature includes optional features, thi method returns the list 
+	 * of optional features to be installed.
+	 * @return the list of optional features to be installed.
+	 */
+	public IFeatureReference[] getOptionalFeatures();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperation.java
new file mode 100644
index 0000000..161518a
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperation.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+import java.lang.reflect.*;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * Base update manager operation.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IOperation {
+	/**
+	 * Returns true when the operation has been processed.
+	 * @return true when the operation completed.
+	 */
+	public abstract boolean isProcessed();
+	/**
+	 * Marks the operation as processed.
+	 */
+	public abstract void markProcessed();
+	/**
+	 * Executes operation.
+	 * @param pm Progress monitor for the operation
+	 * @param listener Operation listener
+	 * @return true if operation was successful and a restart is needed.
+	 * @throws CoreException
+	 * @throws InvocationTargetException
+	 */
+	public abstract boolean execute(IProgressMonitor pm, IOperationListener listener) throws CoreException, InvocationTargetException;
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationFactory.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationFactory.java
new file mode 100644
index 0000000..88d416f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationFactory.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+
+/**
+ * A factory interface for creating operations.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IOperationFactory {
+	/**
+	 * Creates an operation for configuring an installed feature in the specified site.
+	 * @param targetSite site containing the feature to configure
+	 * @param feature feature to be configured
+	 * @return the configure operation
+	 */
+	public IConfigFeatureOperation createConfigOperation(
+		IConfiguredSite targetSite,
+		IFeature feature);
+		
+	/**
+	 * Creates an operation for unconfiguring a feature
+	 * @param targetSite site containing the feature to unconfigure
+	 * @param feature feature to be unconfigured
+	 * @return the unconfigure operation
+	 */
+	public IUnconfigFeatureOperation createUnconfigOperation(
+		IConfiguredSite targetSite,
+		IFeature feature);
+	
+	/**
+	 * Creates an operation for installing a feature.
+	 * @param targetSite site in which the feature is to be installed
+	 * @param feature feature to be installed
+	 * @param optionalFeatures optionally included features to be installed (if any)
+	 * @param unconfiguredOptionalFeatures when installing optional features, some can be left unconfigured
+	 * @param verifier operation verification listener
+	 * @return the install operation
+	 */
+	public IInstallFeatureOperation createInstallOperation(
+		IConfiguredSite targetSite,
+		IFeature feature,
+		IFeatureReference[] optionalFeatures,
+		IFeature[] unconfiguredOptionalFeatures,
+		IVerificationListener verifier);
+		
+	/**
+	 * Creates an operation to uninstall a feature
+	 * @param targetSite site containing the feature to uninstall
+	 * @param feature feature to be uninstalled
+	 * @return the uninstall operation
+	 */
+	public IUninstallFeatureOperation createUninstallOperation(
+		IConfiguredSite targetSite,
+		IFeature feature);
+		
+	/**
+	 * Creates an operation for replacing this feature by a previous version
+	 * @param feature current feature
+	 * @param anotherFeature the new feature to be swapped in
+	 * @return the revert feature version operation
+	 */
+	public IConfigFeatureOperation createReplaceFeatureVersionOperation(
+		IFeature feature,
+		IFeature anotherFeature);
+		
+	/**
+	 * Creates an operation for executing a set of feature operation in batch mode
+	 * @param operations operation to execute in batch mode	
+	 * @return the batch operation
+	 */
+	public IBatchOperation createBatchInstallOperation(IInstallFeatureOperation[] operations);
+	
+	/**
+	 * Creates ann operation to configure/unconfigure an installation site (also known as enable/disable site)
+	 * @param site site to configure/unconfigure
+	 * @return the toggle site operation
+	 */
+	public IToggleSiteOperation createToggleSiteOperation(
+		IConfiguredSite site);
+		
+
+	/**
+	 * Creates an operation to revert to a previous installation configuration.
+	 * @param config configuration to revert to
+	 * @param problemHandler error handler
+	 * @return the revert operation
+	 */
+	public IRevertConfigurationOperation createRevertConfigurationOperation(
+		IInstallConfiguration config,
+		IProblemHandler problemHandler);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationListener.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationListener.java
new file mode 100644
index 0000000..8d15625
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationListener.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * Listener for the operation lifecycle. This allows listeners to execute certain code before an operation
+ * starts, or after it completes.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IOperationListener {
+	/**
+	 * May be called before an operation starts executing.
+	 * @param operation operation to listen to
+	 * @param data info specific to the operation
+	 * @return not used
+	 */
+	public boolean beforeExecute(IOperation operation, Object data);
+	/**
+	 * May be called after an operation finishes executing.
+	 * @param operation operation to listen to 
+	 * @param data info specific to the operation
+	 * @return not used
+	 */
+	public boolean afterExecute(IOperation operation, Object data);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationValidator.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationValidator.java
new file mode 100644
index 0000000..31cf00b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IOperationValidator.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+
+/**
+ * This class contains various validation methods to be invoked before or during executing update manager
+ * operations.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IOperationValidator {
+
+	/**
+	 * Called before performing install.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePendingInstall(IFeature oldFeature, IFeature newFeature);
+
+	/**
+	 * Called before performing operation.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePendingConfig(IFeature feature);
+	
+	/**
+	 * Called before performing operation.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePendingUnconfig(IFeature feature);
+	
+	/**
+	 * Called before performing operation.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePendingReplaceVersion(IFeature feature, IFeature anotherFeature);
+	
+	/**
+	 * Called before doing a revert/ restore operation
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePendingRevert(IInstallConfiguration config);
+
+	/**
+	 * Called by the UI before doing a batched processing of
+	 * several pending changes.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePendingChanges(IInstallFeatureOperation[] jobs);
+
+	/**
+	 * Check the current state.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validateCurrentState();
+	
+	/**
+	 * Checks if the platform configuration has been modified outside this program.
+	 * @return the error status, or null if no errors
+	 */
+	public IStatus validatePlatformConfigValid();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IReplaceFeatureVersionOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IReplaceFeatureVersionOperation.java
new file mode 100644
index 0000000..d52e5c4
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IReplaceFeatureVersionOperation.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * Operation that replaces a current feature by another version.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IReplaceFeatureVersionOperation extends IFeatureOperation {
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IRevertConfigurationOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IRevertConfigurationOperation.java
new file mode 100644
index 0000000..90fabc9
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IRevertConfigurationOperation.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * An operation in which the current configuration is replaced by a previous one.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IRevertConfigurationOperation extends IOperation {
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IToggleSiteOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IToggleSiteOperation.java
new file mode 100644
index 0000000..f85833b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IToggleSiteOperation.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * An operation in which the unconfigured/configured state of a site is toggled.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IToggleSiteOperation extends IOperation {
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUnconfigFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUnconfigFeatureOperation.java
new file mode 100644
index 0000000..53b88e3
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUnconfigFeatureOperation.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * An operation that disables (unconfigure) a feature in a site.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUnconfigFeatureOperation  extends IFeatureOperation {
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUninstallFeatureOperation.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUninstallFeatureOperation.java
new file mode 100644
index 0000000..f5c7080
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUninstallFeatureOperation.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+
+/**
+ * A feature uninstall operation. The feature is removed from the disk when uninstalled.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUninstallFeatureOperation extends IFeatureOperation {
+	public final static String UNINSTALL = "uninstall"; //$NON-NLS-1$
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUpdateModelChangedListener.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUpdateModelChangedListener.java
new file mode 100644
index 0000000..b9dbe91
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/IUpdateModelChangedListener.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+/**
+ * Listener for update model changes. 
+ * Usually, when features are installed, configured, etc.
+ * a GUI may need to update its state, so it will have to register
+ * with the OperationsManager for update events.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateModelChangedListener {
+	/**
+	 * Called after a feature/site/etc. is added
+	 * @param parent parent object
+	 * @param children added children
+	 */
+	public void objectsAdded(Object parent, Object [] children);
+	/**
+	 * Called after a feature/site/etc. is removed.
+	 * @param parent parent object
+	 * @param children removed children
+	 */
+	public void objectsRemoved(Object parent, Object [] children);
+	/**
+	 * Called when there are changes to a site/feature/etc.
+	 * @param object object that changed
+	 * @param property object property that changed
+	 */
+	public void objectChanged(Object object, String property);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/OperationsManager.java b/update/org.eclipse.update.core/src/org/eclipse/update/operations/OperationsManager.java
new file mode 100644
index 0000000..50cff42
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/OperationsManager.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.operations;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.configurator.*;
+import org.eclipse.update.internal.operations.*;
+
+/**
+ * Entry point for update manager operations. Use this class to obtain the factory that creates
+ * update manager operations, or to get the operation validator.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class OperationsManager {
+	private static IOperationValidator validator;
+	private static IOperationFactory operationFactory;
+	private static Vector listeners = new Vector();
+	private static Vector pendingOperations = new Vector();
+	
+	private static boolean inProgress;
+
+	private OperationsManager() {
+	}
+	
+	/**
+	 * Each update operations must be created by the operation factory. 
+	 * Use this method to obtain the factory.
+	 * @return returns the operation factory
+	 */
+	public static IOperationFactory getOperationFactory() {
+		if (operationFactory == null)
+			operationFactory = new OperationFactory();
+		return operationFactory;
+	}
+
+	/**
+	 * Check if the feature is the subject of an update operation such as install,
+	 * configure, etc. and return it. Currently there can only be one pending
+	 * operation on a feature.
+	 * @param feature feature to check for pending operations
+	 * @return pending operation if any, otherwise null.
+	 */
+	public static IFeatureOperation findPendingOperation(IFeature feature) {
+		for (int i = 0; i < pendingOperations.size(); i++) {
+			IFeatureOperation operation =
+				(IFeatureOperation) pendingOperations.elementAt(i);
+			if (operation.getFeature().equals(feature))
+				return operation;
+		}
+		return null;
+	}
+	
+	/**
+	 * Register a pending operation.
+	 * @param operation pending operation
+	 */
+	public static void addPendingOperation(IOperation operation) {
+		pendingOperations.add(operation);
+		//fireObjectsAdded(this, new Object[] { change });
+	}
+
+	/**
+	 * Unregister a pending operation.
+	 * @param operation pending operation
+	 */
+	public static void removePendingOperation(IOperation operation) {
+		pendingOperations.remove(operation);
+		//fireObjectsRemoved(this, new Object[] { change });
+	}
+
+	/**
+	 * Adds a model changed listener.
+	 * @param listener update model change listener
+	 */
+	public static void addUpdateModelChangedListener(IUpdateModelChangedListener listener) {
+		if (!listeners.contains(listener))
+			listeners.add(listener);
+	}
+
+	/**
+	 * Removes an model changed listener.
+	 * @param listener update model change listener
+	 */
+	public static void removeUpdateModelChangedListener(IUpdateModelChangedListener listener) {
+		if (listeners.contains(listener))
+			listeners.remove(listener);
+	}
+
+	/**
+	 * Notifies model changed listeners when features/sites/etc. are added.
+	 * @param parent parent object
+	 * @param children children added
+	 */
+	public static void fireObjectsAdded(Object parent, Object[] children) {
+		for (Iterator iter = listeners.iterator(); iter.hasNext();) {
+			IUpdateModelChangedListener listener =
+				(IUpdateModelChangedListener) iter.next();
+			listener.objectsAdded(parent, children);
+		}
+	}
+
+	/**
+	 * Notifies model changed listeners when features/sites/etc are removed.
+	 * @param parent parent object
+	 * @param children children removed
+	 */
+	public static void fireObjectsRemoved(Object parent, Object[] children) {
+		for (Iterator iter = listeners.iterator(); iter.hasNext();) {
+			IUpdateModelChangedListener listener =
+				(IUpdateModelChangedListener) iter.next();
+			listener.objectsRemoved(parent, children);
+		}
+	}
+
+	/**
+	 * Notifies model changed listeners when features/sites/etc. have changed.
+	 * @param object changed object
+	 * @param property changed object property
+	 */
+	public static void fireObjectChanged(Object object, String property) {
+		for (Iterator iter = listeners.iterator(); iter.hasNext();) {
+			IUpdateModelChangedListener listener =
+				(IUpdateModelChangedListener) iter.next();
+			listener.objectChanged(object, property);
+		}
+	}
+
+	/**
+	 * Returns true when any of the install operations requires a license agreement.
+	 * @param jobs features to install
+	 * @return true when any of the features to install have a license
+	 */
+	public static boolean hasSelectedJobsWithLicenses(IInstallFeatureOperation[] jobs) {
+		for (int i = 0; i < jobs.length; i++) {
+			if (UpdateUtils.hasLicense(jobs[i].getFeature()))
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Returns true when any of the features to install has optional features.
+	 * @param jobs features to install
+	 * @return true when any of the features has optional features
+	 */
+	public static boolean hasSelectedJobsWithOptionalFeatures(IInstallFeatureOperation[] jobs) {
+		for (int i = 0; i < jobs.length; i++) {
+			if (UpdateUtils.hasOptionalFeatures(jobs[i].getFeature()))
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Returns the list of operations that need a license agreement.
+	 * @param jobs features to install
+	 * @return the list of operation that need a license agreement
+	 */
+	public static IInstallFeatureOperation[] getSelectedJobsWithLicenses(IInstallFeatureOperation[] jobs) {
+		ArrayList list = new ArrayList();
+		for (int i = 0; i < jobs.length; i++) {
+			if (UpdateUtils.hasLicense(jobs[i].getFeature()))
+				list.add(jobs[i]);
+		}
+		return (IInstallFeatureOperation[]) list.toArray(
+			new IInstallFeatureOperation[list.size()]);
+	}
+
+	/**
+	 * Returns the list of operations that have optional features to install.
+	 * @param jobs features to install
+	 * @return list of operations that have optional features to install
+	 */
+	public static IInstallFeatureOperation[] getSelectedJobsWithOptionalFeatures(IInstallFeatureOperation[] jobs) {
+		ArrayList list = new ArrayList();
+		for (int i = 0; i < jobs.length; i++) {
+			if (UpdateUtils.hasOptionalFeatures(jobs[i].getFeature()))
+				list.add(jobs[i]);
+		}
+		return (IInstallFeatureOperation[]) list.toArray(
+			new IInstallFeatureOperation[list.size()]);
+	}
+	
+	/**
+	 * Sets whether any operations is in progress.
+	 * @param inProgress true when operation is in progress
+	 */
+	public static synchronized void setInProgress(boolean inProgress) {
+		OperationsManager.inProgress = inProgress;
+	}
+	
+	/**
+	 * Returns true when some operation is being executed, false otherwise.
+	 * @return true when some operation execution is in progress, false otherwise
+	 */
+	public static synchronized boolean isInProgress() {
+		return inProgress;
+	}
+
+	/**
+	 * Returns the operations validator.
+	 * @return the operation validator
+	 */
+	public static IOperationValidator getValidator() {
+		if (validator == null)
+			validator = new OperationValidator();
+		return validator;
+	}
+	
+	/**
+	 * Sets a custom operation validator
+	 * @param validator the custom validator
+	 */
+	public static void setValidator(IOperationValidator validator) {
+		OperationsManager.validator = validator;
+	}
+	
+	/**
+	 * Applies the changes made to the current configuration. 
+	 * Care must be taken when using this method. Normally, if you install a new
+	 * plugin it is safe to do it.
+	 */
+	public static void applyChangesNow() {
+		ConfigurationActivator configurator = ConfigurationActivator.getConfigurator();
+		configurator.installBundles();
+		pendingOperations.clear();
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/operations/package.html b/update/org.eclipse.update.core/src/org/eclipse/update/operations/package.html
new file mode 100644
index 0000000..0588f6e
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/operations/package.html
@@ -0,0 +1,20 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.72 [en] (Windows NT 5.0; U) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides support for performing various install/update operations.
+<h2>
+Package Specification</h2>
+This package contains interfaces for performing update/install operations 
+without using the Update Manager user interface. 
+<p>
+<b>Note:</b> This package has been deprecated and will be deleted in a future
+release.  See bug 311590 for details.
+</p>
+</body>
+</html>
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/BackLevelFilter.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/BackLevelFilter.java
new file mode 100644
index 0000000..b5c3dc0
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/BackLevelFilter.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.operations.*;
+
+/**
+ * This class can be added to the update search request
+ * to filter out features that are back-level (are
+ * older or the same as the features already installed).
+ * 
+ * <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>
+ * @see UpdateSearchRequest
+ * @see IUpdateSearchFilter
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class BackLevelFilter extends BaseFilter {
+	
+	public boolean accept(IFeature match) {
+		
+			PluginVersionIdentifier matchVid = match.getVersionedIdentifier().getVersion();
+			IFeature [] installed = UpdateUtils.getInstalledFeatures(match.getVersionedIdentifier(), false);
+			if (installed.length==0) return true;
+			
+			for (int i=0; i<installed.length; i++) {
+				PluginVersionIdentifier ivid = installed[i].getVersionedIdentifier().getVersion();
+				if (matchVid.isGreaterThan(ivid))
+					continue;
+				// installed version is the same or newer than
+				// the match - filter out
+				return false;
+			}
+			return true;
+		
+	}
+	
+	public boolean accept(IFeatureReference match) {		
+		try {
+			PluginVersionIdentifier matchVid = match.getVersionedIdentifier().getVersion();
+			IFeature [] installed = UpdateUtils.getInstalledFeatures(match.getVersionedIdentifier(), false);
+			if (installed.length==0) return true;
+			
+			for (int i=0; i<installed.length; i++) {
+				PluginVersionIdentifier ivid = installed[i].getVersionedIdentifier().getVersion();
+				if (matchVid.isGreaterThan(ivid))
+					continue;
+				// installed version is the same or newer than
+				// the match - filter out
+				return false;
+			}
+			return true;
+		} catch (CoreException e) {
+			return false;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/BaseFilter.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/BaseFilter.java
new file mode 100644
index 0000000..1d374e1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/BaseFilter.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import org.eclipse.update.core.*;
+
+/**
+ * Base filter class
+ * 
+ * <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>
+ * @see UpdateSearchRequest
+ * @see IUpdateSearchFilter
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class BaseFilter implements IUpdateSearchFilter {
+    /**
+     * @deprecated In 3.1 only the accept (IFeatureReference) will be used
+     */
+	public boolean accept(IFeature match) {
+		return true;
+	}
+	public boolean accept(IFeatureReference match) {
+		return true;
+	}	
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/EnvironmentFilter.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/EnvironmentFilter.java
new file mode 100644
index 0000000..f9aa1f6
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/EnvironmentFilter.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * This class can be added to the update search request
+ * to filter out features that do not match the current
+ * environment settings.
+ * 
+ * <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>
+ * @see UpdateSearchRequest
+ * @see IUpdateSearchFilter
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class EnvironmentFilter extends BaseFilter {
+	
+	public boolean accept(IFeature match) {
+		return UpdateManagerUtils.isValidEnvironment(match);
+	}
+	
+	public boolean accept(IFeatureReference match) {
+		return UpdateManagerUtils.isValidEnvironment(match);
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IQueryUpdateSiteAdapter.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IQueryUpdateSiteAdapter.java
new file mode 100644
index 0000000..c96ffa1
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IQueryUpdateSiteAdapter.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+/**
+ * This interface is used for update site adapter used 
+ * for specific query searches. It adds a mapping ID 
+ * that can be used when mapping file is specified.
+ * If a matching mapping is found for this ID, 
+ * the replacement URL found in the mapping file will be
+ * used instead of this adapter.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IQueryUpdateSiteAdapter extends IUpdateSiteAdapter {
+/**
+ * Returns an ID that can be used for matching against the information in the address mapping file.
+ * @return a mapping Id to compare against the address mapping file.
+ */
+	public String getMappingId();
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchCategory.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchCategory.java
new file mode 100644
index 0000000..f0a52e3
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchCategory.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+/**
+ * This interface is used to encapsulate a particular Update
+ * search pattern. Each search category is free to scan
+ * sites using a specific algorithm. Search category must
+ * have a unique ID. The actual search is performed in
+ * search queries. A category can provide one or more
+ * queries to run during the search.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateSearchCategory {
+/**
+ * Returns the unique identifier of this search category.
+ */
+	public String getId();
+/**
+ * Accepts the identifier assigned to this category during
+ * the registry reading.
+ */
+	public void setId(String id);
+
+/**
+ * Returns an array of update search queries that need to 
+ * be run during the search.
+ * @return an arry of update search queries
+ */	
+	IUpdateSearchQuery [] getQueries();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchFilter.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchFilter.java
new file mode 100644
index 0000000..68df1d5
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchFilter.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import org.eclipse.update.core.*;
+
+/**
+ * Classes that implement this interface can be used to filter the
+ * results of the update search.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateSearchFilter {
+	/**
+	 * Tests a feature according to this filter's criteria.
+	 * @param match the feature to test
+	 * @return <samp>true</samp> if the feature has been accepted, <samp>false</samp> otherwise.
+     * @deprecated In 3.1 only the accept (IFeatureReference) will be used
+	 */
+	boolean accept(IFeature match);
+
+	/**
+	 * Tests a feature reference according to this filter's criteria. 
+	 * This is a prefilter that allows rejecting a feature before a potentially lengthy download.
+	 * @param match the feature reference to test
+	 * @return <samp>true</samp> if the feature reference has been accepted, <samp>false</samp> otherwise.
+	 */
+	boolean accept(IFeatureReference match);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchQuery.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchQuery.java
new file mode 100644
index 0000000..23023e8
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchQuery.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+
+/**
+ * A search query.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateSearchQuery {
+/**
+ * Returns an update site adapter that should be added to the scope
+ * while running this query. Some search category may need to 
+ * scan specific site adapter in addition to those specified in 
+ * the search scope.
+ * 
+ * @return the query-specific site adapter or <samp>null</samp> if
+ * not specified.
+ * 
+ */
+	public IQueryUpdateSiteAdapter getQuerySearchSite();
+
+/**
+ * Executes the query. The implementors should scan the provided
+ * update site (skipping certain categories if provided) and
+ * pass the matches to the result collector. The query is also
+ * responsible for scoping and moving the provided progress monitor.
+ * 
+ * @param site the update site to scan
+ * @param categoriesToSkip an array of category names that need to be skipped or <samp>null</samp> if categories should not be taken into account.
+ * @param filter a filter to apply before passing the match to collector
+ * @param collector an object that is used for reporting search results
+ * @param monitor a progress monitor to report search progress within the provided site
+ */
+	public void run(ISite site, String [] categoriesToSkip, IUpdateSearchFilter filter, IUpdateSearchResultCollector collector, IProgressMonitor monitor);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchResultCollector.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchResultCollector.java
new file mode 100644
index 0000000..329c5cf
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchResultCollector.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import org.eclipse.update.core.*;
+
+/**
+ * Search results are collected by implementing this interface
+ * and passing it to the search request. If the implementation is
+ * visual, it is recommended that the match is shown as soon
+ * as it is collected (rather than kept in a list and presented
+ * at the end of the search).
+  * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateSearchResultCollector {
+/**
+ * Called when a matching feature has been found during
+ * the search.
+ * @param match the matching feature
+ */
+   void accept(IFeature match);
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchResultCollectorFromMirror.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchResultCollectorFromMirror.java
new file mode 100644
index 0000000..a69c790
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchResultCollectorFromMirror.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.update.core.ISiteWithMirrors;
+import org.eclipse.update.core.IURLEntry;
+
+/**
+ * Search results are collected by implementing this interface
+ * and passing it to the search request. If the implementation is
+ * visual, it is recommended that the match is shown as soon
+ * as it is collected (rather than kept in a list and presented
+ * at the end of the search). This interface should be implemented 
+ * when you want to support collection of results from a mirror site,
+ * otherwise you can just implement the IUpdateSearchResultsCollector.
+  * <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>
+ * @since 3.1
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateSearchResultCollectorFromMirror extends
+		IUpdateSearchResultCollector {
+
+	/**
+	 * Returns a mirror of the specified site. Normally, if the site defines some mirrors,
+	 * this method can be implement so that it prompts the user to pick one of the mirrors.
+	 * @param site the site to get the mirror for
+	 * @param siteName the name of the site
+	 * @return a mirror (url+label) for the specified site, or null if no mirror is needed
+	 * @throws OperationCanceledException if the user chooses to cancel
+	 * the prompt instead of choosing the mirror from the list.
+	 */
+	public IURLEntry getMirror(ISiteWithMirrors site, String siteName) throws OperationCanceledException;
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchSite.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchSite.java
new file mode 100644
index 0000000..e4b7fb7
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSearchSite.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+/**
+ * This interface is used to represent sites that need to be
+ * searched within the search scope. In addition to being an
+ * update site adapter, it also returns an array of categories
+ * within the site that need not be searched (skipping categories
+ * makes the search faster because fewer features need to
+ * be checked and potentially downloaded from the server).
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateSearchSite extends IUpdateSiteAdapter {
+/**
+ * Returns an array of categories that need not be searched
+ * when scanning this site or <samp>null</samp> if all the
+ * features must be tested.
+ * @return an array of category names or <samp>null</samp> if
+ * all the features must be tested.
+ */
+	String[] getCategoriesToSkip();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSiteAdapter.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSiteAdapter.java
new file mode 100644
index 0000000..d1539a8
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/IUpdateSiteAdapter.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import java.net.*;
+
+/**
+ * This interface wraps an update site URL and adds 
+ * a presentation label. It is used to encapsulate sites that need
+ * to be visited during the update search.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public interface IUpdateSiteAdapter {
+	/**
+	 * Returns the presentation string that can be used
+	 * for this site.
+	 * @return the update site label
+	 */
+	public String getLabel();
+	/**
+	 * Returns the URL of the update site.
+	 * @return the URL of the update site.
+	 */
+	public URL getURL();
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/UpdateSearchRequest.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/UpdateSearchRequest.java
new file mode 100644
index 0000000..d5a7481
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/UpdateSearchRequest.java
@@ -0,0 +1,520 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.IFeatureReference;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.ISiteWithMirrors;
+import org.eclipse.update.core.IURLEntry;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.internal.core.ExtendedSite;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.operations.UpdateUtils;
+import org.eclipse.update.internal.search.SiteSearchCategory;
+import org.eclipse.update.internal.search.UpdatePolicy;
+import org.eclipse.update.internal.search.UpdateSiteAdapter;
+import org.eclipse.update.internal.search.UpdatesSearchCategory;
+
+/**
+ * This class is central to update search. The search pattern
+ * is encapsulated in update search category, while the search
+ * scope is defined in the scope object. When these two objects
+ * are defined and set, search can be performed using the 
+ * provided method. Search results are reported to the
+ * result collector, while search progress is tracked using
+ * the progress monitor.
+ * <p>Classes that implement <samp>IUpdateSearchResultCollector</samp>
+ * should call 'accept' to test if the match should be
+ * accepted according to the filters added to the request.
+ * 
+ * <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>
+ * @see UpdateSearchScope
+ * @see IUpdateSearchCategory
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class UpdateSearchRequest {
+	private IUpdateSearchCategory category;
+	private UpdateSearchScope scope;
+	private boolean searchInProgress = false;
+	private AggregateFilter aggregateFilter = new AggregateFilter();
+	
+	private static class UpdateSearchSite
+	extends UpdateSiteAdapter
+	implements IUpdateSearchSite {
+	private String[] categoriesToSkip;
+
+	public UpdateSearchSite(
+		String label,
+		URL siteURL,
+		String[] categoriesToSkip) {
+		super(label, siteURL);
+		this.categoriesToSkip = categoriesToSkip;
+	}
+	public String[] getCategoriesToSkip() {
+		return categoriesToSkip;
+	}
+}
+
+	class MirroredUpdateSiteAdapter extends UpdateSiteAdapter {
+		public MirroredUpdateSiteAdapter(IURLEntry mirror) {
+			super(mirror.getAnnotation(), mirror.getURL());
+		}
+	}
+	
+	class AggregateFilter implements IUpdateSearchFilter {
+		private ArrayList filters;
+		public void addFilter(IUpdateSearchFilter filter) {
+			if (filters == null)
+				filters = new ArrayList();
+			if (filters.contains(filter) == false)
+				filters.add(filter);
+		}
+		
+		public void removeFilter(IUpdateSearchFilter filter) {
+			if (filters == null)
+				return;
+			filters.remove(filter);
+		}
+	
+        /**
+         * @deprecated In 3.1 only the accept (IFeatureReference) will be used
+         */
+		public boolean accept(IFeature match) {
+			if (filters == null)
+				return true;
+			for (int i = 0; i < filters.size(); i++) {
+				IUpdateSearchFilter filter = (IUpdateSearchFilter) filters.get(i);
+				if (filter.accept(match) == false)
+					return false;
+			}
+			return true;
+		}
+		
+		public boolean accept(IFeatureReference match) {
+			if (filters == null)
+				return true;
+			for (int i = 0; i < filters.size(); i++) {
+				IUpdateSearchFilter filter = (IUpdateSearchFilter) filters.get(i);
+				if (filter.accept(match) == false)
+					return false;
+			}
+			return true;
+		}
+	}
+
+	/**
+	 * Returns an updates search category for use in discovering updates
+	 * to existing function on update sites.
+	 * 
+	 * @return an updates search category
+	 * @since 3.1
+	 */
+	public static IUpdateSearchCategory createDefaultUpdatesSearchCategory() {
+		return new UpdatesSearchCategory();
+	}
+	
+	/**
+	 * Returns a site search category for use in discovering new function on update sites.
+	 * 
+	 * @return a site search category
+	 * @since 3.1
+	 */
+	public static IUpdateSearchCategory createDefaultSiteSearchCategory() {
+		return new SiteSearchCategory();
+	}
+	
+	/**
+	 * The constructor that accepts the search category and 
+	 * scope objects.
+	 * @param category the actual search pattern that should be applied
+	 * @param scope a list of sites that need to be scanned during the search
+	 */
+	public UpdateSearchRequest(
+		IUpdateSearchCategory category,
+		UpdateSearchScope scope) {
+		this.category = category;
+		this.scope = scope;
+	}
+	/**
+	 * Returns the search catagory used in this request.
+	 * @return the search category
+	 */
+	
+	public IUpdateSearchCategory getCategory() {
+		return category;
+	}
+	
+	/**
+	 * Returns the scope of this search request.
+	 * @return search scope
+	 */
+	
+	public UpdateSearchScope getScope() {
+		return scope;
+	}
+	/**
+	 * Adds a filter to this request. This method does nothing
+	 * if search is alrady in progress. 
+	 * @param filter the filter 
+	 * @see UpdateSearchRequest#removeFilter
+	 */
+	public void addFilter(IUpdateSearchFilter filter) {
+		if (searchInProgress)
+			return;
+		aggregateFilter.addFilter(filter);
+	}
+	/**
+	 * Removes the filter from this request. This method does
+	 * nothing if search is alrady in progress.
+	 * @param filter the filter to remove
+	 * @see UpdateSearchRequest#addFilter
+	 */
+
+	public void removeFilter(IUpdateSearchFilter filter) {
+		if (searchInProgress)
+			return;
+		aggregateFilter.removeFilter(filter);
+	}
+
+	/**
+	 * Sets the scope object. It is possible to reuse the search request
+	 * object by modifying the scope and re-running the search.
+	 * @param scope the new search scope
+	 */
+	public void setScope(UpdateSearchScope scope) {
+		this.scope = scope;
+	}
+	/**
+	 * Tests whether this search request is current running.
+	 * @return <samp>true</samp> if the search is currently running, <samp>false</samp> otherwise.
+	 */
+	public boolean isSearchInProgress() {
+		return searchInProgress;
+	}
+
+	/**
+	 * Runs the search using the category and scope configured into
+	 * this request. As results arrive, they are passed to the
+	 * search result collector object.
+	 * @param collector matched features are passed to this object
+	 * @param monitor used to track the search progress
+	 * @throws CoreException
+	 */
+	public void performSearch(
+		IUpdateSearchResultCollector collector,
+		IProgressMonitor monitor)
+		throws CoreException, OperationCanceledException {
+		
+		ArrayList statusList = new ArrayList();
+
+		searchInProgress = true;
+		IUpdateSearchQuery[] queries = category.getQueries();
+		IUpdateSearchSite[] candidates = scope.getSearchSites();
+		Set visitedSitesURL = new HashSet();
+		Set visitedSites = new HashSet();
+		for(int i = 0; i < candidates.length; i++) {
+			visitedSitesURL.add(candidates[i].getURL());
+			//visitedSites.add(candidates[i]);
+		}
+		URL updateMapURL = scope.getUpdateMapURL();
+		boolean searchFeatureProvidedSites = scope.isFeatureProvidedSitesEnabled();
+
+		if (!monitor.isCanceled()) {
+			
+			int nsearchsites = 0;
+			try {
+			for (int i = 0; i < queries.length; i++) {
+				if (queries[i].getQuerySearchSite() != null)
+					nsearchsites++;
+			}
+			} catch (Throwable t) {
+				t.printStackTrace();
+				
+			}
+			
+			int ntasks = nsearchsites + queries.length * candidates.length;
+			if (updateMapURL!=null) ntasks++;
+
+			monitor.beginTask(Messages.UpdateSearchRequest_searching, ntasks); 
+			
+			try {
+				UpdatePolicy updatePolicy=null;
+				if (updateMapURL!=null) {
+					updatePolicy = new UpdatePolicy();
+					IStatus status =UpdateUtils.loadUpdatePolicy(updatePolicy, updateMapURL, new SubProgressMonitor(monitor, 1));
+					if (status != null)
+						statusList.add(status);
+				}
+				
+				List combinedAssociateSites = new ArrayList();
+				for (int i = 0; i < queries.length; i++) {
+					IUpdateSearchQuery query = queries[i];
+					IQueryUpdateSiteAdapter qsite = query.getQuerySearchSite();
+					// currently, the next conditional is only executed (qsite!=null) when
+					// running an update search. 
+					if (qsite != null && searchFeatureProvidedSites) {
+						// do not update features that are installed in read-only locations
+						if (query instanceof UpdatesSearchCategory.UpdateQuery) {
+							IFeature feature = ((UpdatesSearchCategory.UpdateQuery)query).getFeature();
+							if (feature != null && !feature.getSite().getCurrentConfiguredSite().verifyUpdatableStatus().isOK())
+								continue;
+						}
+						// check for mapping
+						IUpdateSiteAdapter mappedSite = getMappedSite(updatePolicy, qsite);
+						// when there is no mapped site the feature is not updatable
+						if (mappedSite == null || mappedSite.getURL() == null)
+							continue;
+						SubProgressMonitor subMonitor =
+							new SubProgressMonitor(monitor, 1);
+						List associateSites = new ArrayList();
+						IStatus status =
+							searchOneSite(
+								mappedSite,
+								null,
+								query,
+								collector,
+								associateSites,
+								subMonitor,
+								true);
+						if (status != null)
+							statusList.add(status);
+						if (monitor.isCanceled())
+							break;
+						combinedAssociateSites = combineAssociateSites( combinedAssociateSites, associateSites, visitedSitesURL, visitedSites);
+					}
+					
+					for (int j = 0; j < candidates.length; j++) {
+						if (monitor.isCanceled()) {
+							break;
+						}
+						IUpdateSearchSite source = candidates[j];
+						SubProgressMonitor subMonitor =
+							new SubProgressMonitor(monitor, 1);
+						List associateSites = new ArrayList();
+						IStatus status =
+							searchOneSite(
+								source,
+								source.getCategoriesToSkip(),
+								query,
+								collector,
+								associateSites,
+								subMonitor,
+								true);
+						if (status != null)
+							statusList.add(status);
+						combinedAssociateSites = combineAssociateSites( combinedAssociateSites, associateSites, visitedSitesURL, visitedSites);
+					}
+					if (monitor.isCanceled())
+						break;
+					
+					
+					for(int associateSitesDepth = 0; associateSitesDepth < 5; associateSitesDepth++) {
+						List tempCombinedSites = new ArrayList();
+						Iterator combinedAssociateSitesIterator = combinedAssociateSites.iterator();
+						while(combinedAssociateSitesIterator.hasNext()) {
+							
+							IUpdateSearchSite source = (IUpdateSearchSite)combinedAssociateSitesIterator.next();
+							
+							List associateSites = new ArrayList();
+							SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1);
+							IStatus status =
+								searchOneSite(
+									source,
+									source.getCategoriesToSkip(),
+									query,
+									collector,
+									associateSites,
+									subMonitor,
+									true);
+							combinedAssociateSites = combineAssociateSites( tempCombinedSites, associateSites, visitedSitesURL, visitedSites);
+							if (status != null)
+								statusList.add(status);
+						}	
+						combinedAssociateSites = tempCombinedSites;
+						
+					}
+					if (monitor.isCanceled())
+						break;
+				}
+			} catch (CoreException e) {
+				searchInProgress = false;
+				monitor.done();
+				throw e;
+			}
+		}
+		searchInProgress = false;
+		monitor.done();
+		
+
+		Iterator visitedSitesIterator = visitedSites.iterator();
+		while (visitedSitesIterator.hasNext()) {
+			IUpdateSearchSite associateSite = (IUpdateSearchSite)visitedSitesIterator.next();
+			scope.addSearchSite(associateSite.getLabel(), associateSite.getURL(), null);
+		}
+		if (statusList.size() > 0) {
+			if (statusList.size()==1 && ((IStatus)statusList.get(0)).getSeverity()==IStatus.CANCEL)
+				throw new OperationCanceledException();
+			IStatus[] children =
+				(IStatus[]) statusList.toArray(new IStatus[statusList.size()]);
+			MultiStatus multiStatus =
+				new MultiStatus(
+					"org.eclipse.update.core", //$NON-NLS-1$
+					ISite.SITE_ACCESS_EXCEPTION,
+					children,
+					Messages.Search_networkProblems, 
+					null);
+			
+			throw new CoreException(multiStatus);
+		}
+	}
+
+
+	private List combineAssociateSites(List combinedAssociateSites, List associateSites, Set visitedSitesURL, Set visitedSites) {
+		Iterator iterator = associateSites.iterator();
+
+		while(iterator.hasNext()) {
+			UpdateSearchSite associateSite = (UpdateSearchSite)iterator.next();
+			if ( !visitedSitesURL.contains(associateSite.getURL())) {
+				combinedAssociateSites.add(associateSite);
+				visitedSitesURL.add(associateSite.getURL());
+				visitedSites.add(associateSite);
+			}
+			
+		}
+		return combinedAssociateSites;
+	}
+
+
+/*
+ * See if this query site adapter is mapped in the map file
+ * to a different URL.
+ */
+	private IUpdateSiteAdapter getMappedSite(UpdatePolicy policy, IQueryUpdateSiteAdapter qsite) {
+		if (policy!=null && policy.isLoaded()) {
+			IUpdateSiteAdapter mappedSite = policy.getMappedSite(qsite.getMappingId());
+			if (mappedSite!=null) 
+				return mappedSite;
+			else // no match - use original site if fallback allowed, or nothing.
+				return policy.isFallbackAllowed()? qsite : null;
+		}
+		return qsite;
+	}
+
+/*
+ * Search one site using the provided query.
+ */
+	private IStatus searchOneSite(
+		IUpdateSiteAdapter siteAdapter,
+		String[] categoriesToSkip,
+		IUpdateSearchQuery query,
+		IUpdateSearchResultCollector collector,
+		List associateSites,
+		SubProgressMonitor monitor,
+		boolean checkMirrors)
+		throws CoreException {
+		
+		String text = NLS.bind(Messages.UpdateSearchRequest_contacting, siteAdapter.getLabel());
+		monitor.subTask(text);
+		monitor.beginTask("", 10); //$NON-NLS-1$
+		URL siteURL = siteAdapter.getURL();
+
+		ISite site;
+		try {
+			site =
+				SiteManager.getSite(
+					siteURL,
+					new SubProgressMonitor(monitor, 1));
+			
+			// If frozen connection was canceled, there will be no site.
+			if (site == null) {
+				monitor.worked(9);
+				return null;
+			}
+			
+			
+			// prompt the user to pick up a site (do not recursively go into mirror sites on the mirror site)
+			if ((collector instanceof IUpdateSearchResultCollectorFromMirror) &&
+				(site instanceof ISiteWithMirrors) &&
+                !(siteAdapter instanceof MirroredUpdateSiteAdapter)) {
+				
+				IURLEntry mirror = null;
+				try {
+					mirror = ((IUpdateSearchResultCollectorFromMirror)collector).getMirror((ISiteWithMirrors)site, siteAdapter.getLabel());
+					if (site instanceof ExtendedSite) {
+						((ExtendedSite)site).setSelectedMirror(mirror);
+					}
+				}
+				catch (OperationCanceledException e) {
+					monitor.setCanceled(true);
+					return Status.CANCEL_STATUS;
+				}
+				
+				if (mirror != null) 
+					return searchOneSite(new MirroredUpdateSiteAdapter(mirror), categoriesToSkip, query, collector, associateSites, new SubProgressMonitor(monitor,1), false);
+			}
+		} catch (CoreException e) {
+			// Test the exception. If the exception is
+			// due to the site connection problems,
+			// allow the search to move on to 
+			// the next site. Otherwise,
+			// rethrow the exception, causing the search
+			// to terminate.
+			IStatus status = e.getStatus();
+			if (status == null)
+//				|| status.getCode() != ISite.SITE_ACCESS_EXCEPTION)
+				throw e;
+			monitor.worked(10);
+			return status;
+		}
+
+		text = NLS.bind(Messages.UpdateSearchRequest_checking, siteAdapter.getLabel());
+		monitor.getWrappedProgressMonitor().subTask(text);
+
+		if (site instanceof ExtendedSite) {
+			//System.out.println("ExtendedSite is here"); //$NON-NLS-1$
+			IURLEntry[] associateSitesList = ((ExtendedSite)site).getAssociateSites(); 
+			if (associateSitesList != null) {
+				for(int i = 0; i < associateSitesList.length; i++) {
+					associateSites.add(new UpdateSearchSite(associateSitesList[i].getAnnotation(), associateSitesList[i].getURL(), null));
+				}
+			}
+		}
+		query.run(
+			site,
+			categoriesToSkip,
+			aggregateFilter,
+			collector,
+			new SubProgressMonitor(monitor, 9));
+		return null;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/UpdateSearchScope.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/UpdateSearchScope.java
new file mode 100644
index 0000000..13f56f2
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/UpdateSearchScope.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import java.net.*;
+import java.util.Vector;
+
+import org.eclipse.update.internal.search.*;
+
+/**
+ * This class encapsulates update scope of the update search.
+ * Sites that need to be visited should be added to the scope.
+ * If some categories should be skipped, their names must be
+ * passed as array of strings to the method.
+ *
+ * <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>
+ * @see UpdateSearchRequest
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class UpdateSearchScope {
+	private Vector sites;
+	private URL updateMapURL;
+	private boolean isFeatureProvidedSitesEnabled = true;
+	
+	private static class UpdateSearchSite
+		extends UpdateSiteAdapter
+		implements IUpdateSearchSite {
+		private String[] categoriesToSkip;
+
+		public UpdateSearchSite(
+			String label,
+			URL siteURL,
+			String[] categoriesToSkip) {
+			super(label, siteURL);
+			this.categoriesToSkip = categoriesToSkip;
+		}
+		public String[] getCategoriesToSkip() {
+			return categoriesToSkip;
+		}
+	}
+
+	/**
+	 * The default constructor.
+	 */
+	public UpdateSearchScope() {
+		sites = new Vector();
+	}
+
+	/**
+	 * Sets the optional URL of the update map file. This file
+	 * is used to redirect search for new updates to other
+	 * servers and is typically used when a local
+	 * update site proxy (possibly behind the firewall) is
+	 * set up.
+	 * @param url the url of the Java properties file that
+	 * contains the redirection information.
+	 */
+	
+	public void setUpdateMapURL(URL url) {
+		this.updateMapURL = url;
+	}
+	
+	/**
+	 * Returns the optional URL of the update map file. By 
+	 * default, no map file is set.
+	 * @return the URL of the map file or <samp>null</samp>
+	 * if not set.
+	 */
+	
+	public URL getUpdateMapURL() {
+		return updateMapURL;
+	}
+
+	/**
+	 * Adds the site to scan to the search scope.
+	 * @param label the presentation name of the site to visit.
+	 * @param siteURL the URL of the site to visit.
+	 * @param categoriesToSkip an array of category names that should be skipped or <samp>null</samp> if all features should be considered.
+	 */
+	public void addSearchSite(
+		String label,
+		URL siteURL,
+		String[] categoriesToSkip) {
+		sites.add(new UpdateSearchSite(label, siteURL, categoriesToSkip));
+	}
+
+	/**
+	 * Returns the sites that should be visited during the search.
+	 * @return an array of site adapters
+	 */
+	public IUpdateSearchSite[] getSearchSites() {
+		return (UpdateSearchSite[]) sites.toArray(
+			new UpdateSearchSite[sites.size()]);
+	}
+	
+	/**
+	 * In addition to the sites added by addSearchSite(), features contribute their own update url's.
+	 * This method returns true if those sites are also searched.
+	 * @return true if update site provided by features are also searched. Default is true.
+	 */
+	public boolean isFeatureProvidedSitesEnabled(){
+		return isFeatureProvidedSitesEnabled;
+	}
+
+	/**
+	 * Enable or disable searching of feature provided update sites. 
+	 * If disabled, only sites added by addSearchSite() are searched.
+	 * @param enable false to disable searching of feature provided sites. By default, these sites are searched.
+	 */
+	public void setFeatureProvidedSitesEnabled(boolean enable){
+		this.isFeatureProvidedSitesEnabled = enable;
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/VersionedIdentifiersFilter.java b/update/org.eclipse.update.core/src/org/eclipse/update/search/VersionedIdentifiersFilter.java
new file mode 100644
index 0000000..996bd02
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/VersionedIdentifiersFilter.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.search;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+
+
+/**
+ * This class can be added to the update search request
+ * to filter out features that are not part of the specified set.
+ * 
+ * <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>
+ * @see UpdateSearchRequest
+ * @see IUpdateSearchFilter
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class VersionedIdentifiersFilter extends BaseFilter {
+	private ArrayList vids;
+	
+	public VersionedIdentifiersFilter() {
+		this(new VersionedIdentifier[0]);
+	}
+	
+	public VersionedIdentifiersFilter(VersionedIdentifier[] vids) {
+		this.vids = new ArrayList(vids.length);
+		for (int i=0; i<vids.length; i++)
+			this.vids.add(vids[i]);
+	}
+	
+	public void add(VersionedIdentifier vid) {
+		vids.add(vid);
+	}
+	
+	public boolean accept(IFeatureReference match) {
+		try {
+			for (int i=0; i<vids.size(); i++) {
+				VersionedIdentifier vid = (VersionedIdentifier)vids.get(i);
+				// installed version is the same as the match - accept
+				if (vid.equals(match.getVersionedIdentifier()))
+					return true;
+			}
+			return false;
+		} catch (CoreException e) {
+			return false;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/search/package.html b/update/org.eclipse.update.core/src/org/eclipse/update/search/package.html
new file mode 100644
index 0000000..350db90
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/search/package.html
@@ -0,0 +1,18 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides support for performing searches and filtering of features.
+<h2>
+Package Specification</h2>
+This package contains interfaces and helper classes for creating and executing search operations. 
+<p>
+<b>Note:</b> This package has been deprecated and will be deleted in a future
+release.  See bug 311590 for details.
+</p>
+</body>
+</html>
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/AddSiteCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/AddSiteCommand.java
new file mode 100644
index 0000000..8af4b11
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/AddSiteCommand.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import java.io.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Command to add a new product extension site.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class AddSiteCommand extends ScriptedCommand {
+	private ISite site;
+	private File sitePath;
+	
+	/**
+	 * @param fromSite if specified, list only the features from the specified local install site
+	 */
+	public AddSiteCommand(String fromSite) throws Exception {
+		try {
+			if (fromSite != null) {
+				sitePath = new File(fromSite);
+				if (!sitePath.exists())
+					throw new Exception(Messages.Standalone_noSite + fromSite); 
+					
+				URL fromSiteURL = sitePath.toURL();
+				site = SiteManager.getSite(fromSiteURL, null);
+				if (site == null) {
+					throw new Exception(Messages.Standalone_noSite + fromSite); 
+				}
+				IConfiguredSite csite = site.getCurrentConfiguredSite();
+				if (csite != null)
+					throw new Exception(Messages.Standalone_siteConfigured + fromSite); 
+			} else {
+				throw new Exception(Messages.Standalone_noSite3 ); 
+			}		
+		} catch (Exception e) {
+			throw e;
+		} 
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		// check if the config file has been modifed while we were running
+		IStatus status = OperationsManager.getValidator().validatePlatformConfigValid();
+		if (status != null) {
+			UpdateCore.log(status);
+			return false;
+		}
+		
+		if (site == null)
+			return false;
+			
+		try {
+			IConfiguredSite csite = getConfiguration().createConfiguredSite(sitePath);
+			getConfiguration().addConfiguredSite(csite);
+			// update the sites array to pick up new site
+			getConfiguration().getConfiguredSites();
+			SiteManager.getLocalSite().save();
+			return true;
+		} catch (CoreException e) {
+			UpdateCore.log(e);
+			return false;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/CmdLineArgs.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/CmdLineArgs.java
new file mode 100644
index 0000000..7ca7891
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/CmdLineArgs.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.mirror.MirrorCommand;
+
+/**
+ * This class parses the command line arguments for update standalone commands
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class CmdLineArgs {
+	private HashMap options = new HashMap();
+	public CmdLineArgs(String[] args) {
+//		// default command
+//		options.put("-command", "install");
+
+		for (int i = 0; i < args.length - 1; i++) {
+			if ("-command".equals(args[i])) { //$NON-NLS-1$
+				if (isValidCommand(args[i + 1])) {
+					options.put("-command", args[i + 1]); //$NON-NLS-1$
+					i++;
+				} else {
+					StandaloneUpdateApplication.exceptionLogged();
+					UpdateCore.log(
+						Utilities.newCoreException(
+							Messages.Standalone_invalidCmd + args[i + 1], 
+							null));
+					return;
+				}
+			}
+
+			if (isValidParam(args[i])) {
+				options.put(args[i], args[i + 1]);
+				i++;
+
+			}
+			// -to should specify a directory
+			// if -to specifies file URL, change it to a directory
+			String to = (String) options.get("-to"); //$NON-NLS-1$
+			if (to != null && to.startsWith("file:")) { //$NON-NLS-1$
+				try {
+					URL url = new URL(to);
+					options.put("-to", url.getFile()); //$NON-NLS-1$
+				} catch (MalformedURLException mue) {
+				}
+			}
+		}
+	}
+
+	private boolean isValidParam(String param) {
+		return param.equals("-command") //$NON-NLS-1$
+			|| param.equals("-version") //$NON-NLS-1$
+			|| param.equals("-to") //$NON-NLS-1$
+			|| param.equals("-from") //$NON-NLS-1$
+			|| param.equals("-featureId") //$NON-NLS-1$
+			|| param.equals("-verifyOnly") //$NON-NLS-1$
+			|| param.equals("-mirrorURL") //$NON-NLS-1$
+		    || param.equals("-ignoreMissingPlugins"); //$NON-NLS-1$
+	}
+
+	private boolean isValidCommand(String cmd) {
+		if (cmd == null)
+			return false;
+		else
+			return cmd.equals("install") //$NON-NLS-1$
+			|| cmd.equals("enable") //$NON-NLS-1$
+			|| cmd.equals("disable") //$NON-NLS-1$
+			|| cmd.equals("search") //$NON-NLS-1$
+			|| cmd.equals("update") //$NON-NLS-1$
+			|| cmd.equals("mirror") //$NON-NLS-1$
+			|| cmd.equals("uninstall") //$NON-NLS-1$
+			|| cmd.equals("listFeatures") //$NON-NLS-1$
+			|| cmd.equals("addSite") //$NON-NLS-1$
+			|| cmd.equals("removeSite"); //$NON-NLS-1$
+	}
+
+	public ScriptedCommand getCommand() {
+		try {
+			String cmd = (String) options.get("-command"); //$NON-NLS-1$
+			if (cmd == null)
+				return null;
+			if (cmd.equals("install")) //$NON-NLS-1$
+				return new InstallCommand(
+					(String) options.get("-featureId"), //$NON-NLS-1$
+					(String) options.get("-version"), //$NON-NLS-1$
+					(String) options.get("-from"), //$NON-NLS-1$
+					(String) options.get("-to"), //$NON-NLS-1$
+					(String) options.get("-verifyOnly")); //$NON-NLS-1$
+			else if (cmd.equals("enable")) //$NON-NLS-1$
+				return new EnableCommand(
+					(String) options.get("-featureId"), //$NON-NLS-1$
+					(String) options.get("-version"), //$NON-NLS-1$
+					(String) options.get("-to"), //$NON-NLS-1$
+					(String) options.get("-verifyOnly")); //$NON-NLS-1$
+			else if (cmd.equals("disable")) //$NON-NLS-1$
+				return new DisableCommand(
+					(String) options.get("-featureId"), //$NON-NLS-1$
+					(String) options.get("-version"), //$NON-NLS-1$
+					(String) options.get("-to"), //$NON-NLS-1$
+					(String) options.get("-verifyOnly")); //$NON-NLS-1$
+			else if (cmd.equals("search")) //$NON-NLS-1$
+				return new SearchCommand((String) options.get("-from")); //$NON-NLS-1$
+			else if (cmd.equals("update")) //$NON-NLS-1$
+				return new UpdateCommand(
+					(String) options.get("-featureId"), //$NON-NLS-1$
+					(String) options.get("-version"), //$NON-NLS-1$
+					(String) options.get("-verifyOnly")); //$NON-NLS-1$
+			else if (cmd.equals("mirror")) //$NON-NLS-1$
+				return new MirrorCommand(
+					(String) options.get("-featureId"), //$NON-NLS-1$
+					(String) options.get("-version"), //$NON-NLS-1$
+					(String) options.get("-from"), //$NON-NLS-1$
+					(String) options.get("-to"), //$NON-NLS-1$
+					(String) options.get("-mirrorURL"), //$NON-NLS-1$
+					(String) options.get("-ignoreMissingPlugins")); //$NON-NLS-1$
+			else if (cmd.equals("uninstall")) //$NON-NLS-1$
+				return new UninstallCommand(
+					(String) options.get("-featureId"), //$NON-NLS-1$
+					(String) options.get("-version"), //$NON-NLS-1$
+					(String) options.get("-to"), //$NON-NLS-1$
+					(String) options.get("-verifyOnly")); //$NON-NLS-1$
+			else if (cmd.equals("listFeatures")) //$NON-NLS-1$
+				return new ListFeaturesCommand((String) options.get("-from")); //$NON-NLS-1$
+			else if (cmd.equals("addSite")) //$NON-NLS-1$
+				return new AddSiteCommand((String) options.get("-from")); //$NON-NLS-1$
+			else if (cmd.equals("removeSite")) //$NON-NLS-1$
+				return new RemoveSiteCommand((String) options.get("-to")); //$NON-NLS-1$
+			else
+				return null;
+		} catch (Exception e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return null;
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/DisableCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/DisableCommand.java
new file mode 100644
index 0000000..856ad73
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/DisableCommand.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.internal.operations.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Command to disable (unconfigure) a feature
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class DisableCommand extends ScriptedCommand {
+
+	private IConfiguredSite targetSite;
+	private IFeature feature;
+
+	public DisableCommand(
+		String featureId,
+		String version,
+		String toSite,
+		String verifyOnly)
+		throws Exception {
+
+		super(verifyOnly);
+
+		try {
+			IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
+
+			// Get site containing the feature to disable
+			if (toSite != null) {
+				URL toSiteURL = new File(toSite).toURL();
+				if (SiteManager.getSite(toSiteURL, null) == null) {
+					throw new Exception(Messages.Standalone_noSite + toSite); 
+				}
+				targetSite =
+					SiteManager
+						.getSite(toSiteURL, null)
+						.getCurrentConfiguredSite();
+			}
+			if (targetSite == null) {
+				for (int i = 0; i < sites.length; i++) {
+					if (sites[i].isProductSite()) {
+						targetSite = sites[i];
+						break;
+					}
+				}
+			}
+
+			IFeature[] features =
+				UpdateUtils.searchSite(featureId, targetSite, true);
+			if (features == null || features.length == 0) {
+				throw new Exception(NLS.bind(Messages.Standalone_noFeatures3, (new String[] { featureId })));
+			}
+			if (version == null || version.trim().length() == 0)
+				feature = features[0]; // pick the first feature
+			else
+				for (int i = 0; features != null && i < features.length; i++) {
+					if (features[i]
+						.getVersionedIdentifier()
+						.getVersion()
+						.toString()
+						.equals(version)) {
+						feature = features[i];
+						break;
+					}
+				}
+			if (feature == null) {
+				throw new Exception(NLS.bind(Messages.Standalone_noFeatures4, (new String[] { featureId, version })));
+			}
+
+		} catch (MalformedURLException e) {
+			throw e;
+		} catch (CoreException e) {
+			throw e;
+		}
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		// check if the config file has been modifed while we were running
+		IStatus status = OperationsManager.getValidator().validatePlatformConfigValid();
+		if (status != null) {
+			UpdateCore.log(status);
+			return false;
+		}
+		if (isVerifyOnly()) {
+			status =
+				OperationsManager.getValidator().validatePendingUnconfig(
+					feature);
+			if (status != null && status.getCode() == IStatus.WARNING)
+				UpdateCore.log(status);
+			return status == null || status.getCode() == IStatus.WARNING;
+		}
+
+		final IUnconfigFeatureOperation configOperation =
+			OperationsManager.getOperationFactory().createUnconfigOperation(
+				targetSite,
+				feature);
+
+		try {
+			configOperation.execute(monitor, this);
+			return true;
+		} catch (CoreException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		} catch (InvocationTargetException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/EnableCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/EnableCommand.java
new file mode 100644
index 0000000..7fdf105
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/EnableCommand.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.internal.operations.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Command to enable (configure) a feature.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class EnableCommand extends ScriptedCommand {
+
+	private IConfiguredSite targetSite;
+	private IFeature feature;
+
+	public EnableCommand(
+		String featureId,
+		String version,
+		String toSite,
+		String verifyOnly)
+		throws Exception {
+
+		super(verifyOnly);
+
+		try {
+			IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
+
+			// Get site to enable to
+			if (toSite != null) {
+				URL toSiteURL = new File(toSite).toURL();
+				if (SiteManager.getSite(toSiteURL, null) == null) {
+					throw new Exception(Messages.Standalone_noSite + toSite); 
+				}
+				targetSite =
+					SiteManager
+						.getSite(toSiteURL, null)
+						.getCurrentConfiguredSite();
+			}
+			if (targetSite == null) {
+				for (int i = 0; i < sites.length; i++) {
+					if (sites[i].isProductSite()) {
+						targetSite = sites[i];
+						break;
+					}
+				}
+			}
+
+			IFeature[] features =
+				UpdateUtils.searchSite(featureId, targetSite, false);
+			if (features == null || features.length == 0) {
+				throw new Exception(NLS.bind(Messages.Standalone_noFeatures1, (new String[] { featureId })));
+			}
+			if (version == null || version.trim().length() == 0)
+				feature = features[0]; // pick the first feature
+			else
+				for (int i = 0; features != null && i < features.length; i++) {
+					if (features[i]
+						.getVersionedIdentifier()
+						.getVersion()
+						.toString()
+						.equals(version)
+						&& !targetSite.isConfigured(features[i])) {
+						feature = features[i];
+						break;
+					}
+				}
+			if (feature == null) {
+				throw new Exception(NLS.bind(Messages.Standalone_noFeatures2, (new String[] { featureId, version })));
+			}
+
+		} catch (MalformedURLException e) {
+			throw e;
+		} catch (CoreException e) {
+			throw e;
+		}
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		// check if the config file has been modifed while we were running
+		IStatus status = OperationsManager.getValidator().validatePlatformConfigValid();
+		if (status != null) {
+			UpdateCore.log(status);
+			return false;
+		}
+		
+		if (isVerifyOnly()) {
+			status =
+				OperationsManager.getValidator().validatePendingConfig(feature);
+			if (status != null && status.getCode() == IStatus.WARNING)
+				UpdateCore.log(status);
+			return status == null || status.getCode() == IStatus.WARNING;
+		}
+
+		final IConfigFeatureOperation configOperation =
+			OperationsManager.getOperationFactory().createConfigOperation(
+				targetSite,
+				feature);
+
+		try {
+			configOperation.execute(monitor, this);
+			return true;
+		} catch (CoreException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		} catch (InvocationTargetException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/InstallCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/InstallCommand.java
new file mode 100644
index 0000000..4dbc525
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/InstallCommand.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.IConfiguredSite;
+import org.eclipse.update.core.IFeature;
+import org.eclipse.update.core.ISite;
+import org.eclipse.update.core.SiteManager;
+import org.eclipse.update.core.Utilities;
+import org.eclipse.update.core.VersionedIdentifier;
+import org.eclipse.update.internal.configurator.UpdateURLDecoder;
+import org.eclipse.update.internal.core.Messages;
+import org.eclipse.update.internal.core.UpdateCore;
+import org.eclipse.update.internal.core.UpdateManagerUtils;
+import org.eclipse.update.internal.operations.DuplicateConflictsValidator;
+import org.eclipse.update.internal.operations.UpdateUtils;
+import org.eclipse.update.internal.search.SiteSearchCategory;
+import org.eclipse.update.operations.IBatchOperation;
+import org.eclipse.update.operations.IInstallFeatureOperation;
+import org.eclipse.update.operations.OperationsManager;
+import org.eclipse.update.search.BackLevelFilter;
+import org.eclipse.update.search.EnvironmentFilter;
+import org.eclipse.update.search.IUpdateSearchResultCollector;
+import org.eclipse.update.search.UpdateSearchRequest;
+import org.eclipse.update.search.UpdateSearchScope;
+import org.eclipse.update.search.VersionedIdentifiersFilter;
+
+/**
+ * Command to install a feature.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class InstallCommand extends ScriptedCommand {
+
+	private IConfiguredSite targetSite;
+	private UpdateSearchRequest searchRequest;
+	private UpdateSearchResultCollector collector;
+	private URL remoteSiteURL;
+	private String featureId;
+	private String version;
+
+	public InstallCommand(
+		String featureId,
+		String version,
+		String fromSite,
+		String toSite,
+		String verifyOnly)
+		throws Exception {
+
+		super(verifyOnly);
+
+		try {
+			this.featureId = featureId;
+			this.version = version;
+
+			//PAL foundation
+			this.remoteSiteURL = new URL(UpdateURLDecoder.decode(fromSite, "UTF-8")); //$NON-NLS-1$
+
+			// Get site to install to
+			targetSite = getTargetSite(toSite);
+			// if no site, try selecting the site that already has the old feature
+			if (targetSite == null)
+				targetSite = UpdateUtils.getSiteWithFeature(getConfiguration(), featureId);
+			// if still no site, pick the product site, if writeable
+			if (targetSite == null) {
+				IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
+				for (int i = 0; i < sites.length; i++) {
+					if (sites[i].isProductSite() && sites[i].isUpdatable()) {
+						targetSite = sites[i];
+						break;
+					}
+				}
+			}
+			// if all else fails, pick the first updateable site
+			if (targetSite == null) {
+				IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
+				for (int i = 0; i < sites.length; i++) {
+					if (sites[i].isUpdatable()) {
+						targetSite = sites[i];
+						break;
+					}
+				}
+			}
+			// are we still checking for sites? forget about it
+			if (targetSite == null)
+				throw Utilities.newCoreException(
+						Messages.Standalone_cannotInstall + featureId + " " + version,  //$NON-NLS-1$
+						null);
+			
+			UpdateSearchScope searchScope = new UpdateSearchScope();
+			searchScope.addSearchSite(
+				NLS.bind( Messages.InstallCommand_site, remoteSiteURL.toExternalForm()),
+				remoteSiteURL,
+				new String[0]);
+
+			searchRequest =
+				new UpdateSearchRequest(new SiteSearchCategory(), searchScope);
+			VersionedIdentifier vid =
+				new VersionedIdentifier(featureId, version);
+			searchRequest.addFilter(
+				new VersionedIdentifiersFilter(
+					new VersionedIdentifier[] { vid }));
+			searchRequest.addFilter(new EnvironmentFilter());
+			searchRequest.addFilter(new BackLevelFilter());
+
+			collector = new UpdateSearchResultCollector();
+
+		} catch (MalformedURLException e) {
+			throw e;
+		} catch (CoreException e) {
+			throw e;
+		}
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		try {
+			monitor.beginTask(Messages.Standalone_installing, 4); 
+			searchRequest.performSearch(collector, new SubProgressMonitor(monitor,1));
+			IInstallFeatureOperation[] operations = collector.getOperations();
+			if (operations == null || operations.length == 0) {
+				throw Utilities.newCoreException(
+					NLS.bind(Messages.Standalone_notFoundOrNewer, featureId + ' ' + version, remoteSiteURL),
+					null);
+			}
+
+			// Check for duplication conflicts
+			ArrayList conflicts =
+				DuplicateConflictsValidator.computeDuplicateConflicts(
+					operations,
+					getConfiguration());
+			if (conflicts != null) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(Utilities.newCoreException(Messages.Standalone_duplicate, null));
+				System.out.println(Messages.Standalone_duplicate); 
+			}
+
+			if (isVerifyOnly()) {
+				if (operations == null || operations.length == 0)
+					return false;
+				IStatus status = OperationsManager.getValidator().validatePendingChanges(operations);
+				if (status != null && status.getCode() == IStatus.ERROR)
+					throw new CoreException(status);
+				else
+					return true;
+			}
+
+			IBatchOperation installOperation =
+				OperationsManager
+					.getOperationFactory()
+					.createBatchInstallOperation(
+					operations);
+			try {
+				installOperation.execute(new SubProgressMonitor(monitor,3), this);
+				System.out.println(NLS.bind(Messages.Standalone_installed, featureId + ' ' + version));
+				return true;
+			} catch (Exception e) {
+				throw Utilities.newCoreException(
+					Messages.Standalone_cannotInstall + featureId + " " + version,  //$NON-NLS-1$
+					e);
+			}
+		} catch (CoreException ce) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(ce);
+			return false;
+		} catch (OperationCanceledException ce) {
+			return true;
+		} finally {
+			monitor.done();
+		}
+	}
+
+	private IConfiguredSite getTargetSite(String toSite) throws Exception {
+		if (toSite == null) 
+			return null;
+			
+		IConfiguredSite[] configuredSites = getConfiguration().getConfiguredSites();
+		File sitePath = new File(toSite);
+		File secondaryPath = sitePath.getName().equals("eclipse") ? //$NON-NLS-1$
+							null : new File(sitePath, "eclipse"); //$NON-NLS-1$
+
+		for (int i = 0; i < configuredSites.length; i++) {
+			IConfiguredSite csite = configuredSites[i];
+			if (UpdateManagerUtils.sameURL(csite.getSite().getURL(),sitePath.toURL()))
+				return csite;
+			else if (secondaryPath != null && UpdateManagerUtils.sameURL(csite.getSite().getURL(),secondaryPath.toURL()))
+				return csite;
+		}
+
+		// extension site not found, need to create one
+		if (!sitePath.exists())
+			sitePath.mkdirs();
+		URL toSiteURL = sitePath.toURL();
+		ISite site = SiteManager.getSite(toSiteURL, null);
+		if (site == null) {
+			throw new Exception(Messages.Standalone_noSite + toSite); 
+		}
+		IConfiguredSite csite = site.getCurrentConfiguredSite();
+		if (csite == null) {
+			csite = getConfiguration().createConfiguredSite(sitePath);
+			IStatus status = csite.verifyUpdatableStatus();
+			if (status.isOK())
+				getConfiguration().addConfiguredSite(csite);
+			else 
+				throw new CoreException(status);
+
+			return csite;
+		}
+		return csite;
+	}
+	class UpdateSearchResultCollector implements IUpdateSearchResultCollector {
+		private ArrayList operations = new ArrayList();
+
+		public void accept(IFeature feature) {
+			if (feature
+				.getVersionedIdentifier()
+				.getIdentifier()
+				.equals(featureId)
+				&& feature
+					.getVersionedIdentifier()
+					.getVersion()
+					.toString()
+					.equals(
+					version)) {
+				operations.add(
+					OperationsManager
+						.getOperationFactory()
+						.createInstallOperation(
+						targetSite,
+						feature,
+						null,
+						null,
+						null));
+			}
+		}
+		public IInstallFeatureOperation[] getOperations() {
+			IInstallFeatureOperation[] opsArray =
+				new IInstallFeatureOperation[operations.size()];
+			operations.toArray(opsArray);
+			return opsArray;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/ListFeaturesCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/ListFeaturesCommand.java
new file mode 100644
index 0000000..8ac038b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/ListFeaturesCommand.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import java.io.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+
+
+/**
+ * Command to list all installed features.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class ListFeaturesCommand extends ScriptedCommand {
+	private IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
+	
+	/**
+	 * @param fromSite if specified, list only the features from the specified local install site
+	 */
+	public ListFeaturesCommand(String fromSite) throws Exception {
+		try {
+			if (fromSite != null) {
+				File sitePath = new File(fromSite);
+				if (!sitePath.exists())
+					throw new Exception(Messages.Standalone_noSite + fromSite); 
+					
+				URL fromSiteURL = sitePath.toURL();
+				ISite site = SiteManager.getSite(fromSiteURL, null);
+				if (site == null) {
+					throw new Exception(Messages.Standalone_noSite + fromSite); 
+				}
+				IConfiguredSite csite = site.getCurrentConfiguredSite();
+				if (csite == null)
+					throw new Exception(Messages.Standalone_noConfiguredSite + fromSite); 
+				sites = new IConfiguredSite[] { csite };
+			} 
+		
+		} catch (Exception e) {
+			throw e;
+		} 
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		try {
+			if (sites != null) {
+				for (int i = 0; i < sites.length; i++) {
+					System.out.println("Site: " + sites[i].getSite().getURL()); //$NON-NLS-1$
+					IFeatureReference[] features = sites[i]
+							.getFeatureReferences();
+					for (int f = 0; f < features.length; f++) {
+						boolean configured = sites[i].isConfigured(features[f]
+								.getFeature(null));
+						System.out.println("  Feature: " //$NON-NLS-1$
+								+ features[f].getVersionedIdentifier()
+										.getIdentifier()
+								+ " " //$NON-NLS-1$
+								+ features[f].getVersionedIdentifier()
+										.getVersion() + "  " //$NON-NLS-1$
+								+ (configured ? "enabled" : "disabled")); //$NON-NLS-1$ //$NON-NLS-2$
+					}
+				}
+			}
+			return true;
+		} catch (CoreException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/RemoveSiteCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/RemoveSiteCommand.java
new file mode 100644
index 0000000..bbfece4
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/RemoveSiteCommand.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import java.io.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.operations.*;
+
+
+/**
+ * Command to remove a product extension site.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class RemoveSiteCommand extends ScriptedCommand {
+	private IConfiguredSite csite;
+	private File sitePath;
+	
+	/**
+	 * @param toSite if specified, list only the features from the specified local install site
+	 */
+	public RemoveSiteCommand(String toSite) throws Exception {
+		try {
+			if (toSite != null) {
+				sitePath = new File(toSite);
+				if (!sitePath.getName().equals("eclipse")) //$NON-NLS-1$
+					sitePath = new File(sitePath, "eclipse"); //$NON-NLS-1$
+				if (!sitePath.exists())
+					throw new Exception(Messages.Standalone_noSite + toSite); 
+					
+				IConfiguredSite[] csites = SiteManager.getLocalSite().getCurrentConfiguration().getConfiguredSites();
+				for (int i=0; i<csites.length; i++) {
+					File f = new File(csites[i].getSite().getURL().getFile());
+					if (f.equals(sitePath)) {
+						csite = csites[i];
+						break;
+					}
+				}
+				
+				if (csite == null)
+					throw new Exception(Messages.Standalone_noConfiguredSite + toSite); 
+			} else {
+				throw new Exception(Messages.Standalone_noSite3); 
+			}
+		
+		} catch (Exception e) {
+			throw e;
+		} 
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		// check if the config file has been modifed while we were running
+		IStatus status = OperationsManager.getValidator().validatePlatformConfigValid();
+		if (status != null) {
+			UpdateCore.log(status);
+			return false;
+		}	
+		try {
+			getConfiguration().removeConfiguredSite(csite);
+			// update the sites array
+			getConfiguration().getConfiguredSites();
+			SiteManager.getLocalSite().save();
+			return true;
+		} catch (CoreException e) {
+			UpdateCore.log(e);
+			return false;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/ScriptedCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/ScriptedCommand.java
new file mode 100644
index 0000000..eead53b
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/ScriptedCommand.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Parent class for all the update manager standalone commands.
+ * Subclasses will provide specific operations and the implementation of the run() method.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public abstract class ScriptedCommand implements IOperationListener {
+
+	private IInstallConfiguration config;
+	protected boolean verifyOnly;
+
+	/**
+	 * Constructor
+	 *
+	 */
+	public ScriptedCommand() {
+		this(null);
+	}
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param verifyOnly if true, the command is not executed, but will only attempt to run the command. 
+	 * This is mostly used when wanted to know if the command would fail.
+	 */
+	public ScriptedCommand(String verifyOnly) {
+		this.verifyOnly = "true".equals(verifyOnly); //$NON-NLS-1$
+	}
+
+	/**
+	 * @return  true if the command should only be run in simulation mode,
+	 * to verify if it can execute.
+	 */
+	protected final boolean isVerifyOnly() {
+		return verifyOnly;
+	}
+
+	/**
+	 * Convenience method that executes the command with a null progress monitor.
+	 */
+	public final boolean run() {
+		return run(new NullProgressMonitor());
+	}
+	
+	/**
+	 * Executes the command. Subclasses are responsible for implementing this method.
+	 * If the command was constructed with verifyOnly=true, the command should not execute, but only verify it can execute.
+	 * @param monitor progress monitor during command execution.
+	 */
+	public abstract boolean run(IProgressMonitor monitor);
+
+	/**
+	 * Applies the changes made to the current configuration.
+	 */
+	public void applyChangesNow() {
+		OperationsManager.applyChangesNow();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.operations.IOperationListener#afterExecute(org.eclipse.update.operations.IOperation)
+	 */
+	public boolean afterExecute(IOperation operation, Object data) {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.update.operations.IOperationListener#beforeExecute(org.eclipse.update.operations.IOperation)
+	 */
+	public boolean beforeExecute(IOperation operation, Object data) {
+		return true;
+	}
+
+	/**
+	 * @return the installation configuration affected by the command
+	 */
+	public final IInstallConfiguration getConfiguration() {
+		try {
+			ILocalSite localSite = SiteManager.getLocalSite();
+			config = localSite.getCurrentConfiguration();
+		} catch (CoreException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+		}
+		return config;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/SearchCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/SearchCommand.java
new file mode 100644
index 0000000..a6c989d
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/SearchCommand.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import java.io.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.configurator.UpdateURLDecoder;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.internal.search.*;
+import org.eclipse.update.search.*;
+
+/**
+ * Command to search an update site and list its features.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class SearchCommand extends ScriptedCommand {
+
+	private URL remoteSiteURL;
+	private UpdateSearchRequest searchRequest;
+	private IUpdateSearchResultCollector collector;
+
+	public SearchCommand(String fromSite) {
+		try {
+			//PAL foundation
+			this.remoteSiteURL = new URL(UpdateURLDecoder.decode(fromSite, "UTF-8")); //$NON-NLS-1$
+			UpdateSearchScope searchScope = new UpdateSearchScope();
+			searchScope.addSearchSite(
+				"remoteSite", //$NON-NLS-1$
+				remoteSiteURL,
+				new String[0]);
+			searchRequest =
+				new UpdateSearchRequest(new SiteSearchCategory(), searchScope);
+			collector = new UpdateSearchResultCollector();
+		} catch (MalformedURLException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+		} catch (UnsupportedEncodingException e) {
+		}
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		try {
+			monitor.beginTask(Messages.Standalone_searching + remoteSiteURL.toExternalForm(), 4); 
+			searchRequest.performSearch(collector, monitor);
+			return true;
+		} catch (CoreException ce) {
+			IStatus status = ce.getStatus();
+			if (status != null
+				&& status.getCode() == ISite.SITE_ACCESS_EXCEPTION) {
+				// Just show this but do not throw exception
+				// because there may be results anyway.
+				System.out.println(Messages.Standalone_connection); 
+			} else {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(ce);
+			}
+			return false;
+		} catch (OperationCanceledException ce) {
+			return true;
+		} finally {
+			monitor.done();
+		}
+	}
+
+
+	class UpdateSearchResultCollector implements IUpdateSearchResultCollector {
+		public void accept(IFeature feature) {
+			System.out.println(
+				"\"" //$NON-NLS-1$
+					+ feature.getLabel()
+					+ "\" " //$NON-NLS-1$
+					+ feature.getVersionedIdentifier().getIdentifier()
+					+ " " //$NON-NLS-1$
+					+ feature.getVersionedIdentifier().getVersion());
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/StandaloneUpdateApplication.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/StandaloneUpdateApplication.java
new file mode 100644
index 0000000..f27932f
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/StandaloneUpdateApplication.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.internal.core.*;
+
+/**
+ * The application class used to launch standalone update commands.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class StandaloneUpdateApplication implements IPlatformRunnable {
+
+	public final static Integer EXIT_ERROR = new Integer(1);
+	private static boolean loggedException = false;
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.boot.IPlatformRunnable#run(java.lang.Object)
+	 */
+	public Object run(Object args) throws Exception {
+		if (args == null)
+			return EXIT_ERROR;
+		if (args instanceof String[]) {
+			String[] params = (String[]) args;
+			CmdLineArgs cmdLineArgs = new CmdLineArgs(params);
+			ScriptedCommand cmd = cmdLineArgs.getCommand();
+			if (cmd == null) {
+				System.out.println(NLS.bind(Messages.Standalone_cmdFailed, (new String[] { Platform.getLogFileLocation().toOSString() })));
+				return EXIT_ERROR;
+			}
+			loggedException = false;
+			boolean result = cmd.run();
+			if (result) {
+				if (loggedException) {
+					System.out.println(NLS.bind(Messages.Standalone_cmdCompleteWithErrors, (new String[] { Platform.getLogFileLocation().toOSString() })));
+				} else {
+					System.out.println(Messages.Standalone_cmdOK); 
+				}
+				return IPlatformRunnable.EXIT_OK;
+			} else {
+				if (loggedException) {
+					System.out.println(NLS.bind(Messages.Standalone_cmdFailed, (new String[] { Platform.getLogFileLocation().toOSString() })));
+				} else {
+					System.out.println(Messages.Standalone_cmdFailedNoLog);
+				}
+				return EXIT_ERROR;
+			}
+		}
+		return EXIT_ERROR;
+	}
+	public static void exceptionLogged() {
+		loggedException = true;
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/UninstallCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/UninstallCommand.java
new file mode 100644
index 0000000..b360535
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/UninstallCommand.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.internal.operations.*;
+import org.eclipse.update.operations.*;
+
+/**
+ * Command to uninstall a feature. The feature must be disabled before uninstalling it.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class UninstallCommand extends ScriptedCommand {
+
+	private IConfiguredSite targetSite;
+	private IFeature feature;
+
+	public UninstallCommand(
+		String featureId,
+		String version,
+		String toSite,
+		String verifyOnly)
+		throws Exception {
+
+		super(verifyOnly);
+
+		try {
+			IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
+
+			// Get site that contains the feature to uninstall
+			if (toSite != null) {
+				URL toSiteURL = new File(toSite).toURL();
+				if (SiteManager.getSite(toSiteURL, null) == null) {
+					throw new Exception(Messages.Standalone_noSite + toSite); 
+				}
+				targetSite =
+					SiteManager
+						.getSite(toSiteURL, null)
+						.getCurrentConfiguredSite();
+			}
+			if (targetSite == null) {
+				for (int i = 0; i < sites.length; i++) {
+					if (sites[i].isProductSite()) {
+						targetSite = sites[i];
+						break;
+					}
+				}
+			}
+
+			IFeature[] features =
+				UpdateUtils.searchSite(featureId, targetSite, false);
+			if (features == null || features.length == 0) {
+				throw new Exception(NLS.bind(Messages.Standalone_noFeatures1, (new String[] { featureId })));
+			}
+			if (version == null || version.trim().length() == 0)
+				feature = features[0]; // pick the first feature
+			else
+				for (int i = 0; features != null && i < features.length; i++) {
+					if (features[i]
+						.getVersionedIdentifier()
+						.getVersion()
+						.toString()
+						.equals(version)
+						&& !targetSite.isConfigured(features[i])) {
+						feature = features[i];
+						break;
+					}
+				}
+			if (feature == null) {
+				throw new Exception(NLS.bind(Messages.Standalone_noFeatures2, (new String[] { featureId, version })));
+			}
+
+		} catch (MalformedURLException e) {
+			throw e;
+		} catch (CoreException e) {
+			throw e;
+		}
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		// check if the config file has been modifed while we were running
+		IStatus status = OperationsManager.getValidator().validatePlatformConfigValid();
+		if (status != null) {
+			UpdateCore.log(status);
+			return false;
+		}
+		if (InstallRegistry.getInstance().get("feature_"+ feature.getVersionedIdentifier()) == null) { //$NON-NLS-1$
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(Utilities.newCoreException(NLS.bind(Messages.UninstallCommand_featureNotInstalledByUM, (new String[] { feature.toString() })),null));
+			return false;
+		}
+							
+		if (isVerifyOnly()) {
+			// if reached this point, it is safe to uninstall
+			return true;
+		}
+
+		final IUninstallFeatureOperation uninstallOperation =
+			OperationsManager.getOperationFactory().createUninstallOperation(
+				targetSite,
+				feature);
+
+		try {
+			uninstallOperation.execute(monitor, this);
+			return true;
+		} catch (CoreException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		} catch (InvocationTargetException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+			return false;
+		}
+	}
+
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/UpdateCommand.java b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/UpdateCommand.java
new file mode 100644
index 0000000..8312c5e
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/UpdateCommand.java
@@ -0,0 +1,225 @@
+/*******************************************************************************
+ *  Copyright (c) 2000, 2010 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.update.standalone;
+
+import java.net.*;
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.update.configuration.*;
+import org.eclipse.update.core.*;
+import org.eclipse.update.internal.core.*;
+import org.eclipse.update.internal.operations.*;
+import org.eclipse.update.operations.*;
+import org.eclipse.update.search.*;
+
+/**
+ * Command to update and existing feature.
+ * <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>
+ * @since 3.0
+ * @deprecated The org.eclipse.update component has been replaced by Equinox p2.
+ * This API will be deleted in a future release. See bug 311590 for details.
+ */
+public class UpdateCommand extends ScriptedCommand {
+
+	private IConfiguredSite targetSite;
+	private UpdateSearchRequest searchRequest;
+	private UpdateSearchResultCollector collector;
+	private String featureId;
+	//private String version;
+	private IFeature currentFeature;
+
+	/**
+	 * Update command for updating a feature to a newer version.
+	 * @param featureId
+	 * @param verifyOnly
+	 * @throws Exception
+	 */
+	public UpdateCommand(String featureId, String verifyOnly)
+	throws Exception {
+		this(featureId, null, verifyOnly);
+	}
+	
+	/**
+	 * Update command for updating a feature to a specified newer version.
+	 * @param featureId
+	 * @param version
+	 * @param verifyOnly
+	 * @throws Exception
+	 */
+	public UpdateCommand(String featureId, String version, String verifyOnly)
+		throws Exception {
+
+		super(verifyOnly);
+
+		try {
+			this.featureId = featureId;
+			//this.version = version;
+			if (featureId != null) {
+				this.targetSite =
+					UpdateUtils.getSiteWithFeature(
+						getConfiguration(),
+						featureId);
+				if (targetSite == null) {
+					throw new Exception(NLS.bind(Messages.Standalone_noConfigSiteForFeature, (new String[] { featureId })));
+				}
+				IFeature[] currentFeatures =
+					UpdateUtils.searchSite(featureId, targetSite, true);
+				if (currentFeatures == null || currentFeatures.length == 0) {
+					throw new Exception(NLS.bind(Messages.Standalone_noFeatures3, (new String[] { featureId })));
+				}
+				this.currentFeature = currentFeatures[0];
+			} else {
+				// Get site to install to
+				IConfiguredSite[] sites =
+					getConfiguration().getConfiguredSites();
+				for (int i = 0; i < sites.length; i++) {
+					if (sites[i].isProductSite()) {
+						targetSite = sites[i];
+						break;
+					}
+				}
+			}
+			if (currentFeature == null)
+				searchRequest = UpdateUtils.createNewUpdatesRequest(null);
+			else {
+				searchRequest =
+					UpdateUtils.createNewUpdatesRequest(
+						new IFeature[] { currentFeature });
+				if (version != null)
+					searchRequest.addFilter(
+						new VersionedIdentifiersFilter(
+							new VersionedIdentifier[] { new VersionedIdentifier(featureId, version) }));
+			}
+
+			collector = new UpdateSearchResultCollector();
+
+		} catch (MalformedURLException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+		} catch (CoreException e) {
+			StandaloneUpdateApplication.exceptionLogged();
+			UpdateCore.log(e);
+		}
+	}
+
+	/**
+	 */
+	public boolean run(IProgressMonitor monitor) {
+		// check if the config file has been modifed while we were running
+		IStatus status = OperationsManager.getValidator().validatePlatformConfigValid();
+		if (status != null) {
+			UpdateCore.log(status);
+			return false;
+		}
+		try {
+			monitor.beginTask(Messages.Standalone_updating, 4);  
+			searchRequest.performSearch(collector, new SubProgressMonitor(monitor,1));
+			IInstallFeatureOperation[] operations = collector.getOperations();
+			if (operations == null || operations.length == 0) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(Utilities.newCoreException(NLS.bind(Messages.Standalone_noUpdate, (new String[] { featureId })),	null));
+				return false;
+			}
+
+			// Check for duplication conflicts
+			ArrayList conflicts =
+				DuplicateConflictsValidator.computeDuplicateConflicts(
+					operations,
+					getConfiguration());
+			if (conflicts != null) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(Utilities.newCoreException(Messages.Standalone_duplicate, null)); 
+				System.out.println(Messages.Standalone_duplicate);
+			}
+			
+			if (isVerifyOnly()) {
+				status = OperationsManager.getValidator().validatePendingChanges(operations);
+				if (status != null && status.getCode() == IStatus.ERROR)
+					throw new CoreException(status);
+				else
+					return true;
+			}
+
+			IBatchOperation installOperation =
+				OperationsManager
+					.getOperationFactory()
+					.createBatchInstallOperation(
+					operations);
+			try {
+				installOperation.execute(new SubProgressMonitor(monitor,3), this);
+				System.out.println(NLS.bind(Messages.Standalone_updated, featureId));
+				return true;
+			} catch (Exception e) {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(
+					Utilities.newCoreException(
+							NLS.bind(Messages.Standalone_noUpdate, (new String[] { featureId })),
+						e));
+				return false;
+			}
+		} catch (CoreException ce) {
+			status = ce.getStatus();
+			if (status != null
+				&& status.getCode() == ISite.SITE_ACCESS_EXCEPTION) {
+				// Just show this but do not throw exception
+				// because there may be results anyway.
+				System.out.println(Messages.Standalone_connection); 
+			} else {
+				StandaloneUpdateApplication.exceptionLogged();
+				UpdateCore.log(ce);
+			}
+			return false;
+		} catch (OperationCanceledException ce) {
+			return true;
+		} finally {
+			monitor.done();
+		}
+	}
+
+
+	class UpdateSearchResultCollector implements IUpdateSearchResultCollector {
+		private ArrayList operations = new ArrayList();
+
+		public void accept(IFeature feature) {
+
+			IInstallFeatureOperation op =
+				OperationsManager.getOperationFactory().createInstallOperation(
+					null,
+					feature,
+					null,
+					null,
+					null);
+
+			IConfiguredSite site =
+				UpdateUtils.getDefaultTargetSite(getConfiguration(), op);
+			if (site == null)
+				site = UpdateUtils.getAffinitySite(getConfiguration(), feature);
+			if (site == null)
+				site = targetSite;
+
+			op.setTargetSite(site);
+			operations.add(op);
+		}
+		public IInstallFeatureOperation[] getOperations() {
+			IInstallFeatureOperation[] opsArray =
+				new IInstallFeatureOperation[operations.size()];
+			operations.toArray(opsArray);
+			return opsArray;
+		}
+	}
+}
diff --git a/update/org.eclipse.update.core/src/org/eclipse/update/standalone/package.html b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/package.html
new file mode 100644
index 0000000..7044920
--- /dev/null
+++ b/update/org.eclipse.update.core/src/org/eclipse/update/standalone/package.html
@@ -0,0 +1,21 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.72 [en] (Windows NT 5.0; U) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides support for performing various install/update operations from the command line.
+<h2>
+Package Specification</h2>
+This package contains interfaces for performing update/install operations 
+without using the Update Manager user interface. In general, it is best to use these
+classes for command line invocation, but it is possible to also use them programmatically.
+<p>
+<b>Note:</b> This package has been deprecated and will be deleted in a future
+release.  See bug 311590 for details.
+</p>
+</body>
+</html>