blob: fa0603cd91ed025d2bbebff342f4e66c24d26264 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2005-2007 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
******************************************************************************/
package org.eclipse.wst.common.project.facet.core.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.common.project.facet.core.FacetedProjectFramework;
import org.eclipse.wst.common.project.facet.core.IActionConfig;
import org.eclipse.wst.common.project.facet.core.IActionDefinition;
import org.eclipse.wst.common.project.facet.core.IDynamicPreset;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.IFacetedProjectWorkingCopy;
import org.eclipse.wst.common.project.facet.core.IListener;
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.events.IFacetedProjectEvent;
import org.eclipse.wst.common.project.facet.core.events.IFacetedProjectFrameworkEvent;
import org.eclipse.wst.common.project.facet.core.events.IFacetedProjectFrameworkListener;
import org.eclipse.wst.common.project.facet.core.events.IFacetedProjectListener;
import org.eclipse.wst.common.project.facet.core.events.IProjectFacetsChangedEvent;
import org.eclipse.wst.common.project.facet.core.events.internal.FacetedProjectEvent;
import org.eclipse.wst.common.project.facet.core.events.internal.ProjectFacetsChangedEvent;
import org.eclipse.wst.common.project.facet.core.internal.util.IndexedSet;
import org.eclipse.wst.common.project.facet.core.runtime.IRuntime;
import org.eclipse.wst.common.project.facet.core.runtime.RuntimeManager;
/**
* @since 3.0
* @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
*/
public final class FacetedProjectWorkingCopy
implements IFacetedProjectWorkingCopy
{
private static final SortedSet<IProjectFacetVersion> EMPTY_SORTED_FV_SET
= Collections.unmodifiableSortedSet( new TreeSet<IProjectFacetVersion>() );
/**
* The name of the project in the scenario where the working copy is for a
* project that doesn't exist yet, <code>null</code> otherwise.
*/
private String projectName;
/**
* The location of the project in the scenario where the working copy is for a
* project that doesn't exist yet, <code>null</code> otherwise.
*/
private IPath projectLocation;
private final IFacetedProject project;
private Set<IProjectFacet> fixedFacets;
private IndexedSet<IProjectFacet,IProjectFacetVersion> facets;
private Map<IProjectFacet,SortedSet<IProjectFacetVersion>> availableFacets;
private IndexedSet<String,IPreset> availablePresets;
private IPreset selectedPreset;
private final Set<IRuntime> targetableRuntimes;
private final Set<IRuntime> targetedRuntimes;
private IRuntime primaryRuntime;
private Set<Action> actions;
private List<IStatus> problems;
private final List<Runnable> disposeTasks;
/**
* Maps event types to the list of listeners registered for those events. The map is
* populated with empty listeners lists for all event types at initialization and is
* not modified after that. The contained lists use copy-on-write behavior to guarantee
* for safe iteration when notifying listeners.
*/
private final Map<IFacetedProjectEvent.Type,List<IFacetedProjectListener>> listeners;
public FacetedProjectWorkingCopy( final IFacetedProject project )
{
this.projectName = null;
this.projectLocation = null;
this.project = project;
this.actions = Collections.emptySet();
this.problems = Collections.emptyList();
this.fixedFacets = Collections.emptySet();
this.facets = new IndexedSet<IProjectFacet,IProjectFacetVersion>();
this.availableFacets = Collections.emptyMap();
this.availablePresets = new IndexedSet<String,IPreset>();
this.selectedPreset = null;
this.targetableRuntimes = new CopyOnWriteArraySet<IRuntime>();
this.targetedRuntimes = new CopyOnWriteArraySet<IRuntime>();
this.primaryRuntime = null;
this.disposeTasks = new ArrayList<Runnable>();
this.listeners = new HashMap<IFacetedProjectEvent.Type,List<IFacetedProjectListener>>();
for( IFacetedProjectEvent.Type eventType : IFacetedProjectEvent.Type.values() )
{
this.listeners.put( eventType, new CopyOnWriteArrayList<IFacetedProjectListener>() );
}
refreshAvailableFacets();
final IFacetedProjectListener avFacetsListener = new IFacetedProjectListener()
{
public void handleEvent( final IFacetedProjectEvent event )
{
refreshAvailableFacets();
}
};
addListener( avFacetsListener,
IFacetedProjectEvent.Type.FIXED_FACETS_CHANGED,
IFacetedProjectEvent.Type.TARGETED_RUNTIMES_CHANGED );
if( this.project != null )
{
setFixedProjectFacets( this.project.getFixedProjectFacets() );
}
refreshAvailablePresets();
final IFacetedProjectListener avPresetsListener = new IFacetedProjectListener()
{
public void handleEvent( final IFacetedProjectEvent event )
{
refreshAvailablePresets();
}
};
addListener( avPresetsListener,
IFacetedProjectEvent.Type.FIXED_FACETS_CHANGED,
IFacetedProjectEvent.Type.AVAILABLE_FACETS_CHANGED,
IFacetedProjectEvent.Type.PRIMARY_RUNTIME_CHANGED );
if( this.project != null )
{
setProjectFacets( this.project.getProjectFacets() );
}
refreshTargetableRuntimes();
if( this.project != null )
{
setTargetedRuntimes( this.project.getTargetedRuntimes() );
setPrimaryRuntime( this.project.getPrimaryRuntime() );
}
// Listen for changes to registered runtimes.
final IListener runtimeManagerListener = new IListener()
{
public void handle()
{
final IFacetedProjectEvent event
= new FacetedProjectEvent( FacetedProjectWorkingCopy.this,
IFacetedProjectEvent.Type.AVAILABLE_RUNTIMES_CHANGED );
notifyListeners( event );
refreshTargetableRuntimes();
}
};
RuntimeManager.addRuntimeListener( runtimeManagerListener );
addDisposeTask
(
new Runnable()
{
public void run()
{
RuntimeManager.removeRuntimeListener( runtimeManagerListener );
}
}
);
// Listen for changes to registered presets.
final IFacetedProjectFrameworkListener presetsListener = new IFacetedProjectFrameworkListener()
{
public void handleEvent( final IFacetedProjectFrameworkEvent event )
{
refreshAvailablePresets();
}
};
FacetedProjectFramework.addListener( presetsListener,
IFacetedProjectFrameworkEvent.Type.PRESET_ADDED,
IFacetedProjectFrameworkEvent.Type.PRESET_REMOVED );
addDisposeTask
(
new Runnable()
{
public void run()
{
FacetedProjectFramework.removeListener( presetsListener );
}
}
);
// Listen for changes to the project.
final IFacetedProjectListener projectFacetsChangedListener = new IFacetedProjectListener()
{
public void handleEvent( final IFacetedProjectEvent event )
{
refreshTargetableRuntimes();
refreshProjectFacetActions();
performValidation();
}
};
addListener( projectFacetsChangedListener,
IFacetedProjectEvent.Type.PROJECT_FACETS_CHANGED );
final IFacetedProjectListener targetedRuntimesChangedListener = new IFacetedProjectListener()
{
public void handleEvent( final IFacetedProjectEvent event )
{
performValidation();
}
};
addListener( targetedRuntimesChangedListener,
IFacetedProjectEvent.Type.TARGETED_RUNTIMES_CHANGED );
}
public synchronized String getProjectName()
{
if( this.project != null )
{
return this.project.getProject().getName();
}
else
{
return this.projectName;
}
}
public synchronized void setProjectName( final String name )
{
if( this.project == null )
{
if( ! equals( this.projectName, name ) )
{
this.projectName = name;
for( Action action : getProjectFacetActions() )
{
final Object config = action.getConfig();
if( config != null )
{
IActionConfig c = null;
if( config instanceof IActionConfig )
{
c = (IActionConfig) config;
}
else
{
final IAdapterManager m = Platform.getAdapterManager();
final String t = IActionConfig.class.getName();
c = (IActionConfig) m.loadAdapter( config, t );
}
if( c != null )
{
c.setProjectName( this.projectName );
}
}
}
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.PROJECT_NAME_CHANGED ) );
}
}
else
{
throw new IllegalArgumentException(); // TODO: needs message
}
}
public synchronized IPath getProjectLocation()
{
if( this.project != null )
{
return this.project.getProject().getLocation();
}
else
{
return this.projectLocation;
}
}
public synchronized void setProjectLocation( final IPath location )
{
if( this.project == null )
{
this.projectLocation = location;
}
else
{
throw new IllegalArgumentException(); // TODO: needs message
}
}
public IProject getProject()
{
if( this.project == null )
{
return null;
}
else
{
return this.project.getProject();
}
}
public IFacetedProject getFacetedProject()
{
return this.project;
}
public synchronized Set<IProjectFacet> getFixedProjectFacets()
{
return this.fixedFacets;
}
public synchronized boolean isFixedProjectFacet( final IProjectFacet facet )
{
return this.fixedFacets.contains( facet );
}
public synchronized void setFixedProjectFacets( final Set<IProjectFacet> fixed )
{
if( this.fixedFacets.equals( fixed ) )
{
return;
}
for( IProjectFacet f : fixed )
{
final IProjectFacetVersion currentVersion = getProjectFacetVersion( f );
if( currentVersion == null )
{
final IProjectFacetVersion highestAvailableVersion = getHighestAvailableVersion( f );
this.facets.add( f, highestAvailableVersion );
}
}
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.PROJECT_FACETS_CHANGED ) );
this.fixedFacets = Collections.unmodifiableSet( new HashSet<IProjectFacet>( fixed ) );
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.FIXED_FACETS_CHANGED ) );
}
public synchronized Map<IProjectFacet,SortedSet<IProjectFacetVersion>> getAvailableFacets()
{
return this.availableFacets;
}
public synchronized boolean isFacetAvailable( final IProjectFacet f )
{
return this.availableFacets.containsKey( f );
}
public synchronized boolean isFacetAvailable( final IProjectFacetVersion fv )
{
final Set<IProjectFacetVersion> versions = this.availableFacets.get( fv.getProjectFacet() );
return ( versions != null && versions.contains( fv ) );
}
public synchronized SortedSet<IProjectFacetVersion> getAvailableVersions( final IProjectFacet f )
{
SortedSet<IProjectFacetVersion> availableVersions = this.availableFacets.get( f );
if( availableVersions == null )
{
availableVersions = EMPTY_SORTED_FV_SET;
}
return availableVersions;
}
public synchronized IProjectFacetVersion getHighestAvailableVersion( final IProjectFacet f )
{
IProjectFacetVersion version = null;
for( IProjectFacetVersion fv : this.availableFacets.get( f ) )
{
if( version == null )
{
version = fv;
}
else
{
if( fv.compareTo( version ) > 0 )
{
version = fv;
}
}
}
return version;
}
private synchronized void refreshAvailableFacets()
{
final Map<IProjectFacet,SortedSet<IProjectFacetVersion>> newAvailableFacets
= new HashMap<IProjectFacet,SortedSet<IProjectFacetVersion>>();
final Set<IRuntime> targetedRuntimes = getTargetedRuntimes();
for( IProjectFacet f : ProjectFacetsManager.getProjectFacets() )
{
SortedSet<IProjectFacetVersion> versions = null;
for( IProjectFacetVersion fv : f.getVersions() )
{
boolean available = true;
if( this.project == null || ! this.project.hasProjectFacet( fv ) )
{
for( IRuntime r : targetedRuntimes )
{
if( ! r.supports( fv ) )
{
available = false;
break;
}
}
if( available && ! fv.isValidFor( this.fixedFacets ) )
{
available = false;
}
}
if( available )
{
if( versions == null )
{
versions = new TreeSet<IProjectFacetVersion>();
newAvailableFacets.put( f, versions );
}
versions.add( fv );
}
}
}
// Add any unknown facets that are referenced by the project.
if( this.project != null )
{
for( IProjectFacetVersion fv : this.project.getProjectFacets() )
{
if( fv.getPluginId() == null )
{
final IProjectFacet f = fv.getProjectFacet();
SortedSet<IProjectFacetVersion> versions = newAvailableFacets.get( f );
if( versions == null )
{
versions = new TreeSet<IProjectFacetVersion>();
newAvailableFacets.put( f, versions );
}
versions.add( fv );
}
}
}
// If there is a change to the available facets, apply the change and notify the listeners.
if( ! this.availableFacets.equals( newAvailableFacets ) )
{
for( Map.Entry<IProjectFacet,SortedSet<IProjectFacetVersion>> entry
: newAvailableFacets.entrySet() )
{
entry.setValue( Collections.unmodifiableSortedSet( entry.getValue() ) );
}
this.availableFacets = Collections.unmodifiableMap( newAvailableFacets );
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.AVAILABLE_FACETS_CHANGED ) );
}
}
public synchronized Set<IProjectFacetVersion> getProjectFacets()
{
return this.facets.getUnmodifiable();
}
public synchronized IProjectFacetVersion getProjectFacetVersion( final IProjectFacet f )
{
return this.facets.get( f );
}
public synchronized boolean hasProjectFacet( final IProjectFacet f )
{
return this.facets.containsKey( f );
}
public synchronized boolean hasProjectFacet( final IProjectFacetVersion fv )
{
return this.facets.contains( fv );
}
public synchronized void setProjectFacets( final Set<IProjectFacetVersion> facets )
{
final IndexedSet<IProjectFacet,IProjectFacetVersion> newProjectFacets
= new IndexedSet<IProjectFacet,IProjectFacetVersion>();
for( IProjectFacetVersion fv : facets )
{
newProjectFacets.add( fv.getProjectFacet(), fv );
}
final Set<IProjectFacetVersion> addedFacets = new HashSet<IProjectFacetVersion>();
final Set<IProjectFacetVersion> removedFacets = new HashSet<IProjectFacetVersion>();
final Set<IProjectFacetVersion> changedVersions = new HashSet<IProjectFacetVersion>();
for( IProjectFacetVersion fv : newProjectFacets )
{
final IProjectFacetVersion currentFacetVersion = this.facets.get( fv.getProjectFacet() );
if( currentFacetVersion == null )
{
addedFacets.add( fv );
}
else
{
if( ! fv.equals( currentFacetVersion ) )
{
changedVersions.add( fv );
}
}
}
for( IProjectFacetVersion fv : this.facets )
{
if( ! newProjectFacets.containsKey( fv.getProjectFacet() ) )
{
removedFacets.add( fv );
}
}
if( addedFacets.isEmpty() && removedFacets.isEmpty() && changedVersions.isEmpty() )
{
return;
}
setSelectedPreset( null );
this.facets = newProjectFacets;
final IProjectFacetsChangedEvent event
= new ProjectFacetsChangedEvent( this, addedFacets, removedFacets,
changedVersions );
notifyListeners( event );
}
public synchronized void setDefaultFacetsForRuntime( final IRuntime runtime )
{
final Set<IProjectFacetVersion> defaultFacets;
if( runtime != null )
{
try
{
defaultFacets = runtime.getDefaultFacets( getFixedProjectFacets() );
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
return;
}
}
else
{
defaultFacets = new HashSet<IProjectFacetVersion>();
for( IProjectFacet f : getFixedProjectFacets() )
{
defaultFacets.add( f.getDefaultVersion() );
}
}
setProjectFacets( defaultFacets );
}
public synchronized void addProjectFacet( final IProjectFacetVersion fv )
{
final IProjectFacetVersion existingVersion = this.facets.get( fv.getProjectFacet() );
if( existingVersion == null )
{
final Set<IProjectFacetVersion> newProjectFacets = new HashSet<IProjectFacetVersion>();
newProjectFacets.addAll( this.facets );
newProjectFacets.add( fv );
setProjectFacets( newProjectFacets );
}
else if( existingVersion == fv )
{
return;
}
else
{
// TODO: needs exception msg
throw new IllegalArgumentException();
}
}
public synchronized void removeProjectFacet( final IProjectFacet f )
{
final IProjectFacetVersion fv = getProjectFacetVersion( f );
if( fv != null )
{
removeProjectFacet( getProjectFacetVersion( f ) );
}
}
public synchronized void removeProjectFacet( final IProjectFacetVersion fv )
{
final IProjectFacetVersion existingVersion = this.facets.get( fv.getProjectFacet() );
if( existingVersion == null )
{
return;
}
else if( existingVersion == fv )
{
final Set<IProjectFacetVersion> newProjectFacets = new HashSet<IProjectFacetVersion>();
newProjectFacets.addAll( this.facets );
newProjectFacets.remove( fv );
setProjectFacets( newProjectFacets );
}
else
{
// TODO: needs exception msg
throw new IllegalArgumentException();
}
}
public synchronized void changeProjectFacetVersion( final IProjectFacetVersion fv )
{
final IProjectFacetVersion existingVersion = this.facets.get( fv.getProjectFacet() );
if( existingVersion == null )
{
// TODO: needs exception msg
throw new IllegalArgumentException();
}
else if( existingVersion == fv )
{
return;
}
else
{
final Set<IProjectFacetVersion> newProjectFacets = new HashSet<IProjectFacetVersion>();
newProjectFacets.addAll( this.facets );
newProjectFacets.remove( existingVersion );
newProjectFacets.add( fv );
setProjectFacets( newProjectFacets );
}
}
private synchronized Set<IProjectFacetVersion> getBaseProjectFacets()
{
if( this.project == null )
{
return Collections.emptySet();
}
else
{
return this.project.getProjectFacets();
}
}
public synchronized Set<IPreset> getAvailablePresets()
{
return this.availablePresets.getUnmodifiable();
}
private synchronized void refreshAvailablePresets()
{
final IndexedSet<String,IPreset> newAvailablePresets = new IndexedSet<String,IPreset>();
Map<String,Object> context = null;
for( IPreset preset : ProjectFacetsManager.getPresets() )
{
if( preset.getType() == IPreset.Type.DYNAMIC )
{
if( context == null )
{
context = new HashMap<String,Object>();
context.put( IDynamicPreset.CONTEXT_KEY_FIXED_FACETS, this.fixedFacets );
context.put( IDynamicPreset.CONTEXT_KEY_PRIMARY_RUNTIME, this.primaryRuntime );
}
preset = ( (IDynamicPreset) preset ).resolve( context );
if( preset == null )
{
continue;
}
}
final Set<IProjectFacetVersion> facets = preset.getProjectFacets();
boolean applicable = true;
// All of the facets listed in the preset and their versions must be available.
for( IProjectFacetVersion fv : facets )
{
if( ! isFacetAvailable( fv ) )
{
applicable = false;
break;
}
}
// The preset must span across all of the fixed facets.
for( IProjectFacet f : this.fixedFacets )
{
boolean found = false;
for( IProjectFacetVersion fv : f.getVersions() )
{
if( facets.contains( fv ) )
{
found = true;
break;
}
}
if( ! found )
{
applicable = false;
break;
}
}
if( applicable )
{
newAvailablePresets.add( preset.getId(), preset );
}
}
if( ! this.availablePresets.equals( newAvailablePresets ) )
{
this.availablePresets = newAvailablePresets;
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.AVAILABLE_PRESETS_CHANGED ) );
if( this.selectedPreset != null &&
! this.availablePresets.containsKey( this.selectedPreset.getId() ) )
{
setSelectedPreset( null );
}
}
}
public synchronized IPreset getSelectedPreset()
{
return this.selectedPreset;
}
public synchronized void setSelectedPreset( final String presetId )
{
if( presetId != null && ! this.availablePresets.containsKey( presetId ) )
{
final String msg = Resources.bind( Resources.couldNotSelectPreset, presetId );
throw new IllegalArgumentException( msg );
}
final IPreset preset = this.availablePresets.get( presetId );
if( ! equals( this.selectedPreset, preset ) )
{
if( preset != null )
{
// The following line keeps the setProjectFacets() call that comes next from
// causing a preset change event from being generated. We want to avoid firing
// two preset change events while presenting a consistent data structure (the
// old preset isn't selected) to event handlers listening on the facet change
// event.
this.selectedPreset = null;
setProjectFacets( preset.getProjectFacets() );
}
this.selectedPreset = preset;
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.SELECTED_PRESET_CHANGED ) );
}
}
public synchronized Set<IRuntime> getTargetableRuntimes()
{
return this.targetableRuntimes;
}
public synchronized boolean isTargetable( final IRuntime runtime )
{
return this.targetableRuntimes.contains( runtime );
}
public synchronized void refreshTargetableRuntimes()
{
final Set<IRuntime> result = new HashSet<IRuntime>();
for( IRuntime r : RuntimeManager.getRuntimes() )
{
boolean ok;
if( this.project != null &&
this.project.getTargetedRuntimes().contains( r ) )
{
ok = true;
}
else
{
ok = true;
for( IProjectFacetVersion fv : this.facets )
{
if( ! r.supports( fv ) )
{
ok = false;
break;
}
}
}
if( ok )
{
result.add( r );
}
}
if( ! this.targetableRuntimes.equals( result ) )
{
this.targetableRuntimes.clear();
this.targetableRuntimes.addAll( result );
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.TARGETABLE_RUNTIMES_CHANGED ) );
final List<IRuntime> toRemove = new ArrayList<IRuntime>();
for( IRuntime r : this.targetedRuntimes )
{
if( ! this.targetableRuntimes.contains( r ) )
{
toRemove.add( r );
}
}
this.targetedRuntimes.removeAll( toRemove );
if( ! toRemove.isEmpty() )
{
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.TARGETED_RUNTIMES_CHANGED ) );
if( this.primaryRuntime != null &&
! this.targetableRuntimes.contains( this.primaryRuntime ) )
{
autoAssignPrimaryRuntime();
}
}
}
}
public synchronized Set<IRuntime> getTargetedRuntimes()
{
return this.targetedRuntimes;
}
public synchronized boolean isTargeted( final IRuntime runtime )
{
return this.targetedRuntimes.contains( runtime );
}
public synchronized void setTargetedRuntimes( final Set<IRuntime> runtimes )
{
if( ! this.targetedRuntimes.equals( runtimes ) )
{
this.targetedRuntimes.clear();
for( IRuntime r : runtimes )
{
if( this.targetableRuntimes.contains( r ) )
{
this.targetedRuntimes.add( r );
}
}
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.TARGETED_RUNTIMES_CHANGED ) );
if( this.primaryRuntime == null ||
! this.targetedRuntimes.contains( this.primaryRuntime ) )
{
autoAssignPrimaryRuntime();
}
}
}
public synchronized void addTargetedRuntime( final IRuntime runtime )
{
if( runtime == null )
{
throw new NullPointerException();
}
else
{
this.targetedRuntimes.add( runtime );
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.TARGETED_RUNTIMES_CHANGED ) );
if( this.primaryRuntime == null )
{
this.primaryRuntime = runtime;
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.PRIMARY_RUNTIME_CHANGED ) );
}
}
}
public synchronized void removeTargetedRuntime( final IRuntime runtime )
{
if( runtime == null )
{
throw new NullPointerException();
}
else
{
if( this.targetedRuntimes.remove( runtime ) )
{
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.TARGETED_RUNTIMES_CHANGED ) );
if( runtime.equals( this.primaryRuntime ) )
{
autoAssignPrimaryRuntime();
}
}
}
}
public synchronized IRuntime getPrimaryRuntime()
{
return this.primaryRuntime;
}
public synchronized void setPrimaryRuntime( final IRuntime runtime )
{
if( ! equals( this.primaryRuntime, runtime ) )
{
if( runtime == null && this.targetedRuntimes.size() > 0 )
{
throw new IllegalArgumentException();
}
if( this.targetedRuntimes.contains( runtime ) )
{
this.primaryRuntime = runtime;
}
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.PRIMARY_RUNTIME_CHANGED ) );
}
}
private void autoAssignPrimaryRuntime()
{
if( this.targetedRuntimes.isEmpty() )
{
this.primaryRuntime = null;
}
else
{
// Pick one to be the primary. No special semantics as to which
// one.
this.primaryRuntime = this.targetedRuntimes.iterator().next();
}
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.PRIMARY_RUNTIME_CHANGED ) );
}
public synchronized Set<Action> getProjectFacetActions()
{
return this.actions;
}
public synchronized Action getProjectFacetAction( final Action.Type type,
final IProjectFacetVersion f )
{
return getProjectFacetAction( this.actions, type, f );
}
private static Action getProjectFacetAction( final Set<Action> actions,
final Action.Type type,
final IProjectFacetVersion fv )
{
for( Action action : actions )
{
if( action.getType() == type && action.getProjectFacetVersion() == fv )
{
return action;
}
}
return null;
}
private static Action getProjectFacetAction( final Set<Action> actions,
final Action.Type type,
final IProjectFacet f )
{
for( Action action : actions )
{
if( action.getType() == type &&
action.getProjectFacetVersion().getProjectFacet() == f )
{
return action;
}
}
return null;
}
private synchronized Action createProjectFacetAction( final Set<Action> actions,
final Action.Type type,
final IProjectFacetVersion fv )
{
Action action = getProjectFacetAction( actions, type, fv );
if( action == null )
{
final Set<IProjectFacetVersion> base = getBaseProjectFacets();
Object config = null;
if( fv.supports( base, type ) )
{
try
{
final IProjectFacet f = fv.getProjectFacet();
action = getProjectFacetAction( actions, type, f );
if( action != null )
{
final IProjectFacetVersion current
= action.getProjectFacetVersion();
if( fv.supports( base, type ) &&
current.supports( base, type ) &&
fv.getActionDefinition( base, type )
== current.getActionDefinition( base, type ) )
{
config = action.getConfig();
IActionConfig c = null;
if( config instanceof IActionConfig )
{
c = (IActionConfig) config;
}
else if( config != null )
{
final IAdapterManager m
= Platform.getAdapterManager();
final String t
= IActionConfig.class.getName();
c = (IActionConfig) m.loadAdapter( config, t );
}
if( c != null )
{
c.setVersion( fv );
}
}
}
if( config == null )
{
final IActionDefinition def = fv.getActionDefinition( base, type );
config = def.createConfigObject( fv, getProjectName() );
}
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
}
}
action = new Action( type, fv, config );
}
return action;
}
private synchronized void refreshProjectFacetActions()
{
final Set<IProjectFacetVersion> base = getBaseProjectFacets();
final Set<IProjectFacetVersion> sel = getProjectFacets();
final Set<Action> old = new HashSet<Action>( this.actions );
final Set<Action> newActions = new HashSet<Action>();
// What has been removed?
for( IProjectFacetVersion fv : base )
{
if( ! sel.contains( fv ) )
{
newActions.add( createProjectFacetAction( old, Action.Type.UNINSTALL, fv ) );
}
}
// What has been added?
for( IProjectFacetVersion fv : sel )
{
if( ! base.contains( fv ) )
{
newActions.add( createProjectFacetAction( old, Action.Type.INSTALL, fv ) );
}
}
// Coalesce uninstall/install pairs into version change actions, if
// possible.
final Set<Action> toadd = new HashSet<Action>();
final Set<Action> toremove = new HashSet<Action>();
for( Action action1 : newActions )
{
for( Action action2 : newActions )
{
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() )
{
toremove.add( action1 );
toremove.add( action2 );
toadd.add( createProjectFacetAction( old, Action.Type.VERSION_CHANGE, f2 ) );
}
}
}
}
newActions.removeAll( toremove );
newActions.addAll( toadd );
this.actions = newActions;
}
public synchronized void addListener( final IFacetedProjectListener listener,
final IFacetedProjectEvent.Type... types )
{
if( types.length == 0 )
{
throw new IllegalArgumentException();
}
for( IFacetedProjectEvent.Type type : types )
{
this.listeners.get( type ).add( listener );
}
}
public synchronized void removeListener( final IFacetedProjectListener listener )
{
for( List<IFacetedProjectListener> listeners : this.listeners.values() )
{
listeners.remove( listener );
}
}
private synchronized void notifyListeners( final IFacetedProjectEvent event )
{
final List<IFacetedProjectListener> listeners = this.listeners.get( event.getType() );
for( IFacetedProjectListener listener : listeners )
{
listener.handleEvent( event );
}
}
public synchronized IStatus validate( final IProgressMonitor monitor )
{
return validate();
}
public synchronized IStatus validate()
{
if( this.problems.isEmpty() )
{
return Status.OK_STATUS;
}
else
{
final IStatus[] probs = this.problems.toArray( new IStatus[ this.problems.size() ] );
return Constraint.createMultiStatus( probs );
}
}
private synchronized void performValidation()
{
final Set<IProjectFacetVersion> base = getBaseProjectFacets();
final List<IStatus> probs = new ArrayList<IStatus>();
final MultiStatus ms = (MultiStatus) ProjectFacetsManager.check( base, this.actions );
for( IStatus st : ms.getChildren() )
{
probs.add( st );
}
for( IProjectFacetVersion fv : base )
{
final IProjectFacet f = fv.getProjectFacet();
String msg = null;
if( f.getPluginId() == null )
{
msg = NLS.bind( Resources.facetNotFound, f.getId() );
}
else if( fv.getPluginId() == null )
{
msg = NLS.bind( Resources.facetVersionNotFound, f.getId(),
fv.getVersionString() );
}
if( msg != null )
{
final IStatus sub
= new Status( IStatus.WARNING, FacetCorePlugin.PLUGIN_ID, 0, msg, null );
probs.add( sub );
}
}
for( IRuntime r : getTargetedRuntimes() )
{
for( IProjectFacetVersion fv : getProjectFacets() )
{
if( ! r.supports( fv ) )
{
final String msg
= NLS.bind( Resources.facetNotSupportedByTarget, fv.toString(),
r.getLocalizedName() );
final IStatus sub
= new Status( IStatus.ERROR, FacetCorePlugin.PLUGIN_ID, 0, msg, null );
probs.add( sub );
}
}
}
if( ! probs.equals( this.problems ) )
{
this.problems = probs;
notifyListeners( new FacetedProjectEvent( this, IFacetedProjectEvent.Type.VALIDATION_PROBLEMS_CHANGED ) );
}
}
public synchronized void commitChanges( final IProgressMonitor monitor )
throws CoreException
{
final SubMonitor pm = SubMonitor.convert( monitor, 24 );
try
{
// Create the project, if it doesn't exist already.
final IFacetedProject fpj;
if( this.project == null )
{
fpj = ProjectFacetsManager.create( this.projectName,
this.projectLocation, submon( pm, 2 ) );
}
else
{
fpj = this.project;
}
// Figure out whether we can set runtimes before applying facet actions. This is better
// for performance reasons, but may not work if the project contains facets that are
// not supported by the new runtime. You can get into this situation if the user tries
// to simultaneously uninstall a facet and select a different runtime. The fallback
// solution for this situation is to set the targeted runtimes to an empty list first,
// then apply the facet actions, and then set the targeted runtimes to the new list.
// This is more drastic than necessary in all situations, but it is not clear that
// implementing additional optimizations is necessary either.
boolean canSetRuntimesFirst = true;
for( IProjectFacetVersion fv : getProjectFacets() )
{
for( IRuntime r : getTargetedRuntimes() )
{
if( ! r.supports( fv ) )
{
canSetRuntimesFirst = false;
break;
}
}
if( ! canSetRuntimesFirst )
{
break;
}
}
pm.subTask( Resources.taskConfiguringRuntimes );
if( canSetRuntimesFirst )
{
fpj.setTargetedRuntimes( getTargetedRuntimes(), submon( pm, 2 ) );
if( fpj.getPrimaryRuntime() != null )
{
fpj.setPrimaryRuntime( getPrimaryRuntime(), submon( pm, 2 ) );
}
else
{
pm.worked( 2 );
}
}
else
{
final Set<IRuntime> emptySet = Collections.emptySet();
fpj.setTargetedRuntimes( emptySet, submon( pm, 2 ) );
}
fpj.modify( getProjectFacetActions(),
pm.newChild( 16, SubMonitor.SUPPRESS_SETTASKNAME ) );
if( ! canSetRuntimesFirst )
{
pm.subTask( Resources.taskConfiguringRuntimes );
fpj.setTargetedRuntimes( getTargetedRuntimes(), submon( pm, 2 ) );
if( fpj.getPrimaryRuntime() != null )
{
fpj.setPrimaryRuntime( getPrimaryRuntime(), submon( pm, 2 ) );
}
else
{
pm.worked( 2 );
}
}
fpj.setFixedProjectFacets( getFixedProjectFacets() );
pm.worked( 2 );
}
finally
{
pm.done();
}
}
public synchronized void mergeChanges( final IFacetedProjectWorkingCopy fpjwc )
{
// TODO: Both this method and the clone method ignore the action config objects.
synchronized( fpjwc )
{
setProjectName( fpjwc.getProjectName() );
setProjectLocation( fpjwc.getProjectLocation() );
setFixedProjectFacets( fpjwc.getFixedProjectFacets() );
setProjectFacets( fpjwc.getProjectFacets() );
setTargetedRuntimes( fpjwc.getTargetedRuntimes() );
setPrimaryRuntime( fpjwc.getPrimaryRuntime() );
final IPreset selectedPreset = fpjwc.getSelectedPreset();
if( selectedPreset != null )
{
setSelectedPreset( selectedPreset.getId() );
}
}
}
public synchronized IFacetedProjectWorkingCopy clone()
{
final FacetedProjectWorkingCopy clone = new FacetedProjectWorkingCopy( this.project );
clone.setProjectName( getProjectName() );
clone.setProjectLocation( getProjectLocation() );
clone.setFixedProjectFacets( getFixedProjectFacets() );
clone.setProjectFacets( getProjectFacets() );
clone.setTargetedRuntimes( getTargetedRuntimes() );
clone.setPrimaryRuntime( getPrimaryRuntime() );
final IPreset selectedPreset = getSelectedPreset();
if( selectedPreset != null )
{
clone.setSelectedPreset( selectedPreset.getId() );
}
return clone;
}
public void dispose()
{
synchronized( this.disposeTasks )
{
for( Runnable task : this.disposeTasks )
{
try
{
task.run();
}
catch( Exception e )
{
FacetCorePlugin.log( e );
}
}
}
}
public void addDisposeTask( final Runnable task )
{
synchronized( this.disposeTasks )
{
this.disposeTasks.add( task );
}
}
private static boolean equals( final Object obj1,
final Object obj2 )
{
if( obj1 == obj2 )
{
return true;
}
else if( obj1 == null || obj2 == null )
{
return false;
}
else
{
return obj1.equals( obj2 );
}
}
private static SubMonitor submon( final SubMonitor parent,
final int ticks )
{
return parent.newChild( ticks, SubMonitor.SUPPRESS_ALL_LABELS );
}
private static final class Resources
extends NLS
{
public static String couldNotSelectPreset;
public static String facetNotFound;
public static String facetVersionNotFound;
public static String facetNotSupportedByTarget;
public static String taskConfiguringRuntimes;
static
{
initializeMessages( FacetedProjectWorkingCopy.class.getName(),
Resources.class );
}
}
}