The project facets framework ui plugin.
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/.classpath b/plugins/org.eclipse.wst.common.project.facet.ui/.classpath
new file mode 100644
index 0000000..065ac06
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/.cvsignore b/plugins/org.eclipse.wst.common.project.facet.ui/.cvsignore
new file mode 100644
index 0000000..1150533
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/.cvsignore
@@ -0,0 +1,4 @@
+bin
+build.xml
+facet-ui.jar
+temp.folder
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/.project b/plugins/org.eclipse.wst.common.project.facet.ui/.project
new file mode 100644
index 0000000..df18711
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.wst.common.project.facet.ui</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<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.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/.settings/org.eclipse.jdt.core.prefs b/plugins/org.eclipse.wst.common.project.facet.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..d7840a2
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Mon Jun 06 17:14:05 PDT 2005
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+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.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/build.properties b/plugins/org.eclipse.wst.common.project.facet.ui/build.properties
new file mode 100644
index 0000000..f78a7b0
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/build.properties
@@ -0,0 +1,6 @@
+source.facet-ui.jar = src/
+output.facet-ui.jar = bin/
+bin.includes = plugin.xml,\
+               plugin.properties,\
+               facet-ui.jar,\
+               images/
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/images/error.gif b/plugins/org.eclipse.wst.common.project.facet.ui/images/error.gif
new file mode 100644
index 0000000..0bc6068
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/images/error.gif
Binary files differ
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/images/unknown.gif b/plugins/org.eclipse.wst.common.project.facet.ui/images/unknown.gif
new file mode 100644
index 0000000..7ccc6a7
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/images/unknown.gif
Binary files differ
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/plugin.properties b/plugins/org.eclipse.wst.common.project.facet.ui/plugin.properties
new file mode 100644
index 0000000..f722f53
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/plugin.properties
@@ -0,0 +1 @@
+add.remove.facets = Add/Remove Project Facets...
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/plugin.xml b/plugins/org.eclipse.wst.common.project.facet.ui/plugin.xml
new file mode 100644
index 0000000..9429b79
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/plugin.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin
+  id="org.eclipse.wst.common.project.facet.ui"
+  name="Project Facet UI"
+  version="1.0"
+  class="org.eclipse.wst.common.project.facet.ui.internal.FacetUiPlugin">
+
+  <runtime>
+    <library name="facet-ui.jar">
+      <export name="*"/>
+    </library>
+  </runtime>
+  
+  <requires>
+    <import plugin="org.eclipse.ui"/>
+    <import plugin="org.eclipse.ui.ide"/>
+    <import plugin="org.eclipse.core.runtime"/>
+    <import plugin="org.eclipse.core.resources"/>
+    <import plugin="org.eclipse.jdt.core"/>
+    <import plugin="org.eclipse.jst.server.core"/>
+    <import plugin="org.eclipse.wst.server.core"/>
+    <import plugin="org.eclipse.jdt.launching"/>
+    <import plugin="org.eclipse.wst.common.project.facet.core"/>
+  </requires>
+  
+  <extension-point 
+    id="wizard"
+    name="Feature Wizard Pages Extension Point"
+    schema="schemas/wizard-pages.exsd"/>
+    
+  <extension
+    point="org.eclipse.ui.popupMenus">
+    <objectContribution
+      adaptable="true"
+      objectClass="org.eclipse.core.resources.IProject"
+      nameFilter="*"
+      id="org.eclipse.wst.common.project.facet.ui.AddRemoveProjectFacetsMenuItem">
+      <filter name="nature" value="org.eclipse.wst.common.project.facet.core.nature"/>
+      <action
+        label="%add.remove.facets"
+        class="org.eclipse.wst.common.project.facet.ui.internal.AddRemoveFacetsAction"
+        menubarPath="additions"
+        enablesFor="+"
+        id="org.eclipse.wst.common.project.facet.ui.AddRemoveProjectFacetsMenuAction"/>
+    </objectContribution>
+  </extension>
+   
+</plugin>
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/schemas/wizard-pages.exsd b/plugins/org.eclipse.wst.common.project.facet.ui/schemas/wizard-pages.exsd
new file mode 100644
index 0000000..3d1581e
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/schemas/wizard-pages.exsd
@@ -0,0 +1,155 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.wst.common.project.facet.ui">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.wst.common.project.facet.ui" id="wizard" name="Project Facet Wizard Pages Extension Point"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <complexType>
+         <sequence>
+            <element ref="wizard-pages" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="wizard-pages">
+      <complexType>
+         <sequence>
+            <element ref="install" minOccurs="0" maxOccurs="1"/>
+            <element ref="uninstall" minOccurs="0" maxOccurs="1"/>
+         </sequence>
+         <attribute name="facet" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="version" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="install">
+      <complexType>
+         <sequence>
+            <element ref="config"/>
+            <element ref="page" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+      </complexType>
+   </element>
+
+   <element name="uninstall">
+      <complexType>
+         <sequence>
+            <element ref="config"/>
+            <element ref="page" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+      </complexType>
+   </element>
+
+   <element name="page">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="config">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AbstractFacetWizardPage.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AbstractFacetWizardPage.java
new file mode 100644
index 0000000..fc614cb
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AbstractFacetWizardPage.java
@@ -0,0 +1,38 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui;
+
+import org.eclipse.jface.wizard.WizardPage;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public abstract class AbstractFacetWizardPage
+
+    extends WizardPage
+    implements IFacetWizardPage
+    
+{
+    protected IWizardContext context;
+    
+    public AbstractFacetWizardPage( final String name )
+    {
+        super( name );
+    }
+    
+    public final void setWizardContext( final IWizardContext context )
+    {
+        this.context = context;
+    }
+    
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AddRemoveFacetsWizard.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AddRemoveFacetsWizard.java
new file mode 100644
index 0000000..1e7d302
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AddRemoveFacetsWizard.java
@@ -0,0 +1,477 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject;
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject.Action;
+import org.eclipse.wst.common.project.facet.ui.internal.ConflictingFacetsFilter;
+import org.eclipse.wst.common.project.facet.ui.internal.FacetsSelectionPage;
+import org.eclipse.wst.common.project.facet.ui.internal.FacetsSelectionPanel;
+import org.eclipse.wst.common.project.facet.ui.internal.FacetUiPlugin;
+import org.eclipse.wst.common.project.facet.ui.internal.RuntimeBridge;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public class AddRemoveFacetsWizard 
+
+    extends Wizard 
+    
+{
+    protected IProject project;
+    
+    private final WizardContext context = new WizardContext(); 
+    protected FacetsSelectionPage facetsSelectionPage;
+    private FacetPages[] facetPages = new FacetPages[ 0 ];
+    private Composite pageContainer;
+    
+    public AddRemoveFacetsWizard( final IProject project )
+    {
+        // Temporary bridge.
+        
+        RuntimeBridge.port();
+
+        this.project = project;
+        
+        setNeedsProgressMonitor( true );
+        setForcePreviousAndNextButtons( true );
+        setWindowTitle( "Add/Remove Project Facets" );
+    }
+    
+    public void addPages()
+    {
+        this.facetsSelectionPage = new FacetsSelectionPage();
+        
+        if( this.project != null )
+        {
+            final IFacetedProject fproj
+                = ProjectFacetsManager.get().create( this.project );
+            
+            this.facetsSelectionPage.setInitialSelection( fproj.getProjectFacets() );
+            this.facetsSelectionPage.setFixedProjectFacets( fproj.getFixedProjectFacets());
+            this.facetsSelectionPage.setFilters( new FacetsSelectionPanel.IFilter[] { new ConflictingFacetsFilter( fproj.getFixedProjectFacets() ) } );
+            this.facetsSelectionPage.setRuntime( fproj.getRuntime() );
+        }
+        
+        this.facetsSelectionPage.addSelectedFacetsChangedListener
+        (
+            new Listener()
+            {
+                public void handleEvent( final Event event ) 
+                {
+                    handleSelectedFacetsChangedEvent();
+                }
+            }
+        );
+        
+        addPage( this.facetsSelectionPage );
+    }
+    
+    public int getPageCount()
+    {
+        return getPages().length;
+    }
+
+    public IWizardPage[] getPages()
+    {
+        final ArrayList list = new ArrayList();
+        
+        list.add( this.facetsSelectionPage );
+        
+        for( int i = 0; i < this.facetPages.length; i++ )
+        {
+            list.addAll( this.facetPages[ i ].pages );
+        }
+        
+        return (IWizardPage[]) list.toArray( new IWizardPage[ 0 ] );
+    }
+    
+    public IWizardPage getPage( final String pageName )
+    {
+        final IWizardPage[] pages = getPages();
+        
+        for( int i = 0; i < pages.length; i++ )
+        {
+            final IWizardPage page = pages[ i ];
+            
+            if( page.getName().equals( pageName ) )
+            {
+                return page;
+            }
+        }
+        
+        return null;
+    }
+    
+    public IWizardPage getStartingPage()
+    {
+        return getPages()[ 0 ];
+    }
+    
+    public IWizardPage getNextPage( final IWizardPage page )
+    {
+        final IWizardPage[] pages = getPages();
+        
+        int pos = -1;
+        
+        for( int i = 0; i < pages.length; i++ )
+        {
+            if( pages[ i ] == page )
+            {
+                pos = i;
+            }
+        }
+        
+        if( pos == pages.length - 1 )
+        {
+            return null;
+        }
+        else
+        {
+            return pages[ pos + 1 ];
+        }
+    }
+
+    public IWizardPage getPreviousPage( final IWizardPage page )
+    {
+        final IWizardPage[] pages = getPages();
+        
+        int pos = -1;
+        
+        for( int i = 0; i < pages.length; i++ )
+        {
+            if( pages[ i ] == page )
+            {
+                pos = i;
+            }
+        }
+        
+        if( pos == 0 )
+        {
+            return null;
+        }
+        else
+        {
+            return pages[ pos - 1 ];
+        }
+    }
+    
+    public boolean canFinish()
+    {
+        if( ! this.facetsSelectionPage.isPageComplete() )
+        {
+            return false;
+        }
+        
+        final IWizardPage[] pages = getPages();
+        
+        for( int i = 0; i < pages.length; i++ )
+        {
+            if( ! pages[ i ].isPageComplete() )
+            {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    public void createPageControls( final Composite container )
+    {
+        super.createPageControls( container );
+        this.pageContainer = container;
+        handleSelectedFacetsChangedEvent();
+    }
+    
+    public boolean performFinish() 
+    {
+        for( int i = 0; i < this.facetPages.length; i++ )
+        {
+            final FacetPages fp = this.facetPages[ i ];
+            
+            for( Iterator itr = fp.pages.iterator(); itr.hasNext(); )
+            {
+                ( (IFacetWizardPage) itr.next() ).transferStateToConfig();
+            }
+        }
+        
+        final Set actions = this.facetsSelectionPage.getActions();
+        
+        final WorkspaceModifyOperation op = new WorkspaceModifyOperation() 
+        {
+            protected void execute( final IProgressMonitor monitor )
+            
+                throws CoreException 
+                
+            {
+                monitor.beginTask( "", actions.size() );
+                
+                try
+                {
+                    performFinish( monitor );
+                }
+                finally
+                {
+                    monitor.done();
+                }
+            }
+        };
+
+        try 
+        {
+            getContainer().run( true, true, op );
+        }
+        catch( InterruptedException e ) 
+        {
+            return false;
+        } 
+        catch( InvocationTargetException e ) 
+        {
+            final Throwable te = e.getTargetException();
+            
+            if( te instanceof CoreException )
+            {
+                final CoreException ce = (CoreException) te;
+                
+                ErrorDialog.openError( getShell(), Resources.errDlgTitle,
+                                       ce.getMessage(), ce.getStatus() );
+                
+                FacetUiPlugin.log( ce );
+            }
+            else
+            {
+                throw new RuntimeException( e.getTargetException() );
+            }
+        }
+        
+        return true;
+    }
+
+    protected void performFinish( final IProgressMonitor monitor )
+    
+        throws CoreException
+        
+    {
+        monitor.beginTask( "", 2 );
+        
+        try
+        {
+            final IFacetedProject fopj 
+                = ProjectFacetsManager.get().create( AddRemoveFacetsWizard.this.project );
+            
+            fopj.setRuntime( this.facetsSelectionPage.getSelectedRuntime(),
+                             new SubProgressMonitor( monitor, 1 ) );
+            
+            fopj.modify( this.facetsSelectionPage.getActions(), 
+                         new SubProgressMonitor( monitor, 1 ) );
+        }
+        finally
+        {
+            monitor.done();
+        }
+    }
+    
+    public String getProjectName()
+    {
+        return this.project.getName();
+    }
+    
+    private static final class FacetPages
+    {
+        public Action action;
+        public List pages;
+    }
+    
+    private void handleSelectedFacetsChangedEvent()
+    {
+        // Get the set of actions and sort them.
+        
+        final Set base;
+        
+        if( this.project == null )
+        {
+            base = Collections.EMPTY_SET;
+        }
+        else
+        {
+            final IFacetedProject fproj
+                = ProjectFacetsManager.get().create( this.project );
+            
+            base = fproj.getProjectFacets();
+        }
+        
+        final Set actions = this.facetsSelectionPage.getActions();
+        final ArrayList sortedActions = new ArrayList( actions );
+        ProjectFacetsManager.get().sort( base, sortedActions );
+        
+        // Recalculate the sequence of wizard pages.
+        
+        final ArrayList newFacetPages = new ArrayList();
+        final boolean[] markers = new boolean[ this.facetPages.length ];
+        
+        for( Iterator itr1 = sortedActions.iterator(); itr1.hasNext(); )
+        {
+            final Action action = (Action) itr1.next();
+            final IProjectFacetVersion f = action.getProjectFacetVersion();
+            FacetPages fp = findFacetPages( action, markers );
+            
+            if( fp == null )
+            {
+                fp = new FacetPages();
+                fp.action = action;
+                fp.pages = ProjectFacetsUiManager.get().getWizardPages( action.getType(), f );
+                
+                for( Iterator itr2 = fp.pages.iterator(); itr2.hasNext(); )
+                {
+                    final IFacetWizardPage page 
+                        = (IFacetWizardPage) itr2.next();
+                    
+                    page.setWizard( this );
+                    page.setWizardContext( this.context );
+                    page.setConfig( action.getConfig() );
+                    
+                    if( page.getControl() == null ) 
+                    {
+                        page.createControl( this.pageContainer );
+                        page.getControl().setVisible( false );
+                    }
+                }
+            }
+            
+            newFacetPages.add( fp );
+        }
+        
+        for( int i = 0; i < this.facetPages.length; i++ )
+        {
+            if( ! markers[ i ] )
+            {
+                for( Iterator itr = this.facetPages[ i ].pages.iterator();
+                     itr.hasNext(); )
+                {
+                    final IFacetWizardPage page 
+                        = (IFacetWizardPage) itr.next();
+                    
+                    page.setWizard( null );
+                    page.dispose();
+                }
+            }
+        }
+        
+        this.facetPages = new FacetPages[ newFacetPages.size() ];
+        newFacetPages.toArray( this.facetPages );
+        
+        this.pageContainer.layout( true, true );
+    }
+    
+    private FacetPages findFacetPages( final Action action,
+                                           final boolean[] markers )
+    {
+        for( int i = 0; i < this.facetPages.length; i++ )
+        {
+            final FacetPages fp = this.facetPages[ i ];
+            
+            if( fp.action == action )
+            {
+                markers[ i ] = true;
+                return fp;
+            }
+        }
+        
+        return null;
+    }
+
+    /**
+     * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+     */
+
+    private final class WizardContext
+
+        implements IWizardContext
+        
+    {
+        public String getProjectName()
+        {
+            return AddRemoveFacetsWizard.this.getProjectName();
+        }
+
+        public Set getSelectedProjectFacets()
+        {
+            return AddRemoveFacetsWizard.this.facetsSelectionPage.getSelectedProjectFacets();
+        }
+
+        public boolean isProjectFacetSelected( final IProjectFacetVersion fv )
+        {
+            return getSelectedProjectFacets().contains( fv );
+        }
+
+        public Set getActions()
+        {
+            final FacetsSelectionPage page
+                = AddRemoveFacetsWizard.this.facetsSelectionPage;
+            
+            return page.getActions();
+        }
+
+        public Action getAction( final Action.Type type,
+                                 final IProjectFacetVersion f )
+        {
+            for( Iterator itr = getActions().iterator(); itr.hasNext(); )
+            {
+                final Action action = (Action) itr.next();
+                
+                if( action.getType() == type && action.getProjectFacetVersion() == f )
+                {
+                    return action;
+                }
+            }
+            
+            return null;
+        }
+    }
+    
+    private static final class Resources
+    
+        extends NLS
+        
+    {
+        public static String errDlgTitle;
+        
+        static
+        {
+            initializeMessages( AddRemoveFacetsWizard.class.getName(), 
+                                Resources.class );
+        }
+    }
+    
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AddRemoveFacetsWizard.properties b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AddRemoveFacetsWizard.properties
new file mode 100644
index 0000000..feb2801
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/AddRemoveFacetsWizard.properties
@@ -0,0 +1 @@
+errDlgTitle = Error
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/FacetedProjectWizard.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/FacetedProjectWizard.java
new file mode 100644
index 0000000..9a5d0c5
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/FacetedProjectWizard.java
@@ -0,0 +1,160 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui;
+
+import java.util.Set;
+
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject;
+import org.eclipse.wst.common.project.facet.core.IPreset;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+import org.eclipse.wst.common.project.facet.ui.internal.ConflictingFacetsFilter;
+import org.eclipse.wst.common.project.facet.ui.internal.FacetsSelectionPanel;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public abstract class FacetedProjectWizard 
+
+    extends AddRemoveFacetsWizard 
+    implements INewWizard
+    
+{
+    private static final String FACETED_PROJECT_NATURE
+        = "org.eclipse.wst.common.project.facet.core.nature";
+    
+    private WizardNewProjectCreationPage firstPage;
+    private IPath customPath;
+    
+    public FacetedProjectWizard()
+    {
+        super( null );
+        
+        this.setWindowTitle( getWindowTitleText() );
+        this.setDefaultPageImageDescriptor( getDefaultPageImageDescriptor() );
+    }
+    
+    public void init( final IWorkbench workbench, 
+                      final IStructuredSelection selection )
+    {
+        
+    }
+    
+    public void addPages()
+    {
+        this.firstPage = new WizardNewProjectCreationPage( "first.page" );
+        this.firstPage.setTitle( getPageTitle() );
+        this.firstPage.setDescription( getPageDescription() );
+    
+        addPage( this.firstPage );
+        
+        super.addPages();
+        
+        this.facetsSelectionPage.setInitialPreset( getInitialPreset() );
+        
+        final Set fixed = getFixedProjectFacets();
+        
+        this.facetsSelectionPage.setFixedProjectFacets( fixed );
+        
+        final ConflictingFacetsFilter filter 
+            = new ConflictingFacetsFilter( fixed );
+        
+        this.facetsSelectionPage.setFilters( new FacetsSelectionPanel.IFilter[] { filter } );
+    }
+    
+    public boolean canFinish()
+    {
+        return this.firstPage.isPageComplete() && super.canFinish();
+    }
+    
+    public IWizardPage[] getPages()
+    {
+        final IWizardPage[] base = super.getPages();
+        final IWizardPage[] pages = new IWizardPage[ base.length + 1 ];
+        
+        pages[ 0 ] = this.firstPage;
+        System.arraycopy( base, 0, pages, 1, base.length );
+        
+        return pages;
+    }
+    
+    public synchronized boolean performFinish() 
+    {
+        this.project = this.firstPage.getProjectHandle();
+
+        this.customPath
+            = this.firstPage.useDefaults() 
+              ? null : this.firstPage.getLocationPath();
+        
+        return super.performFinish();
+    }
+    
+    protected void performFinish( final IProgressMonitor monitor )
+    
+        throws CoreException
+        
+    {
+        final IWorkspace ws = ResourcesPlugin.getWorkspace();
+        
+        final IProjectDescription desc
+            = ws.newProjectDescription( this.project.getName() );
+
+        desc.setLocation( this.customPath );
+        desc.setNatureIds( new String[] { FACETED_PROJECT_NATURE } );
+                
+        this.project.create( desc, new SubProgressMonitor( monitor, 1 ) );
+                    
+        this.project.open( IResource.BACKGROUND_REFRESH,
+                           new SubProgressMonitor( monitor, 1 ) );
+        
+        super.performFinish( monitor );
+        
+        final IFacetedProject fproj
+            = ProjectFacetsManager.get().create( this.project );
+        
+        fproj.setFixedProjectFacets( getFixedProjectFacets() );
+    }
+    
+    public synchronized String getProjectName()
+    {
+        if( this.project == null )
+        {
+            return this.firstPage.getProjectName();
+        }
+        else
+        {
+            return this.project.getName();
+        }
+    }
+    
+    protected abstract String getWindowTitleText();
+    protected abstract String getPageTitle();
+    protected abstract String getPageDescription();
+    protected abstract ImageDescriptor getDefaultPageImageDescriptor();
+    protected abstract IPreset getInitialPreset();
+    protected abstract Set getFixedProjectFacets();
+
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IFacetWizardPage.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IFacetWizardPage.java
new file mode 100644
index 0000000..6b3a9ee
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IFacetWizardPage.java
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui;
+
+import org.eclipse.jface.wizard.IWizardPage;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public interface IFacetWizardPage
+
+    extends IWizardPage
+    
+{
+    void setWizardContext( final IWizardContext context );
+    void setConfig( final Object config );
+    void transferStateToConfig();
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IRuntimeComponentLabelProvider.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IRuntimeComponentLabelProvider.java
new file mode 100644
index 0000000..4148615
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IRuntimeComponentLabelProvider.java
@@ -0,0 +1,6 @@
+package org.eclipse.wst.common.project.facet.ui;
+
+public interface IRuntimeComponentLabelProvider
+{
+    String getLabel();
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IWizardContext.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IWizardContext.java
new file mode 100644
index 0000000..409d34e
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/IWizardContext.java
@@ -0,0 +1,36 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui;
+
+import java.util.Set;
+
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject.Action;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public interface IWizardContext 
+{
+    String getProjectName();
+    
+    Set getSelectedProjectFacets();
+    
+    boolean isProjectFacetSelected( IProjectFacetVersion fv );
+    
+    Set getActions();
+    
+    Action getAction( Action.Type type,
+                      IProjectFacetVersion f );
+    
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/ProjectFacetsUiManager.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/ProjectFacetsUiManager.java
new file mode 100644
index 0000000..3a84c8d
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/ProjectFacetsUiManager.java
@@ -0,0 +1,285 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject.Action;
+import org.eclipse.wst.common.project.facet.ui.internal.FacetUiPlugin;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public final class ProjectFacetsUiManager 
+{
+    private static final String EXTENSION_ID = "wizard";
+
+    private static ProjectFacetsUiManager instance = null;
+    
+    private final HashMap metadata;
+    
+    private ProjectFacetsUiManager()
+    {
+        this.metadata = new HashMap();
+        
+        readExtensions();
+    }
+    
+    public static ProjectFacetsUiManager get()
+    {
+        if( instance == null )
+        {
+            instance = new ProjectFacetsUiManager();
+        }
+        
+        return instance;
+    }
+    
+    public Object getConfig( final Action.Type actionType,
+                             final IProjectFacetVersion f )
+    {
+        final WizardPagesInfo info = (WizardPagesInfo) this.metadata.get( f );
+        
+        if( info != null )
+        {
+            final String clname = (String) info.configs.get( actionType );
+            
+            if( clname != null )
+            {
+                return create( info.plugin, clname );
+            }
+        }
+        
+        return null;
+    }
+    
+    /**
+     * @return (element type: {@see IFacetWizardPage})
+     */
+    
+    public List getWizardPages( final Action.Type actionType,
+                                final IProjectFacetVersion f )
+    {
+        final WizardPagesInfo info = (WizardPagesInfo) this.metadata.get( f );
+        
+        if( info != null )
+        {
+            final List clnames = (List) info.pagesets.get( actionType );
+            
+            if( clnames != null )
+            {
+                return getWizardPages( info.plugin, clnames );
+            }
+        }
+        
+        return Collections.EMPTY_LIST;
+    }
+
+    private List getWizardPages( final String plugin,
+                                 final List clnames )
+    {
+        final List pages = new ArrayList();
+        
+        for( Iterator itr = clnames.iterator(); itr.hasNext(); )
+        {
+            pages.add( create( plugin, (String) itr.next() ) );
+        }
+        
+        return pages;
+    }
+    
+    private Object create( final String plugin,
+                           final String clname )
+    {
+        final Bundle bundle = Platform.getBundle( plugin );
+        
+        try
+        {
+            final Class cl = bundle.loadClass( clname );
+            return cl.newInstance();
+        }
+        catch( Exception e )
+        {
+            // TODO: handle this better.
+            throw new RuntimeException( e );
+        }
+    }
+    
+    private void readExtensions()
+    {
+        final IExtensionRegistry registry = Platform.getExtensionRegistry();
+        
+        final IExtensionPoint point 
+            = registry.getExtensionPoint( FacetUiPlugin.PLUGIN_ID, 
+                                          EXTENSION_ID );
+        
+        if( point == null )
+        {
+            throw new RuntimeException( "Extension point not found!" );
+        }
+        
+        final IExtension[] extensions = point.getExtensions();
+        
+        for( int i = 0; i < extensions.length; i++ )
+        {
+            final IConfigurationElement[] elements 
+                = extensions[ i ].getConfigurationElements();
+            
+            for( int j = 0; j < elements.length; j++ )
+            {
+                final IConfigurationElement config = elements[ j ];
+                final String ename = config.getName();
+                
+                if( ename.equals( "wizard-pages" ) )
+                {
+                    readWizardPagesInfo( config );
+                }
+                else
+                {
+                    // TODO: handle this better.
+                    throw new IllegalStateException();
+                }
+            }
+        }
+    }
+    
+    private void readWizardPagesInfo( final IConfigurationElement config )
+    {
+        final String name = config.getAttribute( "facet" );
+
+        if( name == null )
+        {
+            // TODO: handle this better.
+            throw new IllegalStateException();
+        }
+
+        final String version = config.getAttribute( "version" );
+
+        if( version == null )
+        {
+            // TODO: handle this better.
+            throw new IllegalStateException();
+        }
+        
+        //TODO: Handle the case where the facet is not available.
+        
+        final IProjectFacetVersion fv
+            = ProjectFacetsManager.get().getProjectFacet( name ).getVersion( version );
+        
+        final WizardPagesInfo info = new WizardPagesInfo();
+        info.plugin = config.getDeclaringExtension().getNamespace();
+        
+        final IConfigurationElement[] children = config.getChildren();
+        
+        for( int i = 0; i < children.length; i++ )
+        {
+            final IConfigurationElement child = children[ i ];
+            final String childName = child.getName();
+            
+            final Action.Type actionType;
+            
+            if( childName.equals( "install" ) )
+            {
+                actionType = Action.Type.INSTALL;
+            }
+            else if( childName.equals( "uninstall" ) )
+            {
+                actionType = Action.Type.UNINSTALL;
+            }
+            else
+            {
+                // TODO: handle this better.
+                throw new IllegalStateException();
+            }
+            
+            info.configs.put( actionType, readConfigClass( child ) );
+            info.pagesets.put( actionType, readPageList( child ) );
+        }
+        
+        this.metadata.put( fv, info );
+    }
+
+    private String readConfigClass( final IConfigurationElement config )
+    {
+        final IConfigurationElement[] children = config.getChildren();
+        
+        for( int i = 0; i < children.length; i++ )
+        {
+            final IConfigurationElement child = children[ i ];
+            final String childName = child.getName();
+            
+            if( childName.equals( "config" ) )
+            {
+                final String clname = child.getAttribute( "class" );
+                
+                if( clname == null )
+                {
+                    // TODO: handle this better.
+                    throw new IllegalStateException();
+                }
+                
+                return clname;
+            }
+        }
+
+        // TODO: handle this better.
+        throw new IllegalStateException();
+    }
+    
+    private List readPageList( final IConfigurationElement config )
+    {
+        final ArrayList list = new ArrayList();
+        final IConfigurationElement[] children = config.getChildren();
+        
+        for( int i = 0; i < children.length; i++ )
+        {
+            final IConfigurationElement child = children[ i ];
+            final String childName = child.getName();
+            
+            if( childName.equals( "page" ) )
+            {
+                final String clname = child.getAttribute( "class" );
+                
+                if( clname == null )
+                {
+                    // TODO: handle this better.
+                    throw new IllegalStateException();
+                }
+                
+                list.add( clname );
+            }
+        }
+        
+        return list;
+    }
+    
+    private static class WizardPagesInfo
+    {
+        public String plugin;
+        public HashMap configs = new HashMap();
+        public HashMap pagesets = new HashMap();
+    }
+    
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/AddRemoveFacetsAction.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/AddRemoveFacetsAction.java
new file mode 100644
index 0000000..6cb6a62
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/AddRemoveFacetsAction.java
@@ -0,0 +1,65 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui.internal;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.wst.common.project.facet.ui.AddRemoveFacetsWizard;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public final class AddRemoveFacetsAction
+
+    implements IObjectActionDelegate
+    
+{
+    private Shell shell = null;
+    private ISelection selection = null;
+    
+    public void setActivePart( final IAction action, 
+                               final IWorkbenchPart targetPart ) 
+    {
+        this.shell = targetPart.getSite().getShell();
+    }
+
+    public void run( final IAction action ) 
+    {
+        if( this.selection instanceof IStructuredSelection )
+        {
+            final IStructuredSelection ssel 
+                = (IStructuredSelection) this.selection;
+            
+            final IProject project = (IProject) ssel.getFirstElement();
+            
+            final IWizard wizard = new AddRemoveFacetsWizard( project );
+            final WizardDialog dialog = new WizardDialog( this.shell, wizard );
+            
+            dialog.open();
+        }
+    }
+
+    public void selectionChanged( final IAction action, 
+                                  final ISelection selection ) 
+    {
+        this.selection = selection;
+    }
+
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/ConflictingFacetsFilter.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/ConflictingFacetsFilter.java
new file mode 100644
index 0000000..fe0631c
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/ConflictingFacetsFilter.java
@@ -0,0 +1,149 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui.internal;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.wst.common.project.facet.core.IConstraint;
+import org.eclipse.wst.common.project.facet.core.IGroup;
+import org.eclipse.wst.common.project.facet.core.IProjectFacet;
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public final class ConflictingFacetsFilter 
+
+    implements FacetsSelectionPanel.IFilter
+    
+{
+    private final Set fixed;
+    
+    public ConflictingFacetsFilter( final Set fixed )
+    {
+        this.fixed = fixed;
+    }
+    
+    public boolean check( final IProjectFacetVersion fv )
+    {
+        for( Iterator itr = this.fixed.iterator(); itr.hasNext(); )
+        {
+            final IProjectFacet f = (IProjectFacet) itr.next();
+            
+            if( f.getVersions().contains( fv ) )
+            {
+                return true;
+            }
+        }
+        
+        return check( fv.getConstraint() );
+    }
+    
+    private boolean check( final IConstraint op )
+    {
+        if( op.getType() == IConstraint.Type.AND )
+        {
+            for( Iterator itr = op.getOperands().iterator(); itr.hasNext(); )
+            {
+                if( ! check( (IConstraint) itr.next() ) )
+                {
+                    return false;
+                }
+            }
+            
+            return true;
+        }
+        else if( op.getType() == IConstraint.Type.OR )
+        {
+            for( Iterator itr = op.getOperands().iterator(); itr.hasNext(); )
+            {
+                if( check( (IConstraint) itr.next() ) )
+                {
+                    return true;
+                }
+            }
+            
+            return false;
+        }
+        else if( op.getType() == IConstraint.Type.CONFLICTS )
+        {
+            final String set = (String) op.getOperand( 0 );
+            final IGroup fset = ProjectFacetsManager.get().getGroup( set );
+            
+            for( Iterator itr = this.fixed.iterator(); itr.hasNext(); )
+            {
+                final IProjectFacet f = (IProjectFacet) itr.next();
+                
+                if( fset.getMembers().containsAll( f.getVersions() ) )
+                {
+                    return false;
+                }
+            }
+            
+            return true;
+        }
+        else if( op.getType() == IConstraint.Type.REQUIRES )
+        {
+            final String name = (String) op.getOperand( 0 );
+            final String version = (String) op.getOperand( 1 );
+            
+            final boolean allowNewer 
+                = ( (Boolean) op.getOperand( 2 ) ).booleanValue();
+            
+            final boolean soft
+                = ( (Boolean) op.getOperand( 3 ) ).booleanValue();
+        
+            if( soft )
+            {
+                return true;
+            }
+            else
+            {
+                final IProjectFacet rf 
+                    = ProjectFacetsManager.get().getProjectFacet( name );
+                
+                final Set versions = rf.getVersions();
+                final Comparator comp = rf.getVersionComparator();
+                
+                for( Iterator itr = versions.iterator(); itr.hasNext(); )
+                {
+                    final IProjectFacetVersion fv 
+                        = (IProjectFacetVersion) itr.next();
+                    
+                    final String fvstr = fv.getVersionString();
+                    
+                    final int compres = comp.compare( fvstr, version );
+                    
+                    if( ( allowNewer && compres >= 0 ) ||
+                        ( ! allowNewer && compres == 0 ) )
+                    {
+                        if( check( fv ) )
+                        {
+                            return true;
+                        }
+                    }
+                }
+            
+                return false;
+            }
+        }
+        else
+        {
+            throw new IllegalStateException();
+        }
+    }
+
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetUiPlugin.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetUiPlugin.java
new file mode 100644
index 0000000..587c56e
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetUiPlugin.java
@@ -0,0 +1,52 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui.internal;
+
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public final class FacetUiPlugin 
+
+    extends AbstractUIPlugin 
+    
+{
+    public static final String PLUGIN_ID 
+        = "org.eclipse.wst.common.project.facet.ui";
+    
+    private static FacetUiPlugin plugin;
+    
+    public FacetUiPlugin() 
+    {
+        super();
+        plugin = this;
+    }
+    
+    public static FacetUiPlugin getInstance()
+    {
+        return plugin;
+    }
+    
+    public static void log( final Exception e )
+    {
+        final ILog log = getInstance().getLog();
+        final String msg = e.getMessage();
+        
+        log.log( new Status( IStatus.ERROR, PLUGIN_ID, IStatus.OK, msg, e ) );
+    }
+    
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetsSelectionPage.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetsSelectionPage.java
new file mode 100644
index 0000000..26c73f3
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetsSelectionPage.java
@@ -0,0 +1,199 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui.internal;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.wst.common.project.facet.core.ICategory;
+import org.eclipse.wst.common.project.facet.core.IPreset;
+import org.eclipse.wst.common.project.facet.core.IProjectFacet;
+import org.eclipse.wst.common.project.facet.core.runtime.IRuntime;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public final class FacetsSelectionPage
+
+    extends WizardPage
+
+{
+    private IPreset initialPreset;
+    private Set initialSelection;
+    private final Set fixed;
+    private FacetsSelectionPanel.IFilter[] filters;
+    private IRuntime runtime;
+    private FacetsSelectionPanel panel;
+    private ArrayList listeners;
+
+    public FacetsSelectionPage()
+    {
+        super( "facets.selection.page" );
+
+        setTitle( "Select Project Facets" );
+        setDescription( "Select facets for this project." );
+
+        this.initialPreset = null;
+        this.initialSelection = null;
+        this.fixed = new HashSet();
+        this.filters = new FacetsSelectionPanel.IFilter[ 0 ];
+        this.runtime = null;
+        this.listeners = new ArrayList();
+    }
+
+    public void setInitialPreset( final IPreset preset )
+    {
+        this.initialPreset = preset;
+    }
+    
+    public void setInitialSelection( final Set sel )
+    {
+        this.initialSelection = sel;
+    }
+
+    public void setFixedProjectFacets( final Set fixed )
+    {
+        this.fixed.clear();
+        this.fixed.addAll( fixed );
+    }
+
+    public void setFilters( final FacetsSelectionPanel.IFilter[] filters )
+    {
+        this.filters = filters;
+    }
+    
+    public void setRuntime( final IRuntime runtime )
+    {
+        this.runtime = runtime;
+    }
+    
+    public Set getActions()
+    {
+        return this.panel.getActions();
+    }
+
+    public Set getSelectedProjectFacets()
+    {
+        return this.panel.getSelectedProjectFacets();
+    }
+
+    public void addSelectedFacetsChangedListener( final Listener listener )
+    {
+        this.listeners.add( listener );
+    }
+
+    public void removeSelectedFacetsChangedListener( final Listener listener )
+    {
+        this.listeners.remove( listener );
+    }
+    
+    public IRuntime getSelectedRuntime()
+    {
+        return this.panel.getSelectedRuntime();
+    }
+
+    public void createControl( final Composite parent )
+    {
+        this.panel 
+            = new FacetsSelectionPanel( parent, SWT.NONE, this.runtime );
+
+        this.panel.setFixedProjectFacets( this.fixed );
+        
+        if( this.initialPreset != null )
+        {
+            this.panel.selectPreset( this.initialPreset );
+        }
+        
+        if( this.initialSelection != null )
+        {
+            this.panel.setSelectedProjectFacets( this.initialSelection );
+        }
+
+        for( int i = 0; i < this.filters.length; i++ )
+        {
+            this.panel.addFilter( this.filters[ i ] );
+        }
+        
+        this.panel.addSelectionChangedListener
+        (
+            new ISelectionChangedListener()
+            {
+                public void selectionChanged( final SelectionChangedEvent e )
+                {
+                    handleSelectionChangedEvent( e );
+                }
+            }
+        );
+
+        this.panel.addListener
+        (
+            new Listener()
+            {
+                public void handleEvent( final Event event )
+                {
+                    handleSelectedFacetsChangedEvent( event );
+                }
+            }
+        );
+
+        setControl( this.panel );
+    }
+
+    private void handleSelectionChangedEvent( final SelectionChangedEvent e )
+    {
+        final IStructuredSelection ss
+            = (IStructuredSelection) e.getSelection();
+
+        final Object sel = ss.getFirstElement();
+
+        if( sel != null )
+        {
+            final String desc;
+
+            if( sel instanceof IProjectFacet )
+            {
+                desc = ( (IProjectFacet) sel ).getDescription();
+            }
+            else
+            {
+                desc = ( (ICategory) sel ).getDescription();
+            }
+
+            setDescription( desc );
+        }
+    }
+
+    private void handleSelectedFacetsChangedEvent( final Event event )
+    {
+        for( int i = 0, n = this.listeners.size(); i < n; i++ )
+        {
+            ( (Listener) this.listeners.get( i ) ).handleEvent( event );
+        }
+        
+        final boolean valid
+            = FacetsSelectionPage.this.panel.isSelectionValid();
+
+        setPageComplete( valid );
+    }
+
+}
+
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetsSelectionPanel.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetsSelectionPanel.java
new file mode 100644
index 0000000..2159a32
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/FacetsSelectionPanel.java
@@ -0,0 +1,1630 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui.internal;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ComboBoxCellEditor;
+import org.eclipse.jface.viewers.ICellModifier;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.wst.common.project.facet.core.ICategory;
+import org.eclipse.wst.common.project.facet.core.IPreset;
+import org.eclipse.wst.common.project.facet.core.IProjectFacet;
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject.Action;
+import org.eclipse.wst.common.project.facet.core.runtime.IRuntime;
+import org.eclipse.wst.common.project.facet.ui.ProjectFacetsUiManager;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public final class FacetsSelectionPanel
+
+    extends Composite
+    implements ISelectionProvider
+
+{
+    private static final String CW_FACET = "cw.facet";
+    private static final String CW_VERSION = "cw.version";
+    private static final String SASH1W1 = "sash.1.weight.1";
+    private static final String SASH1W2 = "sash.1.weight.2";
+    private static final String SASH2W1 = "sash.2.weight.1";
+    private static final String SASH2W2 = "sash.2.weight.2";
+    private static final Font FIXED_FONT;
+    
+    static
+    {
+        final FontData system 
+            = Display.getCurrent().getSystemFont().getFontData()[ 0 ];
+        
+        final FontData italic 
+            = new FontData( system.getName(), system.getHeight(), SWT.BOLD );
+        
+        FIXED_FONT = new Font( Display.getCurrent(), italic );
+    }
+
+    private final IDialogSettings settings;
+    private final SashForm sform1;
+    private final SashForm sform2;
+    private final Label presetsLabel;
+    private final Combo presetsCombo;
+    private final Button savePresetButton;
+    private final Button deletePresetButton;
+    private final CheckboxTreeViewer tree;
+    private final TreeColumn colFacet;
+    private final TreeColumn colVersion;
+    private final ComboBoxCellEditor ceditor;
+    private final TableViewer problemsView;
+    private final RuntimesPanel runtimesPanel;
+
+    /**
+     * Contains the <code>TableRowData</code> objects representing all of the
+     * facets, regardless whether they are displayed or not.
+     */
+
+    private final ArrayList data;
+    private final HashSet fixed;
+    private final HashSet base;
+    private final HashSet actions;
+    private final ArrayList presets;
+
+    private IStatus problems;
+    private final HashSet filters;
+    private final ArrayList listeners;
+    private final ArrayList selectionListeners;
+    
+    public interface IFilter 
+    {
+        boolean check( IProjectFacetVersion fv );
+    }
+
+    public FacetsSelectionPanel( final Composite parent,
+                                  final int style,
+                                  final IRuntime runtime )
+    {
+        super( parent, style );
+
+        this.data = new ArrayList();
+        this.fixed = new HashSet();
+        this.base = new HashSet();
+        this.actions = new HashSet();
+        this.presets = new ArrayList();
+        this.problems = Status.OK_STATUS;
+        this.filters = new HashSet();
+        this.listeners = new ArrayList();
+        this.selectionListeners = new ArrayList();
+
+        for( Iterator itr = ProjectFacetsManager.get().getProjectFacets().iterator();
+             itr.hasNext(); )
+        {
+            this.data.add( new TableRowData( (IProjectFacet) itr.next() ) );
+        }
+
+        // Read the dialog settings.
+
+        final IDialogSettings root
+            = FacetUiPlugin.getInstance().getDialogSettings();
+
+        IDialogSettings temp = root.getSection( getClass().getName() );
+
+        if( temp == null )
+        {
+            temp = root.addNewSection( getClass().getName() );
+        }
+        
+        if( temp.get( CW_FACET ) == null ) temp.put( CW_FACET, 200 );
+        if( temp.get( CW_VERSION ) == null ) temp.put( CW_VERSION, 100 );
+        if( temp.get( SASH1W1 ) == null ) temp.put( SASH1W1, 60 );
+        if( temp.get( SASH1W2 ) == null ) temp.put( SASH1W2, 40 );
+        if( temp.get( SASH2W1 ) == null ) temp.put( SASH2W1, 70 );
+        if( temp.get( SASH2W2 ) == null ) temp.put( SASH2W2, 30 );
+
+        this.settings = temp;
+
+        // Layout the panel.
+
+        setLayout( new GridLayout( 1, false ) );
+        
+        this.sform1 = new SashForm( this, SWT.HORIZONTAL | SWT.SMOOTH );
+        this.sform1.setLayoutData( gdfill() );
+
+        final Composite composite = new Composite( this.sform1, SWT.NONE );
+        composite.setLayoutData( gdhfill() );
+        
+        final GridLayout layout = new GridLayout( 4, false );
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        composite.setLayout( layout );
+        
+        this.presetsLabel = new Label( composite, SWT.NONE );
+        this.presetsLabel.setText( "Presets: " );
+        
+        this.presetsCombo = new Combo( composite, SWT.READ_ONLY );
+        this.presetsCombo.setLayoutData( gdhfill() );
+
+        this.presetsCombo.addSelectionListener
+        (
+            new SelectionAdapter()
+            {
+                public void widgetSelected( final SelectionEvent e ) 
+                {
+                    handlePresetSelected();
+                }
+            }
+        );
+        
+        this.savePresetButton = new Button( composite, SWT.PUSH );
+        this.savePresetButton.setText( "Save" );
+        this.savePresetButton.setLayoutData( whint( new GridData(), 60 ) );
+        
+        this.savePresetButton.addSelectionListener
+        (
+            new SelectionAdapter()
+            {
+                public void widgetSelected( final SelectionEvent e )
+                {
+                    handleSavePreset();
+                }
+            }
+        );
+
+        this.deletePresetButton = new Button( composite, SWT.PUSH );
+        this.deletePresetButton.setText( "Delete" );
+        this.deletePresetButton.setLayoutData( whint( new GridData(), 60 ) );
+        
+        this.deletePresetButton.addSelectionListener
+        (
+            new SelectionAdapter()
+            {
+                public void widgetSelected( final SelectionEvent e )
+                {
+                    handleDeletePreset();
+                }
+            }
+        );
+        
+        refreshPresetsCombo();
+        
+        this.sform2 = new SashForm( composite, SWT.VERTICAL | SWT.SMOOTH );
+        this.sform2.setLayoutData( hspan( gdfill(), 4 ) );
+        
+        this.tree = new CheckboxTreeViewer( this.sform2, SWT.BORDER );
+        this.tree.getTree().setHeaderVisible( true );
+
+        this.ceditor
+            = new ComboBoxCellEditor( this.tree.getTree(), new String[ 0 ],
+                                      SWT.READ_ONLY );
+
+        this.tree.setColumnProperties( new String[] { "facet", "version" } );
+        this.tree.setCellModifier( new CellModifier() );
+        this.tree.setCellEditors( new CellEditor[] { null, this.ceditor } );
+
+        this.tree.setContentProvider( new ContentProvider() );
+        this.tree.setLabelProvider( new LabelProvider() );
+        this.tree.setSorter( new Sorter() );
+        
+        this.colFacet = new TreeColumn( this.tree.getTree(), SWT.NONE );
+        this.colFacet.setText( "Project Facet" );
+        this.colFacet.setWidth( this.settings.getInt( CW_FACET ) );
+        this.colFacet.setResizable( true );
+        
+        this.colFacet.addListener
+        (
+            SWT.Resize,
+            new Listener()
+            {
+                public void handleEvent( final Event event )
+                {
+                    settings.put( CW_FACET, colFacet.getWidth() );
+                }
+            }
+        );
+
+        this.colVersion = new TreeColumn( this.tree.getTree(), SWT.NONE );
+        this.colVersion.setText( "Version" );
+        this.colVersion.setWidth( this.settings.getInt( CW_VERSION ) );
+        this.colVersion.setResizable( true );
+
+        this.colVersion.addListener
+        (
+            SWT.Resize,
+            new Listener()
+            {
+                public void handleEvent( final Event event )
+                {
+                    settings.put( CW_VERSION, colVersion.getWidth() );
+                }
+            }
+        );
+
+        this.tree.setInput( new Object() );
+
+        this.tree.addSelectionChangedListener
+        (
+            new ISelectionChangedListener()
+            {
+                public void selectionChanged( final SelectionChangedEvent e )
+                {
+                    FacetsSelectionPanel.this.selectionChanged( e );
+                }
+            }
+        );
+
+        this.tree.addCheckStateListener
+        (
+            new ICheckStateListener()
+            {
+                public void checkStateChanged( final CheckStateChangedEvent e )
+                {
+                    FacetsSelectionPanel.this.checkStateChanged( e );
+                }
+            }
+        );
+
+        this.tree.getTree().addListener
+        (
+            SWT.MouseDown,
+            new Listener()
+            {
+                public void handleEvent( final Event event )
+                {
+                    handleMouseDownEvent( event );
+                }
+            }
+        );
+
+        this.problemsView = new TableViewer( this.sform2, SWT.BORDER );
+        this.problemsView.setContentProvider( new ProblemsContentProvider() );
+        this.problemsView.setLabelProvider( new ProblemsLabelProvider() );
+        this.problemsView.setInput( new Object() );
+
+        this.problemsView.getTable().addListener
+        (
+            SWT.Resize,
+            new Listener()
+            {
+                public void handleEvent( final Event event )
+                {
+                    final int[] weights = sform2.getWeights();
+                    settings.put( SASH2W1, weights[ 0 ] );
+                    settings.put( SASH2W2, weights[ 1 ] );
+                }
+            }
+        );
+
+        final int[] weights2
+            = new int[] { this.settings.getInt( SASH2W1 ),
+                          this.settings.getInt( SASH2W2 ) };
+
+        this.sform2.setWeights( weights2 );
+        
+        this.runtimesPanel 
+            = new RuntimesPanel( this.sform1, SWT.NONE, this, runtime );
+        
+        this.runtimesPanel.setLayoutData( hhint( gdhfill(), 80 ) );
+        
+        this.runtimesPanel.addFilter
+        (
+            new RuntimesPanel.IFilter()
+            {
+                public boolean check( final IRuntime runtime )
+                {
+                    for( Iterator itr = getSelectedProjectFacets().iterator();
+                         itr.hasNext(); )
+                    {
+                        final IProjectFacetVersion fv
+                            = (IProjectFacetVersion) itr.next();
+                        
+                        if( ! runtime.supports( fv ) )
+                        {
+                            return false;
+                        }
+                    }
+                    
+                    return true;
+                }
+            }
+        );
+        
+        this.runtimesPanel.addListener
+        (
+            SWT.Resize,
+            new Listener()
+            {
+                public void handleEvent( final Event event )
+                {
+                    final int[] weights = sform1.getWeights();
+                    settings.put( SASH1W1, weights[ 0 ] );
+                    settings.put( SASH1W2, weights[ 1 ] );
+                }
+            }
+        );
+
+        final int[] weights1
+            = new int[] { this.settings.getInt( SASH1W1 ),
+                          this.settings.getInt( SASH1W2 ) };
+    
+        this.sform1.setWeights( weights1 );
+        
+        updateValidationDisplay();
+    }
+    
+    public boolean isSelectionValid()
+    {
+        return this.problems.isOK();
+    }
+    
+    public Set getActions()
+    {
+        return this.actions;
+    }
+    
+    public Action getAction( final Action.Type type,
+                             final IProjectFacetVersion f )
+    {
+        return getAction( this.actions, type, f );
+    }
+    
+    private static Action getAction( final Set actions,
+                                     final Action.Type type,
+                                     final IProjectFacetVersion f )
+    {
+        for( Iterator itr = actions.iterator(); itr.hasNext(); )
+        {
+            final Action action = (Action) itr.next();
+            
+            if( action.getType() == type && action.getProjectFacetVersion() == f )
+            {
+                return action;
+            }
+        }
+        
+        return null;
+    }
+    
+    private Action createAction( final Set actions,
+                                 final Action.Type type,
+                                 final IProjectFacetVersion f )
+    {
+        Action action = getAction( actions, type, f );
+        
+        if( action == null )
+        {
+            final Object config = ProjectFacetsUiManager.get().getConfig( type, f );
+            action = new Action( type, f, config );
+        }
+        
+        return action;
+    }
+    
+    public IRuntime getSelectedRuntime()
+    {
+        return this.runtimesPanel.getSelectedRuntime();
+    }
+
+    public Set getSelectedProjectFacets()
+    {
+        final HashSet set = new HashSet();
+
+        for( int i = 0, n = this.data.size(); i < n; i++ )
+        {
+            final TableRowData trd = (TableRowData) this.data.get( i );
+
+            if( trd.isSelected() )
+            {
+                set.add( trd.getCurrentVersion() );
+            }
+        }
+
+        return set;
+    }
+
+    public void setSelectedProjectFacets( final Set sel )
+    {
+        for( Iterator itr = sel.iterator(); itr.hasNext(); )
+        {
+            final IProjectFacetVersion fv 
+                = (IProjectFacetVersion) itr.next();
+            
+            final IProjectFacet f = fv.getProjectFacet();
+            final TableRowData trd = findTableRowData( f );
+
+            trd.setSelected( true );
+            trd.setCurrentVersion( fv );
+
+            this.tree.setChecked( trd, true );
+            
+            this.base.add( fv );
+        }
+
+        this.tree.refresh();
+        this.runtimesPanel.refresh();
+        updateValidationDisplay();
+    }
+    
+    public void selectPreset( final IPreset preset )
+    {
+        if( preset != null )
+        {
+            final int index = this.presets.indexOf( preset );
+            
+            if( index == -1 )
+            {
+                throw new IllegalArgumentException();
+            }
+            
+            this.presetsCombo.select( index + 1 );
+            handlePresetSelected();
+        }
+    }
+
+    public void setFixedProjectFacets( final Set fixed )
+    {
+        for( int i = 0, n = this.data.size(); i < n; i++ )
+        {
+            ( (TableRowData) this.data.get( i ) ).setFixed( false );
+        }
+
+        for( Iterator itr = fixed.iterator(); itr.hasNext(); )
+        {
+            final IProjectFacet f = (IProjectFacet) itr.next();
+            final TableRowData trd = findTableRowData( f );
+            
+            this.fixed.add( f );
+            trd.setFixed( true );
+            trd.setSelected( true );
+            this.tree.setChecked( trd, true );
+        }
+
+        this.tree.refresh();
+        this.runtimesPanel.refresh();
+        refreshPresetsCombo();
+        updateValidationDisplay();
+    }
+    
+    public void addFilter( final IFilter filter )
+    {
+        this.filters.add( filter );
+        this.tree.refresh();
+    }
+
+    public void removeFilter( final IFilter filter )
+    {
+        this.filters.remove( filter );
+        this.tree.refresh();
+    }
+
+    public void addListener( final Listener listener )
+    {
+        this.listeners.add( listener );
+    }
+
+    public void removeListener( final Listener listener )
+    {
+        this.listeners.remove( listener );
+    }
+
+    private void notifyListeners()
+    {
+        for( int i = 0, n = this.listeners.size(); i < n; i++ )
+        {
+            ( (Listener) this.listeners.get( i ) ).handleEvent( null );
+        }
+    }
+
+    public void addSelectionChangedListener( final ISelectionChangedListener listener )
+    {
+        this.selectionListeners.add( listener );
+    }
+
+    public void removeSelectionChangedListener( final ISelectionChangedListener listener )
+    {
+        this.selectionListeners.remove( listener );
+    }
+
+    public ISelection getSelection()
+    {
+        final IStructuredSelection ss
+            = (IStructuredSelection) this.tree.getSelection();
+
+        Object sel = ss.getFirstElement();
+
+        if( sel instanceof TableRowData )
+        {
+            sel = ( (TableRowData) sel ).getProjectFacet();
+        }
+
+        if( sel == null )
+        {
+            return new StructuredSelection( new Object[ 0 ] );
+        }
+        else
+        {
+            return new StructuredSelection( sel );
+        }
+    }
+
+    public void setSelection( final ISelection selection )
+    {
+        final IStructuredSelection ss = (IStructuredSelection) selection;
+        final Object sel = ss.getFirstElement();
+        final ISelection ts;
+
+        if( sel == null )
+        {
+            ts = new StructuredSelection( new Object[ 0 ] );
+        }
+        else
+        {
+            if( sel instanceof IProjectFacet )
+            {
+                final TableRowData trd
+                    = findTableRowData( (IProjectFacet) sel );
+
+                ts = new StructuredSelection( trd );
+            }
+            else
+            {
+                ts = selection;
+            }
+        }
+
+        this.tree.setSelection( ts );
+    }
+
+    public void notifySelectionChangedListeners()
+    {
+        final SelectionChangedEvent event
+            = new SelectionChangedEvent( this, getSelection() );
+
+        for( int i = 0, n = this.selectionListeners.size(); i < n; i++ )
+        {
+            final ISelectionChangedListener listener
+                = (ISelectionChangedListener) this.selectionListeners.get( i );
+
+            listener.selectionChanged( event );
+        }
+    }
+
+    private void selectionChanged( final SelectionChangedEvent event )
+    {
+        final Object selection
+            = ( (IStructuredSelection) event.getSelection() ).getFirstElement();
+
+        if( selection instanceof TableRowData )
+        {
+            final TableRowData trd = (TableRowData) selection;
+
+            if( trd == null )
+            {
+                return;
+            }
+
+            // Reset the contents of the combo box cell editor to contain the
+            // versions of the selected project facet.
+
+            final IProjectFacetVersion[] versions = trd.getVersions();
+            final String[] verstrs = new String[ versions.length ];
+
+            for( int i = 0; i < versions.length; i++ )
+            {
+                verstrs[ i ] = versions[ i ].getVersionString();
+            }
+
+            this.ceditor.setItems( verstrs );
+
+            for( int i = 0; i < versions.length; i++ )
+            {
+                if( versions[ i ] == trd.getCurrentVersion() )
+                {
+                    this.ceditor.setValue( new Integer( i ) );
+                    break;
+                }
+            }
+        }
+
+        notifySelectionChangedListeners();
+    }
+
+    private void checkStateChanged( final CheckStateChangedEvent event )
+    {
+        final Object el = event.getElement();
+        final boolean checked = event.getChecked();
+
+        if( el instanceof TableRowData )
+        {
+            final TableRowData trd = (TableRowData) el;
+            
+            if( trd.isFixed() )
+            {
+                if( ! checked )
+                {
+                    this.tree.setChecked( el, true );
+                }
+                
+                return;
+            }
+            
+            trd.setSelected( checked );
+            refreshCategoryState( trd );
+        }
+        else
+        {
+            final ContentProvider cp
+                = (ContentProvider) this.tree.getContentProvider();
+
+            final Object[] children = cp.getChildren( el );
+
+            for( int i = 0; i < children.length; i++ )
+            {
+                final TableRowData trd = (TableRowData) children[ i ];
+                trd.setSelected( checked );
+                this.tree.setChecked( trd, checked );
+            }
+
+            this.tree.setGrayed( el, false );
+        }
+
+        updateValidationDisplay();
+        this.runtimesPanel.refresh();
+        
+        this.presetsCombo.select( 0 );
+        refreshPresetsButtons();
+    }
+
+    private void updateValidationDisplay()
+    {
+        final Set sel = getSelectedProjectFacets();
+        final Set old = new HashSet( this.actions );
+        this.actions.clear();
+        
+        // What has been removed?
+        
+        for( Iterator itr = base.iterator(); itr.hasNext(); )
+        {
+            final IProjectFacetVersion f
+                = (IProjectFacetVersion) itr.next();
+            
+            if( ! sel.contains( f ) )
+            {
+                this.actions.add( createAction( old, Action.Type.UNINSTALL, f ) );
+            }
+        }
+
+        // What has been added?
+        
+        for( Iterator itr = sel.iterator(); itr.hasNext(); )
+        {
+            final IProjectFacetVersion f 
+                = (IProjectFacetVersion) itr.next();
+            
+            if( ! base.contains( f ) )
+            {
+                this.actions.add( createAction( old, Action.Type.INSTALL, f ) );
+            }
+        }
+        
+        // Coalesce uninstall/install pairs into version change actions, if
+        // possible.
+        
+        final HashSet toadd = new HashSet();
+        final HashSet toremove = new HashSet();
+        
+        for( Iterator itr1 = this.actions.iterator(); itr1.hasNext(); )
+        {
+            final Action action1 = (Action) itr1.next();
+            
+            for( Iterator itr2 = this.actions.iterator(); itr2.hasNext(); )
+            {
+                final Action action2 = (Action) itr2.next();
+                
+                if( action1.getType() == Action.Type.UNINSTALL &&
+                    action2.getType() == Action.Type.INSTALL )
+                {
+                    final IProjectFacetVersion f1 = action1.getProjectFacetVersion();
+                    final IProjectFacetVersion f2 = action2.getProjectFacetVersion();
+                    
+                    if( f1.getProjectFacet() == f2.getProjectFacet() )
+                    {
+                        if( f2.supports( Action.Type.VERSION_CHANGE ) )
+                        {
+                            toremove.add( action1 );
+                            toremove.add( action2 );
+                            toadd.add( createAction( old, Action.Type.VERSION_CHANGE, f2 ) );
+                        }
+                    }
+                }
+            }
+        }
+        
+        this.actions.removeAll( toremove );
+        this.actions.addAll( toadd );
+        
+        this.problems = ProjectFacetsManager.get().check( this.base, this.actions );
+        this.problemsView.refresh();
+
+        if( this.problems.isOK() )
+        {
+            this.sform2.setMaximizedControl( this.tree.getTree() );
+        }
+        else
+        {
+            this.sform2.setMaximizedControl( null );
+        }
+
+        notifyListeners();
+    }
+    
+    private void refreshPresetsCombo()
+    {
+        this.presetsCombo.removeAll();
+        this.presets.clear();
+        
+        for( Iterator itr1 = ProjectFacetsManager.get().getPresets().iterator(); 
+             itr1.hasNext(); )
+        {
+            final IPreset preset = (IPreset) itr1.next();
+            final HashSet temp = new HashSet();
+            
+            for( Iterator itr2 = preset.getProjectFacets().iterator(); 
+                 itr2.hasNext(); )
+            {
+                final IProjectFacetVersion fv
+                    = (IProjectFacetVersion) itr2.next();
+                
+                temp.add( fv.getProjectFacet() );
+            }
+            
+            if( temp.containsAll( this.fixed ) )
+            {
+                this.presets.add( preset );
+            }
+        }
+        
+        Collections.sort
+        (
+            this.presets,
+            new Comparator()
+            {
+                public int compare( final Object p1, 
+                                    final Object p2 ) 
+                {
+                    if( p1 == p2 )
+                    {
+                        return 0;
+                    }
+                    else
+                    {
+                        final String label1 = ( (IPreset) p1 ).getLabel();
+                        final String label2 = ( (IPreset) p2 ).getLabel();
+                        
+                        return label1.compareTo( label2 );
+                    }
+                }
+            }
+        );
+        
+        this.presetsCombo.add( "<custom>" );
+        
+        for( Iterator itr = this.presets.iterator(); itr.hasNext(); )
+        {
+            final IPreset preset = (IPreset) itr.next();
+            this.presetsCombo.add( preset.getLabel() );
+        }
+        
+        this.presetsCombo.select( 0 );
+        refreshPresetsButtons();
+    }
+    
+    private void refreshPresetsButtons()
+    {
+        final int selection = this.presetsCombo.getSelectionIndex();
+        
+        if( selection == 0 )
+        {
+            this.savePresetButton.setEnabled( true );
+            this.deletePresetButton.setEnabled( false );
+        }
+        else
+        {
+            final IPreset preset = (IPreset) this.presets.get( selection - 1 );
+            
+            this.savePresetButton.setEnabled( false );
+            this.deletePresetButton.setEnabled( preset.isUserDefined() );
+        }
+    }
+    
+    private void refreshCategoryState( final TableRowData trd )
+    {
+        final ICategory category = trd.getProjectFacet().getCategory();
+        
+        if( category != null )
+        {
+            int selected = 0;
+    
+            for( Iterator itr = category.getProjectFacets().iterator(); 
+                 itr.hasNext(); )
+            {
+                final TableRowData ctrd 
+                    = findTableRowData( (IProjectFacet) itr.next() );
+    
+                if( ctrd.isSelected() )
+                {
+                    selected++;
+                }
+            }
+    
+            if( selected == 0 )
+            {
+                this.tree.setChecked( category, false );
+                this.tree.setGrayed( category, false );
+            }
+            else if( selected == category.getProjectFacets().size() )
+            {
+                this.tree.setChecked( category, true );
+                this.tree.setGrayed( category, false );
+            }
+            else
+            {
+                this.tree.setGrayChecked( category, true );
+            }
+        }
+    }
+
+    private boolean isFilteredOut( final IProjectFacetVersion fv )
+    {
+        for( Iterator itr = FacetsSelectionPanel.this.filters.iterator();
+             itr.hasNext(); )
+        {
+            if( ! ( (IFilter) itr.next() ).check( fv ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private TableRowData findTableRowData( final IProjectFacet f )
+    {
+        for( int i = 0, n = this.data.size(); i < n; i++ )
+        {
+            final TableRowData trd = (TableRowData) this.data.get( i );
+
+            if( trd.getProjectFacet() == f )
+            {
+                return trd;
+            }
+        }
+
+        throw new IllegalStateException();
+    }
+
+    private void handleMouseDownEvent( final Event event )
+    {
+        final ArrayList items = getAllTreeItems();
+
+        for( int i = 0, n = items.size(); i < n; i++ )
+        {
+            final TreeItem item = (TreeItem) items.get( i );
+
+            if( item.getBounds( 1 ).contains( event.x, event.y ) )
+            {
+                this.tree.getTree().setSelection( new TreeItem[] { item } );
+                this.tree.editElement( item.getData(), 1 );
+                break;
+            }
+        }
+    }
+    
+    private void handlePresetSelected()
+    {
+        final int selection = this.presetsCombo.getSelectionIndex();
+        
+        if( selection > 0 )
+        {
+            final IPreset preset = (IPreset) this.presets.get( selection - 1 );
+            final Set selected = new HashSet();
+            
+            for( Iterator itr = preset.getProjectFacets().iterator(); 
+                 itr.hasNext(); )
+            {
+                final IProjectFacetVersion fv 
+                    = (IProjectFacetVersion) itr.next();
+                
+                final TableRowData trd 
+                    = findTableRowData( fv.getProjectFacet() );
+                
+                if( ! trd.isSelected() )
+                {
+                    this.tree.setChecked( trd, true );
+                    trd.setSelected( true );
+                    refreshCategoryState( trd );
+                }
+                
+                if( trd.getCurrentVersion() != fv )
+                {
+                    trd.setCurrentVersion( fv );
+                    this.tree.update( trd, null );
+                }
+                
+                selected.add( trd );
+            }
+
+            for( Iterator itr = this.data.iterator(); itr.hasNext(); )
+            {
+                final TableRowData trd = (TableRowData) itr.next();
+                
+                if( ! selected.contains( trd ) )
+                {
+                    this.tree.setChecked( trd, false );
+                    trd.setSelected( false );
+                    refreshCategoryState( trd );
+                }
+            }
+        }
+        
+        refreshPresetsButtons();
+        updateValidationDisplay();
+        this.runtimesPanel.refresh();
+    }
+    
+    private void handleSavePreset()
+    {
+        final InputDialog dialog 
+            = new InputDialog( getShell(), "Save Preset", 
+                               "Enter the name for the preset.",
+                               null, null );
+        
+        if( dialog.open() == IDialogConstants.OK_ID )
+        {
+            final String name = dialog.getValue();
+            final Set facets = getSelectedProjectFacets();
+            
+            final IPreset preset
+                = ProjectFacetsManager.get().definePreset( name, facets );
+            
+            refreshPresetsCombo();
+            
+            final int pos = this.presets.indexOf( preset );
+            this.presetsCombo.select( pos + 1 );
+            refreshPresetsButtons();
+        }
+    }
+    
+    private void handleDeletePreset()
+    {
+        final int selection = this.presetsCombo.getSelectionIndex();
+        final IPreset preset = (IPreset) this.presets.get( selection - 1 );
+        
+        ProjectFacetsManager.get().deletePreset( preset );
+        
+        refreshPresetsCombo();
+    }
+    
+    private ArrayList getAllTreeItems()
+    {
+        final ArrayList result = new ArrayList();
+        getAllTreeItems( this.tree.getTree().getItems(), result );
+        return result;
+    }
+
+    private static void getAllTreeItems( final TreeItem[] items,
+                                         final ArrayList result)
+    {
+        for( int i = 0; i < items.length; i++ )
+        {
+            final TreeItem item = items[ i ];
+            result.add( item );
+
+            getAllTreeItems( item.getItems(), result );
+        }
+    }
+    
+    private final class TableRowData
+    {
+        private IProjectFacet f;
+        private List versions;
+        private IProjectFacetVersion current;
+        private boolean isSelected;
+        private boolean isFixed;
+
+        public TableRowData( final IProjectFacet f )
+        {
+            this.f = f;
+            this.versions = f.getSortedVersions( false );
+            this.current = f.getLatestVersion();
+            this.isSelected = false;
+            this.isFixed = false;
+        }
+
+        public IProjectFacet getProjectFacet()
+        {
+            return this.f;
+        }
+
+        public IProjectFacetVersion[] getVersions()
+        {
+            final ArrayList list = new ArrayList();
+
+            for( Iterator itr = this.versions.iterator(); itr.hasNext(); )
+            {
+                final IProjectFacetVersion fv 
+                    = (IProjectFacetVersion) itr.next();
+
+                if( ! isFilteredOut( fv ) )
+                {
+                    list.add( fv );
+                }
+            }
+
+            final IProjectFacetVersion[] array
+                = new IProjectFacetVersion[ list.size() ];
+
+            return (IProjectFacetVersion[]) list.toArray( array );
+        }
+
+        public IProjectFacetVersion getCurrentVersion()
+        {
+            if( isFilteredOut( this.current ) )
+            {
+                this.current = getVersions()[ 0 ];
+            }
+
+            return this.current;
+        }
+
+        public void setCurrentVersion( final IProjectFacetVersion fv )
+        {
+            this.current = fv;
+        }
+
+        public boolean isSelected()
+        {
+            if( getVersions().length == 0 )
+            {
+                this.isSelected = false;
+            }
+
+            return this.isSelected;
+        }
+
+        public void setSelected( final boolean isSelected )
+        {
+            this.isSelected = isSelected;
+        }
+
+        public boolean isFixed()
+        {
+            return this.isFixed;
+        }
+
+        public void setFixed( final boolean isFixed )
+        {
+            this.isFixed = isFixed;
+        }
+
+        public boolean isVisible()
+        {
+            return getVersions().length > 0;
+        }
+    }
+
+    private final class ContentProvider
+
+        implements ITreeContentProvider
+
+    {
+        public Object[] getElements( final Object element )
+        {
+            final ArrayList list = new ArrayList();
+            final Set categories = ProjectFacetsManager.get().getCategories();
+
+            for( Iterator itr1 = categories.iterator(); itr1.hasNext(); )
+            {
+                boolean visible = false;
+                
+                final ICategory cat = (ICategory) itr1.next();
+
+                for( Iterator itr2 = cat.getProjectFacets().iterator(); 
+                     itr2.hasNext(); )
+                {
+                    final IProjectFacet f 
+                        = (IProjectFacet) itr2.next();
+                    
+                    if( findTableRowData( f ).isVisible() )
+                    {
+                        visible = true;
+                        break;
+                    }
+                }
+
+                if( visible )
+                {
+                    list.add( cat );
+                }
+            }
+
+            for( int i = 0; i < FacetsSelectionPanel.this.data.size(); i++ )
+            {
+                final TableRowData trd
+                    = (TableRowData) FacetsSelectionPanel.this.data.get( i );
+
+                if( trd.getProjectFacet().getCategory() == null && trd.isVisible() )
+                {
+                    list.add( trd );
+                }
+            }
+
+            return list.toArray();
+        }
+
+        public Object[] getChildren( final Object parent )
+        {
+            if( parent instanceof ICategory )
+            {
+                final ICategory category = (ICategory) parent;
+                
+                final ArrayList trds = new ArrayList();
+
+                for( Iterator itr = category.getProjectFacets().iterator();
+                     itr.hasNext(); )
+                {
+                    final TableRowData trd 
+                        = findTableRowData( (IProjectFacet) itr.next() );
+
+                    if( trd.isVisible() )
+                    {
+                        trds.add( trd );
+                    }
+                }
+
+                return trds.toArray();
+            }
+            else
+            {
+                return new Object[ 0 ];
+            }
+        }
+
+        public Object getParent( final Object element )
+        {
+            if( element instanceof TableRowData )
+            {
+                final IProjectFacet f 
+                    = ( (TableRowData) element ).getProjectFacet();
+
+                return f.getCategory();
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        public boolean hasChildren( final Object element )
+        {
+            return ( element instanceof ICategory ) &&
+                   ! ( (ICategory) element ).getProjectFacets().isEmpty();
+        }
+
+        public void dispose() { }
+
+        public void inputChanged( final Viewer viewer,
+                                  final Object oldObject,
+                                  final Object newObject ) {}
+    }
+
+    private final class LabelProvider
+
+        implements ITableLabelProvider, IFontProvider
+
+    {
+        private ImageRegistry imageRegistry = new ImageRegistry();
+        
+        public String getColumnText( final Object element,
+                                     final int column )
+        {
+            if( element instanceof ICategory )
+            {
+                if( column == 0 )
+                {
+                    return ( (ICategory) element ).getLabel();
+                }
+                else
+                {
+                    return "";
+                }
+            }
+            else
+            {
+                final TableRowData trd = (TableRowData) element;
+
+                switch( column )
+                {
+                    case 0:
+                    {
+                        return trd.getProjectFacet().getLabel();
+                    }
+                    case 1:
+                    {
+                        final String vstr
+                            = trd.getCurrentVersion().getVersionString();
+                        
+                        return trd.getVersions().length == 1 
+                               ? vstr : vstr + " ...";
+                    }
+                    default:
+                    {
+                        throw new IllegalStateException();
+                    }
+                }
+            }
+        }
+
+        public Image getColumnImage( final Object element,
+                                     final int column )
+        {
+            if( column != 0 )
+            {
+                return null;
+            }
+
+            String plugin = null;
+            String iconPath = null;
+
+            if( element instanceof TableRowData )
+            {
+                final IProjectFacet f
+                    = ( (TableRowData) element ).getProjectFacet();
+
+                iconPath = f.getIconPath();
+
+                if( iconPath != null )
+                {
+                    plugin = f.getPluginId();
+                }
+            }
+            else
+            {
+                final ICategory category = (ICategory) element;
+
+                iconPath = category.getIconPath();
+
+                if( iconPath != null )
+                {
+                    plugin = category.getPlugin();
+                }
+            }
+
+            if( iconPath == null )
+            {
+                plugin = FacetUiPlugin.PLUGIN_ID;
+                iconPath = "images/unknown.gif";
+            }
+
+            final String key = plugin + ":" + iconPath;
+            Image image = this.imageRegistry.get( key );
+
+            if( image == null )
+            {
+                final Bundle bundle = Platform.getBundle( plugin );
+                final URL url = bundle.getEntry( iconPath );
+
+                this.imageRegistry.put( key, ImageDescriptor.createFromURL( url ) );
+                image = this.imageRegistry.get( key );
+            }
+
+            return image;
+        }
+
+        public Font getFont( final Object element )
+        {
+            if( element instanceof TableRowData &&
+                ( (TableRowData) element ).isFixed() )
+            {
+                return FIXED_FONT;
+            }
+            
+            return null;
+        }
+
+        public void dispose()
+        {
+            this.imageRegistry.dispose();
+        }
+
+        public boolean isLabelProperty( final Object obj,
+                                        final String s )
+        {
+            return false;
+        }
+        
+        public void addListener( final ILabelProviderListener listener ) {}
+        public void removeListener( ILabelProviderListener listener ) {}
+    }
+
+    private final class CellModifier
+
+        implements ICellModifier
+
+    {
+        public Object getValue( final Object element,
+                                final String property )
+        {
+            final TableRowData trd = (TableRowData) element;
+
+            if( property.equals( "version" ) )
+            {
+                final IProjectFacetVersion[] versions = trd.getVersions();
+
+                for( int i = 0; i < versions.length; i++ )
+                {
+                    if( versions[ i ] == trd.getCurrentVersion() )
+                    {
+                        return new Integer( i );
+                    }
+                }
+
+                return new IllegalStateException();
+            }
+            else
+            {
+                throw new IllegalStateException();
+            }
+        }
+
+        public boolean canModify( final Object element,
+                                  final String property )
+        {
+            return property.equals( "version" ) &&
+                   element instanceof TableRowData &&
+                   ( (TableRowData) element ).getVersions().length > 1;
+        }
+
+        public void modify( final Object element,
+                            final String property,
+                            final Object value )
+        {
+            final TreeItem item = (TreeItem) element;
+            final TableRowData trd = (TableRowData) item.getData();
+
+            if( property.equals( "version" ) )
+            {
+                final int index = ( (Integer) value ).intValue();
+
+                if( index != -1 )
+                {
+                    final IProjectFacetVersion fv 
+                        = trd.getVersions()[ index ];
+                    
+                    if( trd.getCurrentVersion() != fv )
+                    {
+                        trd.setCurrentVersion( trd.getVersions()[ index ] );
+                        FacetsSelectionPanel.this.tree.refresh();
+                        
+                        if( trd.isSelected() )
+                        {
+                            FacetsSelectionPanel.this.presetsCombo.select( 0 );
+                            refreshPresetsButtons();
+                        }
+    
+                        updateValidationDisplay();
+                        FacetsSelectionPanel.this.runtimesPanel.refresh();
+                    }
+                }
+            }
+            else
+            {
+                throw new IllegalStateException();
+            }
+        }
+    }
+
+    private static final class Sorter
+
+        extends ViewerSorter
+
+    {
+        public int compare( final Viewer viewer,
+                            final Object a,
+                            final Object b )
+        {
+            final Boolean fixed1 = Boolean.valueOf( getFixed( a ) );
+            final Boolean fixed2 = Boolean.valueOf( getFixed( b ) );
+            
+            final String label1 = getLabel( a );
+            final String label2 = getLabel( b );
+            
+            int res 
+                = fixed1.equals( fixed2 ) 
+                  ? 0 
+                  : ( fixed1.booleanValue() ? -1 : 1 );
+            
+            if( res == 0 )
+            {
+                res = label1.compareToIgnoreCase( label2 );
+            }
+            
+            return res;
+        }
+
+        private static String getLabel( final Object obj )
+        {
+            if( obj instanceof TableRowData )
+            {
+                return ( (TableRowData) obj ).getProjectFacet().getLabel();
+            }
+            else
+            {
+                return ( (ICategory) obj ).getLabel();
+            }
+        }
+        
+        private static boolean getFixed( final Object obj )
+        {
+            if( obj instanceof TableRowData )
+            {
+                return ( (TableRowData) obj ).isFixed();
+            }
+            else
+            {
+                return false;
+            }
+        }
+    }
+
+    private final class ProblemsContentProvider
+
+        implements IStructuredContentProvider
+
+    {
+        public Object[] getElements( final Object element )
+        {
+            return problems.getChildren();
+        }
+
+        public void dispose() { }
+
+        public void inputChanged( final Viewer viewer,
+                                  final Object oldObject,
+                                  final Object newObject ) {}
+    }
+
+    private final class ProblemsLabelProvider
+
+        implements ITableLabelProvider
+
+    {
+        private Image errorImage;
+
+        public ProblemsLabelProvider()
+        {
+            final Bundle bundle = Platform.getBundle( FacetUiPlugin.PLUGIN_ID );
+            final URL url = bundle.getEntry( "images/error.gif" );
+
+            this.errorImage
+                = ImageDescriptor.createFromURL( url ).createImage();
+        }
+
+        public String getColumnText( final Object element,
+                                     final int column )
+        {
+            return ( (IStatus) element ).getMessage();
+        }
+
+        public Image getColumnImage( final Object element,
+                                     final int column )
+        {
+            return this.errorImage;
+        }
+
+        public boolean isLabelProperty( final Object obj,
+                                        final String s )
+        {
+            return false;
+        }
+
+        public void dispose()
+        {
+            this.errorImage.dispose();
+        }
+
+        public void addListener( final ILabelProviderListener listener ) {}
+        public void removeListener( ILabelProviderListener listener ) {}
+    }
+    
+    private static final GridData gdfill()
+    {
+        return new GridData( SWT.FILL, SWT.FILL, true, true );
+    }
+
+    private static final GridData gdhfill()
+    {
+        return new GridData( GridData.FILL_HORIZONTAL );
+    }
+    
+    private static final GridData whint( final GridData gd,
+                                         final int width )
+    {
+        gd.widthHint = width;
+        return gd;
+    }
+    
+    private static final GridData hhint( final GridData gd,
+                                         final int height )
+    {
+        gd.heightHint = height;
+        return gd;
+    }
+    
+    private static final GridData hspan( final GridData gd,
+                                         final int span )
+    {
+        gd.horizontalSpan = span;
+        return gd;
+    }
+
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/RuntimeBridge.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/RuntimeBridge.java
new file mode 100644
index 0000000..4e73795
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/RuntimeBridge.java
@@ -0,0 +1,133 @@
+package org.eclipse.wst.common.project.facet.ui.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jst.server.core.ClasspathRuntimeTargetHandler;
+import org.eclipse.wst.common.project.facet.core.runtime.IRuntimeComponentVersion;
+import org.eclipse.wst.common.project.facet.core.runtime.RuntimeManager;
+import org.eclipse.wst.server.core.IRuntime;
+import org.eclipse.wst.server.core.IRuntimeTargetHandler;
+import org.eclipse.wst.server.core.ServerCore;
+import org.eclipse.jdt.launching.IVMInstall2;
+import org.eclipse.jdt.launching.IVMInstallType;
+import org.eclipse.jdt.launching.JavaRuntime;
+
+public final class RuntimeBridge
+{
+    private static Map mappings = new HashMap();
+    
+    static
+    {
+        mappings.put( "org.eclipse.jst.server.tomcat.runtime.55",
+                      RuntimeManager.get().getRuntimeComponentType( "tomcat" ).getVersion( "5.5" ) );
+        
+        mappings.put( "org.eclipse.jst.server.tomcat.runtime.41",
+                      RuntimeManager.get().getRuntimeComponentType( "tomcat" ).getVersion( "4.1" ) );
+    }
+    
+    public static void port()
+    {
+        final IRuntime[] runtimes = ServerCore.getRuntimes();
+        
+        for( int i = 0; i < runtimes.length; i++ )
+        {
+            final IRuntime runtime = runtimes[ i ];
+            final String name = runtime.getName();
+            
+            if( ! RuntimeManager.get().isRuntimeDefined( name ) )
+            {
+                final String type = runtime.getRuntimeType().getId();
+                
+                final IRuntimeComponentVersion mapped
+                    = (IRuntimeComponentVersion) mappings.get( type );
+                
+                if( mapped != null )
+                {
+                    final List components = new ArrayList();
+                    
+                    Map properties;
+                    
+                    properties = new HashMap();
+                    properties.put( "location", runtime.getLocation().toPortableString() );
+                    properties.put( "name", name );
+                    
+                    components.add( RuntimeManager.get().createRuntimeComponent( mapped, properties ) );
+                    
+                    final ClasspathRuntimeTargetHandler cphandler
+                        = getClasspathHandler( runtime );
+                    
+                    final IPath jrecontainer 
+                        = findJreContainer( cphandler.getDelegateClasspathEntries( runtime, null ) );
+                    
+                    final IVMInstallType vminstalltype
+                        = JavaRuntime.getVMInstallType( jrecontainer.segment( 1 ) );
+                    
+                    final IVMInstall2 vminstall
+                        = (IVMInstall2) vminstalltype.findVMInstallByName( jrecontainer.segment( 2 ) );
+                    
+                    final String jvmver = vminstall.getJavaVersion();
+                    final IRuntimeComponentVersion rcv;
+                    
+                    if( jvmver.startsWith( "1.4" ) )
+                    {
+                        rcv = RuntimeManager.get().getRuntimeComponentType( "standard.jre" ).getVersion( "1.4" );
+                    }
+                    else if( jvmver.startsWith( "1.5" ) )
+                    {
+                        rcv = RuntimeManager.get().getRuntimeComponentType( "standard.jre" ).getVersion( "5.0" );
+                    }
+                    else
+                    {
+                        continue;
+                    }
+
+                    properties = new HashMap();
+                    properties.put( "name", jrecontainer.segment( 2 ) );
+                    
+                    components.add( RuntimeManager.get().createRuntimeComponent( rcv, properties ) );
+                    
+                    RuntimeManager.get().defineRuntime( name, components, null );
+                }
+            }
+        }
+    }
+    
+    private static ClasspathRuntimeTargetHandler getClasspathHandler( final IRuntime r )
+    {
+        final IRuntimeTargetHandler[] handlers 
+            = ServerCore.getRuntimeTargetHandlers();
+        
+        for( int j = 0; j < handlers.length; j++ )
+        {
+            final IRuntimeTargetHandler handler = handlers[ j ];
+            
+            if( handler.supportsRuntimeType( r.getRuntimeType() ) ) 
+            {
+                return (ClasspathRuntimeTargetHandler) handler.getAdapter( ClasspathRuntimeTargetHandler.class );
+            }
+        }
+        
+        throw new IllegalStateException();
+    }
+    
+    private static IPath findJreContainer( final IClasspathEntry[] cpentries )
+    {
+        for( int i = 0; i < cpentries.length; i++ )
+        {
+            final IPath path = cpentries[ i ].getPath();
+            
+            if( path.segment( 0 ).equals( JavaRuntime.JRE_CONTAINER ) )
+            {
+                return path;
+            }
+        }
+        
+        throw new IllegalStateException();
+    }
+    
+}
diff --git a/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/RuntimesPanel.java b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/RuntimesPanel.java
new file mode 100644
index 0000000..a8b70d4
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.project.facet.ui/src/org/eclipse/wst/common/project/facet/ui/internal/RuntimesPanel.java
@@ -0,0 +1,540 @@
+/******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Konstantin Komissarchik - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.wst.common.project.facet.ui.internal;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
+import org.eclipse.wst.common.project.facet.core.runtime.IRuntime;
+import org.eclipse.wst.common.project.facet.core.runtime.IRuntimeComponent;
+import org.eclipse.wst.common.project.facet.core.runtime.IRuntimeComponentType;
+import org.eclipse.wst.common.project.facet.core.runtime.RuntimeManager;
+import org.eclipse.wst.common.project.facet.ui.IRuntimeComponentLabelProvider;
+import org.osgi.framework.Bundle;
+
+/**
+ * @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
+ */
+
+public final class RuntimesPanel
+
+    extends Composite
+
+{
+    private final Label runtimesLabel;
+    private final TreeViewer runtimes;
+    private final Button filterButton;
+    private final Button bindButton;
+    private final Button newButton;
+    private final FacetsSelectionPanel facetsSelectionPanel;
+    private final Map facetFilters;
+    private final Set filters;
+    private IRuntime boundRuntime;
+    
+    public interface IFilter
+    {
+        boolean check( IRuntime runtime );
+    }
+
+    public RuntimesPanel( final Composite parent,
+                          final int style,
+                          final FacetsSelectionPanel facetsSelectionPanel,
+                          final IRuntime runtime )
+    {
+        super( parent, style );
+
+        this.facetsSelectionPanel = facetsSelectionPanel;
+        this.facetFilters = new HashMap();
+        this.filters = new HashSet();
+
+        if( runtime != null )
+        {
+            this.boundRuntime = runtime;
+            addProjectFacetsFilter( runtime );
+        }
+        
+        // Layout the panel.
+        
+        final GridLayout layout = new GridLayout( 1, false );
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+
+        setLayout( layout );
+
+        this.runtimesLabel = new Label( this, SWT.NONE );
+        this.runtimesLabel.setText( "Runtimes:" );
+        this.runtimesLabel.setLayoutData( gdhfill() );
+        
+        this.runtimes = new TreeViewer( this, SWT.BORDER );
+        this.runtimes.getTree().setLayoutData( gdfill() );
+        this.runtimes.setContentProvider( new ContentProvider() );
+        this.runtimes.setLabelProvider( new LabelProvider() );
+        this.runtimes.setSorter( new Sorter() );
+        this.runtimes.setInput( new Object() );
+        
+        this.runtimes.addSelectionChangedListener
+        (
+            new ISelectionChangedListener()
+            {
+                public void selectionChanged( final SelectionChangedEvent e )
+                {
+                    updateButtons();
+                }
+            }
+        );
+        
+        final Composite buttons = new Composite( this, SWT.NONE );
+        buttons.setLayoutData( halign( new GridData(), SWT.RIGHT ) );
+        
+        final GridLayout buttonsLayout = new GridLayout( 3, false );
+        buttonsLayout.marginHeight = 0;
+        buttonsLayout.marginWidth = 0;
+        
+        buttons.setLayout( buttonsLayout );
+        
+        this.filterButton = new Button( buttons, SWT.PUSH );
+        this.filterButton.setText( "Add Filter" );
+        this.filterButton.setLayoutData( whint( new GridData(), 80 ) );
+        
+        this.filterButton.addSelectionListener
+        (
+            new SelectionAdapter()
+            {
+                public void widgetSelected( final SelectionEvent e )
+                {
+                    handleAddRemoveFilter();
+                }
+            }
+        );
+
+        this.bindButton = new Button( buttons, SWT.PUSH );
+        this.bindButton.setText( "Bind" );
+        this.bindButton.setLayoutData( whint( new GridData(), 60 ) );
+
+        this.bindButton.addSelectionListener
+        (
+            new SelectionAdapter()
+            {
+                public void widgetSelected( final SelectionEvent e )
+                {
+                    handleBindUnbindRuntime();
+                }
+            }
+        );
+        
+        this.newButton = new Button( buttons, SWT.PUSH );
+        this.newButton.setText( "New" );
+        this.newButton.setLayoutData( whint( new GridData(), 60 ) );
+        
+        updateButtons();
+    }
+    
+    public IRuntime getSelectedRuntime()
+    {
+        return this.boundRuntime;
+    }
+    
+    public void addFilter( final IFilter filter )
+    {
+        this.filters.add( filter );
+        this.runtimes.refresh();
+    }
+    
+    public void removeFilter( final IFilter filter )
+    {
+        this.filters.remove( filter );
+        this.runtimes.refresh();
+    }
+    
+    public void refresh()
+    {
+        this.runtimes.refresh();
+    }
+    
+    private void addProjectFacetsFilter( final IRuntime r )
+    {
+        if( ! this.facetFilters.containsKey( r ) )
+        {
+            final FacetsSelectionPanel.IFilter filter
+                = new SupportedFacetsFilter( r );
+            
+            this.facetFilters.put( r, filter );
+            this.facetsSelectionPanel.addFilter( filter );
+        }
+    }
+    
+    private void removeProjectFacetsFilter( final IRuntime r )
+    {
+        final FacetsSelectionPanel.IFilter filter
+            = (FacetsSelectionPanel.IFilter) this.facetFilters.remove( r );
+        
+        this.facetsSelectionPanel.removeFilter( filter );
+    }
+    
+    private void handleAddRemoveFilter()
+    {
+        final IStructuredSelection ssel 
+            = (IStructuredSelection) this.runtimes.getSelection();
+        
+        final IRuntime runtime = (IRuntime) ssel.getFirstElement();
+        
+        if( this.facetFilters.containsKey( runtime ) )
+        {
+            removeProjectFacetsFilter( runtime );
+        }
+        else
+        {
+            addProjectFacetsFilter( runtime );
+        }
+        
+        this.runtimes.update( runtime, null );
+        updateButtons();
+    }
+    
+    private void handleBindUnbindRuntime()
+    {
+        final IStructuredSelection ssel 
+            = (IStructuredSelection) this.runtimes.getSelection();
+        
+        final IRuntime runtime = (IRuntime) ssel.getFirstElement();
+        
+        final IRuntime old = this.boundRuntime;
+        final boolean isBindAction = ( old != runtime );
+        
+        if( old != null )
+        {
+            removeProjectFacetsFilter( old );
+            this.boundRuntime = null;
+            this.runtimes.update( old, null );
+        }
+        
+        if( isBindAction )
+        {
+            addProjectFacetsFilter( runtime );
+            this.boundRuntime = runtime;
+            this.runtimes.update( runtime, null );
+        }
+        
+        updateButtons();
+    }
+    
+    private void updateButtons()
+    {
+        final IStructuredSelection ssel
+            = (IStructuredSelection) this.runtimes.getSelection();
+        
+        final Object sel = ssel.getFirstElement();
+        
+        if( sel == null || sel instanceof IRuntimeComponent )
+        {
+            this.filterButton.setText( "Add Filter" );
+            this.filterButton.setEnabled( false );
+            this.bindButton.setText( "Bind" );
+            this.bindButton.setEnabled( false );
+        }
+        else
+        {
+            final IRuntime runtime = (IRuntime) sel;
+            
+            if( this.facetFilters.containsKey( runtime ) )
+            {
+                this.filterButton.setText( "Remove Filter" );
+            }
+            else
+            {
+                this.filterButton.setText( "Add Filter" );
+            }
+            
+            if( this.boundRuntime == runtime )
+            {
+                this.bindButton.setText( "Unbind" );
+                this.filterButton.setEnabled( false );
+            }
+            else
+            {
+                this.bindButton.setText( "Bind" );
+                this.filterButton.setEnabled( true );
+            }
+            
+            this.bindButton.setEnabled( true );
+        }
+    }
+    
+    private final class ContentProvider
+
+        implements ITreeContentProvider
+    
+    {
+        public Object[] getElements( final Object element )
+        {
+            final Set res = new HashSet();
+            
+            for( Iterator itr1 = RuntimeManager.get().getRuntimes().iterator();
+                 itr1.hasNext(); )
+            {
+                final IRuntime r = (IRuntime) itr1.next();
+                boolean ok = true;
+                
+                for( Iterator itr2 = RuntimesPanel.this.filters.iterator();
+                     itr2.hasNext(); )
+                {
+                    if( ! ( (IFilter) itr2.next() ).check( r ) )
+                    {
+                        ok = false;
+                        break;
+                    }
+                }
+                
+                if( ok )
+                {
+                    res.add( r );
+                }
+            }
+            
+            return res.toArray();
+        }
+    
+        public Object[] getChildren( final Object parent )
+        {
+            if( parent instanceof IRuntime )
+            {
+                return ( (IRuntime) parent ).getRuntimeComponents().toArray();
+            }
+            else
+            {
+                return new Object[ 0 ];
+            }
+        }
+    
+        public Object getParent( final Object element )
+        {
+            return null;
+        }
+    
+        public boolean hasChildren( final Object element )
+        {
+            return ( element instanceof IRuntime );
+        }
+    
+        public void dispose() { }
+    
+        public void inputChanged( final Viewer viewer,
+                                  final Object oldObject,
+                                  final Object newObject ) {}
+    }
+    
+    private final class LabelProvider
+
+        implements ILabelProvider
+    
+    {
+        private ImageRegistry imageRegistry = new ImageRegistry();
+        
+        public String getText( final Object element )
+        {
+            if( element instanceof IRuntime )
+            {
+                final StringBuffer label = new StringBuffer();
+                label.append( ( (IRuntime) element ).getName() );
+                
+                if( RuntimesPanel.this.facetFilters.containsKey( element ) )
+                {
+                    label.append( " <f>" );
+                }
+                
+                if( RuntimesPanel.this.boundRuntime == element )
+                {
+                    label.append( " <b>" );
+                }
+                
+                return label.toString();
+            }
+            else
+            {
+                final IRuntimeComponent comp = (IRuntimeComponent) element;
+                
+                final IRuntimeComponentLabelProvider provider
+                    = (IRuntimeComponentLabelProvider) comp.getAdapter( IRuntimeComponentLabelProvider.class );
+                
+                if( provider == null )
+                {
+                    final StringBuffer label = new StringBuffer();
+                    label.append( comp.getRuntimeComponentType().getId() );
+                    label.append( ' ' );
+                    label.append( comp.getRuntimeComponentVersion().getVersionString() );
+                    
+                    return label.toString();
+                }
+                else
+                {
+                    return provider.getLabel();
+                }
+            }
+        }
+
+        public Image getImage( final Object element )
+        {
+            String plugin = null;
+            String iconPath = null;
+
+            if( element instanceof IRuntime )
+            {
+                final IRuntime r = (IRuntime) element;
+                
+                final IRuntimeComponent rc 
+                    = (IRuntimeComponent) r.getRuntimeComponents().get( 0 );
+                
+                final IRuntimeComponentType rct = rc.getRuntimeComponentType();
+                
+                plugin = rct.getPluginId();
+                iconPath = rct.getIconPath();
+            }
+            else
+            {
+                final IRuntimeComponent rc = (IRuntimeComponent) element;
+                final IRuntimeComponentType rct = rc.getRuntimeComponentType();
+                
+                plugin = rct.getPluginId();
+                iconPath = rct.getIconPath();
+            }
+
+            if( iconPath == null )
+            {
+                plugin = FacetUiPlugin.PLUGIN_ID;
+                iconPath = "images/unknown.gif";
+            }
+
+            final String key = plugin + ":" + iconPath;
+            Image image = this.imageRegistry.get( key );
+
+            if( image == null )
+            {
+                final Bundle bundle = Platform.getBundle( plugin );
+                final URL url = bundle.getEntry( iconPath );
+
+                this.imageRegistry.put( key, ImageDescriptor.createFromURL( url ) );
+                image = this.imageRegistry.get( key );
+            }
+
+            return image;
+        }
+        
+        public void dispose()
+        {
+            this.imageRegistry.dispose();
+        }
+
+        public boolean isLabelProperty( final Object element, 
+                                        final String property )
+        {
+            return false;
+        }
+
+        public void addListener( final ILabelProviderListener listener ) {}
+        public void removeListener( final ILabelProviderListener listener ) {}
+    }
+
+    private static final class Sorter
+
+        extends ViewerSorter
+    
+    {
+        public int compare( final Viewer viewer,
+                            final Object a,
+                            final Object b )
+        {
+            if( a instanceof IRuntime )
+            {
+                final IRuntime r1 = (IRuntime) a;
+                final IRuntime r2 = (IRuntime) b;
+                
+                return r1.getName().compareToIgnoreCase( r2.getName() );
+            }
+            else
+            {
+                // Don't sort the runtime components. Their order is 
+                // significant.
+                
+                return 0;
+            }
+        }
+    }
+    
+    private static final class SupportedFacetsFilter
+    
+        implements FacetsSelectionPanel.IFilter
+        
+    {
+        private final IRuntime runtime;
+        
+        public SupportedFacetsFilter( final IRuntime runtime )
+        {
+            this.runtime = runtime;
+        }
+        
+        public boolean check( final IProjectFacetVersion fv )
+        {
+            return this.runtime.supports( fv );
+        }
+    }
+    
+    private static final GridData gdfill()
+    {
+        return new GridData( SWT.FILL, SWT.FILL, true, true );
+    }
+    
+    private static final GridData gdhfill()
+    {
+        return new GridData( GridData.FILL_HORIZONTAL );
+    }
+    
+    private static final GridData whint( final GridData gd,
+                                         final int width )
+    {
+        gd.widthHint = width;
+        return gd;
+    }
+
+    private static final GridData halign( final GridData gd,
+                                          final int alignment )
+    {
+        gd.horizontalAlignment = alignment;
+        return gd;
+    }
+    
+}