blob: 2041796799d73ba8a09062c29561733c4822dc5b [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2010, 2021 Oracle and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Konstantin Komissarchik - initial implementation and ongoing maintenance
******************************************************************************/
package org.eclipse.wst.common.project.facet.core.internal;
import static org.eclipse.wst.common.project.facet.core.util.internal.FileUtil.FILE_DOT_PROJECT;
import static org.eclipse.wst.common.project.facet.core.util.internal.FileUtil.validateEdit;
import static org.eclipse.wst.common.project.facet.core.util.internal.PluginUtil.findOptionalElement;
import static org.eclipse.wst.common.project.facet.core.util.internal.PluginUtil.findRequiredAttribute;
import static org.eclipse.wst.common.project.facet.core.util.internal.PluginUtil.getElementValue;
import static org.eclipse.wst.common.project.facet.core.util.internal.PluginUtil.instantiate;
import static org.eclipse.wst.common.project.facet.core.util.internal.PluginUtil.reportMissingAttribute;
import static org.eclipse.wst.common.project.facet.core.util.internal.PluginUtil.reportMissingElement;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
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.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.SubProgressMonitor;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.common.project.facet.core.IActionDefinition;
import org.eclipse.wst.common.project.facet.core.ICategory;
import org.eclipse.wst.common.project.facet.core.IConstraint;
import org.eclipse.wst.common.project.facet.core.IDefaultVersionProvider;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.IFacetedProject.Action;
import org.eclipse.wst.common.project.facet.core.IFacetedProjectTemplate;
import org.eclipse.wst.common.project.facet.core.IFacetedProjectWorkingCopy;
import org.eclipse.wst.common.project.facet.core.IGroup;
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.IVersionExpr;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
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.internal.EventsExtensionPoint;
import org.eclipse.wst.common.project.facet.core.events.internal.FacetedProjectFrameworkEvent;
import org.eclipse.wst.common.project.facet.core.events.internal.FrameworkListenerRegistry;
import org.eclipse.wst.common.project.facet.core.events.internal.LegacyEventHandlerAdapter;
import org.eclipse.wst.common.project.facet.core.events.internal.ProjectListenerRegistry;
import org.eclipse.wst.common.project.facet.core.util.internal.IndexedSet;
import org.eclipse.wst.common.project.facet.core.util.internal.PluginUtil.InvalidExtensionException;
import org.eclipse.wst.common.project.facet.core.util.internal.VersionExpr;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
/**
* The implementation of the {@link ProjectFacetsManager} abstract class.
*
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
public final class FacetedProjectFrameworkImpl
{
private static final String EXTENSION_ID = "facets"; //$NON-NLS-1$
private static final String ATTR_CATEGORY = "category"; //$NON-NLS-1$
private static final String ATTR_CLASS = "class"; //$NON-NLS-1$
private static final String ATTR_FACET = "facet"; //$NON-NLS-1$
private static final String ATTR_GROUP = "group"; //$NON-NLS-1$
private static final String ATTR_ID = "id"; //$NON-NLS-1$
private static final String ATTR_NAME = "name"; //$NON-NLS-1$
private static final String ATTR_PROVIDER = "provider"; //$NON-NLS-1$
private static final String ATTR_SOFT = "soft"; //$NON-NLS-1$
private static final String ATTR_TYPE = "type"; //$NON-NLS-1$
private static final String ATTR_VALUE = "value"; //$NON-NLS-1$
private static final String ATTR_VERSION = "version"; //$NON-NLS-1$
private static final String EL_ACTION = "action"; //$NON-NLS-1$
private static final String EL_CATEGORY = "category"; //$NON-NLS-1$
private static final String EL_CONFIG_FACTORY = "config-factory"; //$NON-NLS-1$
private static final String EL_CONSTRAINT = "constraint"; //$NON-NLS-1$
private static final String EL_DEFAULT_VERSION = "default-version"; //$NON-NLS-1$
private static final String EL_DELEGATE = "delegate"; //$NON-NLS-1$
private static final String EL_DESCRIPTION = "description"; //$NON-NLS-1$
private static final String EL_EVENT_HANDLER = "event-handler"; //$NON-NLS-1$
private static final String EL_LABEL = "label"; //$NON-NLS-1$
private static final String EL_MEMBER = "member"; //$NON-NLS-1$
private static final String EL_PROJECT_FACET = "project-facet"; //$NON-NLS-1$
private static final String EL_PROJECT_FACET_VERSION = "project-facet-version"; //$NON-NLS-1$
private static final String EL_PROPERTY = "property"; //$NON-NLS-1$
private static final String EL_VERSION_COMPARATOR = "version-comparator"; //$NON-NLS-1$
private static final String DEFAULT_DESCRIPTION = ""; //$NON-NLS-1$
private static FacetedProjectFrameworkImpl instance = null;
private final IndexedSet<String,IProjectFacet> facets;
private final IndexedSet<String,IActionDefinition> actions;
private final IndexedSet<String,ICategory> categories;
private final IndexedSet<String,IPreset> presets;
private boolean presetsInitialized = false;
private final IndexedSet<String,IGroup> groups;
private final Map<String,FacetedProject> projects;
private final ProjectListenerRegistry projectListenerRegistry;
private final FrameworkListenerRegistry frameworkListenerRegistry;
private WeakReference<ProjectFacetPreferencesGroup> globalPreferencesGroup = null;
private Map<String,WeakReference<ProjectFacetPreferencesGroup>> projectPreferencesGroups
= new HashMap<String,WeakReference<ProjectFacetPreferencesGroup>>();
private FacetedProjectFrameworkImpl()
{
long activationStart = 0;
if( FacetCorePlugin.isTracingFrameworkActivation() )
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter( sw );
( new Throwable() ).printStackTrace( pw );
String context = sw.getBuffer().toString();
final int endOfFirstLine = context.indexOf( '\n' );
context = context.substring( endOfFirstLine + 1 );
context = context.replaceAll( "\\t", " " ); //$NON-NLS-1$ //$NON-NLS-2$
int length = context.length();
if( context.charAt( length - 2 ) == '\r' )
{
length = length - 2;
}
else
{
length = length - 1;
}
context = context.substring( 0, length );
final String msg
= NLS.bind( Resources.tracingFrameworkActivationStarting,
context );
System.out.println( msg );
activationStart = System.currentTimeMillis();
}
this.facets = new IndexedSet<String,IProjectFacet>();
this.actions = new IndexedSet<String,IActionDefinition>();
this.categories = new IndexedSet<String,ICategory>();
this.presets = new IndexedSet<String,IPreset>();
this.presetsInitialized = false;
this.groups = new IndexedSet<String,IGroup>();
this.projects = new HashMap<String,FacetedProject>();
this.projectListenerRegistry = new ProjectListenerRegistry();
this.frameworkListenerRegistry = new FrameworkListenerRegistry();
readMetadata();
EventsExtensionPoint.processExtensions( this );
( new ResourceChangeListener() ).register();
if( FacetCorePlugin.isTracingFrameworkActivation() )
{
final long duration
= System.currentTimeMillis() - activationStart;
final String msg
= NLS.bind( Resources.tracingFrameworkActivationFinished,
String.valueOf( duration ) );
System.out.println( msg );
}
}
public synchronized static FacetedProjectFrameworkImpl getInstance()
{
if( instance == null )
{
instance = new FacetedProjectFrameworkImpl();
}
return instance;
}
public Set<IProjectFacet> getProjectFacets()
{
return this.facets.getItemSet();
}
public boolean isProjectFacetDefined( final String id )
{
return this.facets.containsKey( id );
}
public IProjectFacet getProjectFacet( final String id )
{
final IProjectFacet f = this.facets.getItemByKey( id );
if( f == null )
{
final String msg = NLS.bind( Resources.facetNotDefined, id );
throw new IllegalArgumentException( msg );
}
return f;
}
public Set<IActionDefinition> getActionDefinitions()
{
return this.actions.getItemSet();
}
public boolean isActionDefined( final String id )
{
return this.actions.containsKey( id );
}
public IActionDefinition getActionDefinition( final String id )
{
final IActionDefinition adef = this.actions.getItemByKey( id );
if( adef == null )
{
final String msg = NLS.bind( Resources.actionNotDefined, id );
throw new IllegalArgumentException( msg );
}
return adef;
}
public Set<ICategory> getCategories()
{
return this.categories.getItemSet();
}
public boolean isCategoryDefined( final String id )
{
return this.categories.containsKey( id );
}
public ICategory getCategory( final String id )
{
final ICategory category = this.categories.getItemByKey( id );
if( category == null )
{
final String msg = NLS.bind( Resources.categoryNotDefined, id );
throw new IllegalArgumentException( msg );
}
return category;
}
public Set<IPreset> getPresets()
{
synchronized( this.presets )
{
initializePresets();
return this.presets.getItemSet();
}
}
public boolean isPresetDefined( final String id )
{
synchronized( this.presets )
{
initializePresets();
return this.presets.containsKey( id );
}
}
public IPreset getPreset( final String id )
{
synchronized( this.presets )
{
initializePresets();
final IPreset preset = this.presets.getItemByKey( id );
if( preset == null )
{
final String msg = NLS.bind( Resources.presetNotDefined, id );
throw new IllegalArgumentException( msg );
}
return preset;
}
}
public IPreset definePreset( final String name,
final Set<IProjectFacetVersion> facets )
{
return definePreset( name, null, facets );
}
public IPreset definePreset( final String name,
final String description,
final Set<IProjectFacetVersion> facets )
{
initializePresets();
final IPreset preset = definePreset( name, description, facets, true );
final IFacetedProjectFrameworkEvent event
= new FacetedProjectFrameworkEvent( IFacetedProjectFrameworkEvent.Type.PRESET_ADDED );
this.frameworkListenerRegistry.notifyListeners( event );
return preset;
}
private IPreset definePreset( final String name,
final String description,
final Set<IProjectFacetVersion> facets,
final boolean save )
{
synchronized( this.presets )
{
String id;
int i = 0;
do
{
id = ".usr." + i; //$NON-NLS-1$
i++;
}
while( this.presets.containsKey( id ) );
final UserPreset preset
= new UserPreset( id, name, description == null ? "" : description, //$NON-NLS-1$
facets );
this.presets.addItemWithKey( id, preset );
if( save )
{
saveUserPresets();
}
return preset;
}
}
public boolean deletePreset( final IPreset preset )
{
boolean deleted;
synchronized( this.presets )
{
initializePresets();
if( preset.getType() != IPreset.Type.USER_DEFINED )
{
return false;
}
deleted = this.presets.removeItemByKey( preset.getId() );
if( deleted )
{
saveUserPresets();
}
}
if( deleted )
{
final IFacetedProjectFrameworkEvent event
= new FacetedProjectFrameworkEvent( IFacetedProjectFrameworkEvent.Type.PRESET_REMOVED );
this.frameworkListenerRegistry.notifyListeners( event );
}
return deleted;
}
private void initializePresets()
{
synchronized( this.presets )
{
if( ! this.presetsInitialized )
{
for( IPreset preset : PresetsExtensionPoint.getPresets() )
{
this.presets.addItemWithKey( preset.getId(), preset );
}
readUserPresets();
this.presetsInitialized = true;
}
}
}
private void saveUserPresets()
{
try
{
final Preferences root = getUserPresetsPreferences();
final String[] children = root.childrenNames();
for( int i = 0; i < children.length; i++ )
{
root.node( children[ i ] ).removeNode();
}
for( IPreset preset : this.presets.getItemSet() )
{
if( preset.getType() == IPreset.Type.USER_DEFINED )
{
final Preferences pnode = root.node( preset.getId() );
pnode.put( EL_LABEL, preset.getLabel() );
pnode.put( EL_DESCRIPTION, preset.getDescription() );
int counter = 1;
for( IProjectFacetVersion fv : preset.getProjectFacets() )
{
final Preferences fnode = pnode.node( String.valueOf( counter ) );
fnode.put( ATTR_ID, fv.getProjectFacet().getId() );
fnode.put( ATTR_VERSION, fv.getVersionString() );
counter++;
}
}
}
root.flush();
}
catch( BackingStoreException e )
{
FacetCorePlugin.log( e );
}
}
private void readUserPresets()
{
try
{
final Preferences root = getUserPresetsPreferences();
final String[] pkeys = root.childrenNames();
for( int i = 0; i < pkeys.length; i++ )
{
final Preferences pnode = root.node( pkeys[ i ] );
final String label = pnode.get( EL_LABEL, null );
if( label == null )
{
break;
}
String description = pnode.get( EL_DESCRIPTION, null );
if( description == null )
{
description = ""; //$NON-NLS-1$
}
final String[] fkeys = pnode.childrenNames();
Set<IProjectFacetVersion> facets = new HashSet<IProjectFacetVersion>();
for( int j = 0; j < fkeys.length; j++ )
{
final Preferences fnode = pnode.node( fkeys[ j ] );
final String id = fnode.get( ATTR_ID, null );
final String version = fnode.get( ATTR_VERSION, null );
if( id == null || version == null )
{
facets = null;
break;
}
if( isProjectFacetDefined( id ) )
{
final IProjectFacet f = getProjectFacet( id );
if( f.hasVersion( version ) )
{
facets.add( f.getVersion( version ) );
}
else
{
facets = null;
break;
}
}
else
{
facets = null;
break;
}
}
if( facets != null )
{
definePreset( label, description, facets, false );
}
}
}
catch( BackingStoreException e )
{
FacetCorePlugin.log( e );
}
}
private static Preferences getUserPresetsPreferences()
{
final InstanceScope scope = new InstanceScope();
final IEclipsePreferences pluginRoot
= scope.getNode( FacetCorePlugin.PLUGIN_ID );
return pluginRoot.node( "user.presets" ); //$NON-NLS-1$
}
public Set<IFacetedProjectTemplate> getTemplates()
{
return FacetedProjectTemplatesExtensionPoint.getTemplates();
}
public boolean isTemplateDefined( final String id )
{
return ( FacetedProjectTemplatesExtensionPoint.getTemplate( id ) != null );
}
public IFacetedProjectTemplate getTemplate( final String id )
{
final IFacetedProjectTemplate template
= FacetedProjectTemplatesExtensionPoint.getTemplate( id );
if( template == null )
{
final String msg = NLS.bind( Resources.templateNotDefined, id );
throw new IllegalArgumentException( msg );
}
return template;
}
public Set<IGroup> getGroups()
{
return this.groups.getItemSet();
}
public boolean isGroupDefined( final String id )
{
return this.groups.containsKey( id );
}
public IGroup getGroup( final String id )
{
final IGroup group = this.groups.getItemByKey( id );
if( group == null )
{
final String msg = NLS.bind( Resources.groupNotDefined, id );
throw new IllegalArgumentException( msg );
}
return group;
}
void addGroup( final IGroup group )
{
this.groups.addItemWithKey( group.getId(), group );
}
public Set<IFacetedProject> getFacetedProjects()
throws CoreException
{
return getFacetedProjects( null, null );
}
public Set<IFacetedProject> getFacetedProjects( final IProjectFacet f )
throws CoreException
{
return getFacetedProjects( f, null );
}
public Set<IFacetedProject> getFacetedProjects( final IProjectFacetVersion fv )
throws CoreException
{
return getFacetedProjects( null, fv );
}
private Set<IFacetedProject> getFacetedProjects( final IProjectFacet f,
final IProjectFacetVersion fv )
throws CoreException
{
final IProject[] all
= ResourcesPlugin.getWorkspace().getRoot().getProjects();
final Set<IFacetedProject> result = new HashSet<IFacetedProject>();
for( int i = 0; i < all.length; i++ )
{
final IProject proj = all[ i ];
final IFacetedProject fproj = create( proj );
if( fproj != null )
{
if( ( f != null && ! fproj.hasProjectFacet( f ) ) ||
( fv != null && ! fproj.hasProjectFacet( fv ) ) )
{
continue;
}
else
{
result.add( fproj );
}
}
}
return result;
}
public IFacetedProjectWorkingCopy createNewProject()
{
return new FacetedProjectWorkingCopy( null );
}
public IFacetedProject create( final IProject project )
throws CoreException
{
if( project.isAccessible() &&
project.isNatureEnabled( FacetedProjectNature.NATURE_ID ) )
{
FacetedProject fproj = null;
synchronized( this.projects )
{
fproj = this.projects.get( project.getName() );
if( fproj == null )
{
fproj = new FacetedProject( project );
this.projects.put( project.getName(), fproj );
}
}
fproj.refresh();
return fproj;
}
return null;
}
public IFacetedProject create( final IProject project,
final boolean convertIfNecessary,
final IProgressMonitor monitor)
throws CoreException
{
if( monitor != null )
{
monitor.beginTask( "", 1 ); //$NON-NLS-1$
}
try
{
if( project.isAccessible() &&
! project.isNatureEnabled( FacetedProjectNature.NATURE_ID ) &&
convertIfNecessary )
{
final IProjectDescription description = project.getDescription();
final String[] prevNatures = description.getNatureIds();
final String[] newNatures = new String[ prevNatures.length + 1 ];
System.arraycopy( prevNatures, 0, newNatures, 0, prevNatures.length );
newNatures[ prevNatures.length ] = FacetedProjectNature.NATURE_ID;
description.setNatureIds( newNatures );
validateEdit( project.getFile( FILE_DOT_PROJECT ) );
project.setDescription( description, submon( monitor, 1 ) );
}
return create( project );
}
finally
{
if( monitor != null )
{
monitor.done();
}
}
}
public IFacetedProject create( final String name,
final IPath location,
final IProgressMonitor monitor )
throws CoreException
{
if( monitor != null )
{
monitor.beginTask( "", 2 ); //$NON-NLS-1$
}
try
{
final IWorkspace ws = ResourcesPlugin.getWorkspace();
final IProject project = ws.getRoot().getProject( name );
final IProjectDescription desc
= ws.newProjectDescription( name );
desc.setLocation( location );
project.create( desc, submon( monitor, 1 ) );
project.open( submon( monitor, 1 ) );
// This is odd, but apparently nature's configure() method will only
// be called if the setDescription() method is used. It will not be
// called if nature is added to the project description prior to
// calling IProject.create() method.
desc.setNatureIds( new String[] { FacetedProjectNature.NATURE_ID } );
project.setDescription( desc, null );
return create( project );
}
finally
{
if( monitor != null )
{
monitor.done();
}
}
}
public void addListener( final IFacetedProjectListener listener,
final IFacetedProjectEvent.Type... types )
{
this.projectListenerRegistry.addListener( listener, types );
}
public void addListener( final IFacetedProjectFrameworkListener listener,
final IFacetedProjectFrameworkEvent.Type... types )
{
this.frameworkListenerRegistry.addListener( listener, types );
}
public void removeListener( final IFacetedProjectListener listener )
{
this.projectListenerRegistry.removeListener( listener );
}
public void removeListener( final IFacetedProjectFrameworkListener listener )
{
this.frameworkListenerRegistry.removeListener( listener );
}
ProjectListenerRegistry getProjectListenerRegistry()
{
return this.projectListenerRegistry;
}
public IStatus check( final Set<IProjectFacetVersion> base,
final Set<Action> actions )
{
MultiStatus result = Constraint.createMultiStatus();
// Verify that all of the actions are supported.
for( Action action : actions )
{
if( ! action.getProjectFacetVersion().supports( base, action.getType() ) )
{
final ValidationProblem.Type ptype;
if( action.getType() == Action.Type.INSTALL )
{
ptype = ValidationProblem.Type.INSTALL_NOT_SUPPORTED;
}
else if( action.getType() == Action.Type.UNINSTALL )
{
ptype = ValidationProblem.Type.UNINSTALL_NOT_SUPPORTED;
}
else if( action.getType() == Action.Type.VERSION_CHANGE )
{
ptype = ValidationProblem.Type.VERSION_CHANGE_NOT_SUPPORTED;
}
else
{
throw new IllegalStateException();
}
final IProjectFacetVersion fv = action.getProjectFacetVersion();
final ValidationProblem vp
= new ValidationProblem( ptype,
fv.getProjectFacet().getLabel(),
fv.getVersionString() );
result.add( vp );
}
}
// Multiple actions on the same facet are not supported in the same
// batch. The only exception is an uninstall of a previosly-installed
// version followed by an install of a new version.
final Map<IProjectFacet,Set<Action>> facetToActionsMap
= new HashMap<IProjectFacet,Set<Action>>();
for( Action action : actions )
{
final IProjectFacet f = action.getProjectFacetVersion().getProjectFacet();
Set<Action> group = facetToActionsMap.get( f );
if( group == null )
{
group = new HashSet<Action>();
facetToActionsMap.put( f, group );
}
group.add( action );
}
for( Set<Action> group : facetToActionsMap.values() )
{
if( group.size() > 1 )
{
boolean bad = true;
if( group.size() == 2 )
{
Action install = null;
Action uninstall = null;
for( Action action : group )
{
if( action.getType() == Action.Type.INSTALL )
{
install = action;
}
else if( action.getType() == Action.Type.UNINSTALL )
{
uninstall = action;
}
else
{
break;
}
}
if( install != null && uninstall != null )
{
if( base.contains( uninstall.getProjectFacetVersion() ) )
{
bad = false;
}
}
}
if( bad )
{
final ValidationProblem.Type ptype
= ValidationProblem.Type.MULTIPLE_ACTIONS_NOT_SUPPORTED;
result.add( new ValidationProblem( ptype ) );
break;
}
}
}
// Check for attempts to uninstall or change version of facets that
// haven't been installed. Also check for attempts to install a facet
// that's already installed.
for( Action action : actions )
{
final IProjectFacetVersion fv = action.getProjectFacetVersion();
final IProjectFacet f = fv.getProjectFacet();
ValidationProblem.Type ptype = null;
if( action.getType() == Action.Type.UNINSTALL )
{
if( ! base.contains( fv ) )
{
ptype = ValidationProblem.Type.CANNOT_UNINSTALL;
}
}
else
{
IProjectFacetVersion existing = null;
for( IProjectFacetVersion temp : base )
{
if( temp.getProjectFacet() == f )
{
existing = temp;
break;
}
}
if( action.getType() == Action.Type.VERSION_CHANGE && existing == null )
{
ptype = ValidationProblem.Type.CANNOT_CHANGE_VERSION;
}
else if( action.getType() == Action.Type.INSTALL && existing != null )
{
// this is nearly useless to the end user
ptype = ValidationProblem.Type.FACET_ALREADY_INSTALLED;
}
}
if( ptype != null )
{
result.add( new ValidationProblem( ptype, f.getLabel(),
fv.getVersionString() ) );
}
}
// Abort at this point if there are any validation problems.
if( ! result.isOK() )
{
return result;
}
// Apply all the uninstall actions.
final Set<IProjectFacetVersion> all = new HashSet<IProjectFacetVersion>( base );
for( Action action : actions )
{
if( action.getType() == Action.Type.UNINSTALL )
{
apply( all, action );
}
}
// Apply all the install and version change actions.
for( Action action : actions )
{
if( action.getType() != Action.Type.UNINSTALL )
{
apply( all, action );
}
}
// Check the constrains on all of the facets.
for( IProjectFacetVersion fv : all )
{
final IConstraint constraint = fv.getConstraint();
if( constraint != null )
{
final IStatus st = constraint.check( all );
if( ! st.isOK() )
{
result.addAll( st );
}
}
}
// Eliminate symmetric conflicts problem entries.
final Set<IStatus> problems = new HashSet<IStatus>();
IStatus[] children = result.getChildren();
for( IStatus child : children )
{
problems.add( child );
}
final Set<IStatus> toremove = new HashSet<IStatus>();
for( IStatus problem : problems )
{
if( ! toremove.contains( problem ) )
{
final ValidationProblem valprob = (ValidationProblem) problem;
if( valprob.getType() == ValidationProblem.Type.CONFLICTS )
{
final Object[] p = valprob.getParameters();
final ValidationProblem reverse
= new ValidationProblem( ValidationProblem.Type.CONFLICTS,
new Object[] { p[ 1 ], p[ 0 ] } );
toremove.add( reverse );
}
}
}
if( toremove.size() > 0 )
{
problems.removeAll( toremove );
children = problems.toArray( new IStatus[ problems.size() ] );
result = Constraint.createMultiStatus( children );
}
// Return the problems to the caller.
return result;
}
public void sort( final Set<IProjectFacetVersion> base,
final List<Action> actions )
{
final int count = actions.size();
if( count == 0 )
{
return;
}
// Before sorting, check that the constraints can be met. Otherwise
// the sort algorithm will not terminate.
final IStatus st = check( base, new HashSet<Action>( actions ) );
if( ! st.isOK() )
{
FacetCorePlugin.log( st );
return;
}
// Initialize tracing.
List<Action> unsorted = null;
int steps = 0;
if( FacetCorePlugin.isTracingActionSorting() )
{
unsorted = new ArrayList<Action>( actions );
}
// Step 1 : Pre-sort all uninstall actions to the front of the list.
// Within that order, sort actions based on facet id. The
// secondary sort assures a stable sort order among actions on
// unrelated facets.
final Comparator<Action> comp = new Comparator<Action>()
{
public int compare( final Action a1,
final Action a2 )
{
int res = compare( a1.getType(), a2.getType() );
if( res == 0 )
{
final String fid1
= a1.getProjectFacetVersion().getProjectFacet().getId();
final String fid2
= a2.getProjectFacetVersion().getProjectFacet().getId();
res = fid1.compareTo( fid2 );
}
return res;
}
private int compare( final Action.Type t1,
final Action.Type t2 )
{
if( t1 == t2 )
{
return 0;
}
else if( t1 == Action.Type.UNINSTALL )
{
return -1;
}
else if( t2 == Action.Type.UNINSTALL )
{
return 1;
}
else
{
return 0;
}
}
};
Collections.sort( actions, comp );
// Step 2 : Sort based on the constraints.
final Set<IProjectFacetVersion> fnl = new HashSet<IProjectFacetVersion>( base );
for( Action action : actions )
{
apply( fnl, action );
}
boolean makeAnotherPass = true;
while( makeAnotherPass )
{
makeAnotherPass = false;
Set<IProjectFacetVersion> state = new HashSet<IProjectFacetVersion>( base );
for( int i = 0; i < count; )
{
final Action action = actions.get( i );
final Action.Type type = action.getType();
final IProjectFacetVersion fv = action.getProjectFacetVersion();
final IConstraint constraint = fv.getConstraint();
if( type == Action.Type.UNINSTALL )
{
if( ! constraint.check( state, true ).isOK() &&
constraint.check( base, true ).isOK() )
{
moveToFront( actions, i );
makeAnotherPass = true;
steps++;
break;
}
else
{
apply( state, action );
i++;
}
}
else
{
if( constraint.check( state ).isOK() &&
! ( ! constraint.check( state, true ).isOK() &&
constraint.check( fnl, true ).isOK() ) )
{
apply( state, action );
i++;
}
else
{
moveToEnd( actions, i );
steps++;
}
}
}
}
// Output tracing information.
if( FacetCorePlugin.isTracingActionSorting() )
{
final String text
= Resources.bind( Resources.tracingActionSorting,
toString( base ), toString( unsorted ),
toString( actions ), String.valueOf( steps ) );
System.out.println( text );
}
}
static void apply( final Set<IProjectFacetVersion> facets,
final Action action )
{
final Action.Type type = action.getType();
final IProjectFacetVersion fv = action.getProjectFacetVersion();
if( type == Action.Type.INSTALL )
{
facets.add( fv );
}
else if( type == Action.Type.UNINSTALL )
{
facets.remove( fv );
}
else if( type == Action.Type.VERSION_CHANGE )
{
for( IProjectFacetVersion x : facets )
{
if( x.getProjectFacet() == fv.getProjectFacet() )
{
facets.remove( x );
break;
}
}
facets.add( fv );
}
}
public synchronized Preferences getPreferences( final IProjectFacet facet )
throws BackingStoreException
{
ProjectFacetPreferencesGroup group = null;
if( this.globalPreferencesGroup != null )
{
group = this.globalPreferencesGroup.get();
}
if( group == null )
{
group = new ProjectFacetPreferencesGroup( null );
this.globalPreferencesGroup = new WeakReference<ProjectFacetPreferencesGroup>( group );
}
return group.getPreferences( facet );
}
public synchronized Preferences getPreferences( final IProjectFacet facet,
final IFacetedProject project )
throws BackingStoreException
{
final String pjname = project.getProject().getName();
ProjectFacetPreferencesGroup group = null;
WeakReference<ProjectFacetPreferencesGroup> ref = this.projectPreferencesGroups.get( pjname );
if( ref != null )
{
group = ref.get();
}
if( group == null )
{
group = new ProjectFacetPreferencesGroup( project );
ref = new WeakReference<ProjectFacetPreferencesGroup>( group );
this.projectPreferencesGroups.put( pjname, ref );
}
return group.getPreferences( facet );
}
private static IProgressMonitor submon( final IProgressMonitor monitor,
final int ticks )
{
if( monitor == null )
{
return null;
}
else
{
return new SubProgressMonitor( monitor, ticks );
}
}
private static void moveToFront( final List<Action> actions,
final int index )
{
final Action action = actions.get( index );
for( int i = index; i > 0; i-- )
{
actions.set( i, actions.get( i - 1 ) );
}
actions.set( 0, action );
}
private static void moveToEnd( final List<Action> actions,
final int index )
{
final Action action = actions.get( index );
for( int i = index + 1, n = actions.size(); i < n; i++ )
{
actions.set( i - 1, actions.get( i ) );
}
actions.set( actions.size() - 1, action );
}
private void readMetadata()
{
final IExtensionRegistry registry = Platform.getExtensionRegistry();
final IExtensionPoint point
= registry.getExtensionPoint( FacetCorePlugin.PLUGIN_ID,
EXTENSION_ID );
if( point == null )
{
throw new RuntimeException( "Extension point not found!" ); //$NON-NLS-1$
}
final List<IConfigurationElement> cfgels = new ArrayList<IConfigurationElement>();
for( IExtension extension : point.getExtensions() )
{
for( IConfigurationElement cfgel : extension.getConfigurationElements() )
{
cfgels.add( cfgel );
}
}
for( IConfigurationElement config : cfgels )
{
if( config.getName().equals( EL_CATEGORY ) )
{
try
{
readCategory( config );
}
catch( InvalidExtensionException e )
{
// Continue. The problem has been reported in the log.
}
}
}
for( IConfigurationElement config : cfgels )
{
if( config.getName().equals( EL_PROJECT_FACET ) )
{
try
{
readProjectFacet( config );
}
catch( InvalidExtensionException e )
{
// Continue. The problem has been reported in the log.
}
}
}
final Map<ProjectFacetVersion,IConfigurationElement> fvToConstraint
= new HashMap<ProjectFacetVersion,IConfigurationElement>();
final Map<ProjectFacetVersion,List<IConfigurationElement>> fvToActions
= new HashMap<ProjectFacetVersion,List<IConfigurationElement>>();
for( IConfigurationElement config : cfgels )
{
if( config.getName().equals( EL_PROJECT_FACET_VERSION ) )
{
readProjectFacetVersion( config, fvToConstraint, fvToActions );
}
}
calculateVersionComparisonTables( fvToConstraint, fvToActions );
ProjectFacetGroupsExtensionPoint.processExtensions( this );
for( Map.Entry<ProjectFacetVersion,IConfigurationElement> x : fvToConstraint.entrySet() )
{
readConstraint( x.getValue(), x.getKey() );
}
for( Map.Entry<ProjectFacetVersion,List<IConfigurationElement>> x : fvToActions.entrySet() )
{
final ProjectFacetVersion fv = x.getKey();
final List<IConfigurationElement> actions = x.getValue();
for( IConfigurationElement config : actions )
{
readAction( config, (ProjectFacet) fv.getProjectFacet(), fv.getVersionString() );
}
}
for( IConfigurationElement config : cfgels )
{
if( config.getName().equals( EL_ACTION ) )
{
readAction( config );
}
else if( config.getName().equals( EL_EVENT_HANDLER ) )
{
readEventHandler( config );
}
}
for( IConfigurationElement config : cfgels )
{
if( config.getName().equals( EL_PROJECT_FACET ) )
{
readDefaultVersionInfo( config );
}
}
}
private void readCategory( final IConfigurationElement config )
throws InvalidExtensionException
{
final Category category = new Category();
category.setId( findRequiredAttribute( config, ATTR_ID ) );
category.setPluginId( config.getContributor().getName() );
final IConfigurationElement elLabel = findOptionalElement( config, EL_LABEL );
category.setLabel( getElementValue( elLabel, category.getId() ) );
final IConfigurationElement elDesc = findOptionalElement( config, EL_DESCRIPTION );
category.setDescription( getElementValue( elDesc, DEFAULT_DESCRIPTION ) );
this.categories.addItemWithKey( category.getId(), category );
}
private void readProjectFacet( final IConfigurationElement config )
throws InvalidExtensionException
{
final ProjectFacet f = new ProjectFacet();
f.setId( findRequiredAttribute( config, ATTR_ID ) );
f.setPluginId( config.getContributor().getName() );
final IConfigurationElement elLabel = findOptionalElement( config, EL_LABEL );
f.setLabel( getElementValue( elLabel, f.getId() ) );
final IConfigurationElement elDesc = findOptionalElement( config, EL_DESCRIPTION );
f.setDescription( getElementValue( elDesc, DEFAULT_DESCRIPTION ) );
final IConfigurationElement elComp = findOptionalElement( config, EL_VERSION_COMPARATOR );
if( elComp != null )
{
f.setVersionComparator( findRequiredAttribute( elComp, ATTR_CLASS ) );
}
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( EL_PROPERTY ) )
{
final String name = child.getAttribute( ATTR_NAME );
if( name == null )
{
reportMissingAttribute( child, ATTR_NAME );
continue;
}
final String value = child.getAttribute( ATTR_VALUE );
if( value == null )
{
reportMissingAttribute( child, ATTR_VALUE );
continue;
}
Object parsedValue = value;
if( name.equals( IProjectFacet.PROP_HIDE_VERSION ) )
{
parsedValue = Boolean.parseBoolean( value );
}
f.setProperty( name, parsedValue );
}
}
String catname = null;
final IConfigurationElement elMember = findOptionalElement( config, EL_MEMBER );
if( elMember != null )
{
catname = findRequiredAttribute( elMember, ATTR_CATEGORY );
}
// ## DEPRECATED : 2.0 ##
{
final IConfigurationElement elCategory = findOptionalElement( config, EL_CATEGORY );
if( elCategory != null )
{
catname = getElementValue( elCategory, null );
}
}
if( catname != null )
{
if( isCategoryDefined( catname ) )
{
final Category category = (Category) getCategory( catname );
f.setCategory( category );
category.addProjectFacet( f );
}
else
{
final String msg
= NLS.bind( Resources.categoryNotDefined, catname ) +
NLS.bind( Resources.usedInPlugin, config.getContributor().getName() );
FacetCorePlugin.log( msg );
}
}
this.facets.addItem( f );
this.facets.addKey( f.getId(), f );
for( String alias : ProjectFacetAliasesExtensionPoint.getAliases( f ) )
{
this.facets.addKey( alias, f );
f.addAlias( alias );
}
}
private void readProjectFacetVersion( final IConfigurationElement config,
final Map<ProjectFacetVersion,IConfigurationElement> fvToConstraint,
final Map<ProjectFacetVersion,List<IConfigurationElement>> fvToActions )
{
final String fid = config.getAttribute( ATTR_FACET );
if( fid == null )
{
reportMissingAttribute( config, ATTR_FACET );
return;
}
final String ver = config.getAttribute( ATTR_VERSION );
if( ver == null )
{
reportMissingAttribute( config, ATTR_VERSION );
return;
}
final ProjectFacet f = (ProjectFacet) this.facets.getItemByKey( fid );
if( f == null )
{
ProblemLog.reportMissingFacet( fid, config.getContributor().getName() );
return;
}
final ProjectFacetVersion fv
= new ProjectFacetVersion();
fv.setProjectFacet( f );
fv.setVersionString( ver );
fv.setPluginId( config.getContributor().getName() );
final List<IConfigurationElement> actions = new ArrayList<IConfigurationElement>();
fvToActions.put( fv, actions );
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( EL_CONSTRAINT ) )
{
fvToConstraint.put( fv, child );
}
else if( childName.equals( EL_ACTION ) )
{
actions.add( child );
}
else if( childName.equals( EL_PROPERTY ) )
{
final String name = child.getAttribute( ATTR_NAME );
if( name == null )
{
reportMissingAttribute( child, ATTR_NAME );
continue;
}
final String value = child.getAttribute( ATTR_VALUE );
if( value == null )
{
reportMissingAttribute( child, ATTR_VALUE );
continue;
}
fv.setProperty( name, value );
}
}
f.addVersion( fv );
// This has to happen after facet version is registered.
for( int i = 0; i < children.length; i++ )
{
final IConfigurationElement child = children[ i ];
final String childName = child.getName();
if( childName.equals( EL_EVENT_HANDLER ) )
{
readEventHandler( child, f, ver );
}
}
}
/**
* Pre-computes the tables that describe how versions of a facet compare
* to each other. This allows the IProjectFacetVersion.compareTo() operation,
* which is called rather frequently, to be reduced to a hash table lookup
* instead of having to do a parse and comparison of two version strings.
*/
private void calculateVersionComparisonTables( final Map<ProjectFacetVersion,IConfigurationElement> fvToConstraint,
final Map<ProjectFacetVersion,List<IConfigurationElement>> fvToActions )
{
final List<IProjectFacet> badFacets = new ArrayList<IProjectFacet>();
for( IProjectFacet f : this.facets.getItemSet() )
{
try
{
final Comparator<String> comp = f.getVersionComparator();
final List<IProjectFacetVersion> versions
= new ArrayList<IProjectFacetVersion>( f.getVersions() );
final Map<IProjectFacetVersion,Map<IProjectFacetVersion,Integer>> compTables
= new HashMap<IProjectFacetVersion,Map<IProjectFacetVersion,Integer>>();
for( IProjectFacetVersion fv : versions )
{
compTables.put( fv, new HashMap<IProjectFacetVersion,Integer>() );
}
for( int i = 0, n = versions.size(); i < n; i++ )
{
final IProjectFacetVersion iVer = versions.get( i );
final String iVerStr = iVer.getVersionString();
final Map<IProjectFacetVersion,Integer> iCompTable = compTables.get( iVer );
for( int j = i + 1; j < n; j++ )
{
final IProjectFacetVersion jVer = versions.get( j );
final String jVerStr = jVer.getVersionString();
final Map<IProjectFacetVersion,Integer> jCompTable = compTables.get( jVer );
final int result = comp.compare( iVerStr, jVerStr );
iCompTable.put( jVer, new Integer( result ) );
jCompTable.put( iVer, new Integer( result * -1 ) );
}
}
for( Map.Entry<IProjectFacetVersion,Map<IProjectFacetVersion,Integer>> entry
: compTables.entrySet() )
{
final ProjectFacetVersion fv = (ProjectFacetVersion) entry.getKey();
fv.setComparisonTable( entry.getValue() );
}
}
catch( Exception e )
{
// The failure here is due to the problem loading the provided
// version comparator or due to the problem comparing the
// version string. In either case, we log the exception and
// remove all traces of this facet from the system to keep a
// faulty facet from dragging down the entire framework.
FacetCorePlugin.log( e );
badFacets.add( f );
}
}
for( IProjectFacet f : badFacets )
{
this.facets.removeItem( f );
final Category category = (Category) f.getCategory();
if( category != null )
{
category.removeProjectFacet( f );
}
for( IProjectFacetVersion fv : f.getVersions() )
{
fvToConstraint.remove( fv );
fvToActions.remove( fv );
}
}
}
private void readDefaultVersionInfo( final IConfigurationElement config )
{
final String id = config.getAttribute( ATTR_ID );
if( id == null || ! isProjectFacetDefined( id ) )
{
// The error should have already been reported, don't need to log
// this again.
return;
}
final ProjectFacet f = (ProjectFacet) getProjectFacet( id );
boolean defaultVersionSpecified = false;
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( EL_DEFAULT_VERSION ) )
{
final String clname = child.getAttribute( ATTR_PROVIDER );
if( clname != null )
{
final IDefaultVersionProvider defaultVersionProvider
= instantiate( f.getPluginId(), clname, IDefaultVersionProvider.class );
if( defaultVersionProvider != null )
{
f.setDefaultVersionProvider( defaultVersionProvider );
defaultVersionSpecified = true;
}
}
else
{
final String version = child.getAttribute( ATTR_VERSION );
if( version != null )
{
if( f.hasVersion( version ) )
{
f.setDefaultVersion( f.getVersion( version ) );
defaultVersionSpecified = true;
}
else
{
ProblemLog.reportMissingFacetVersion( f, version, config.getContributor().getName() );
}
}
else
{
reportMissingAttribute( config, ATTR_VERSION );
}
}
}
}
if( ! defaultVersionSpecified )
{
try
{
f.setDefaultVersion( f.getLatestVersion() );
}
catch( Exception e )
{
FacetCorePlugin.log( e );
}
}
}
private void readAction( final IConfigurationElement config )
{
final String fid = config.getAttribute( ATTR_FACET );
if( fid == null )
{
reportMissingAttribute( config, ATTR_FACET );
return;
}
final ProjectFacet f = (ProjectFacet) this.facets.getItemByKey( fid );
if( f == null )
{
ProblemLog.reportMissingFacet( fid, config.getContributor().getName() );
return;
}
String ver = config.getAttribute( ATTR_VERSION );
if( ver == null )
{
ver = IVersionExpr.WILDCARD_SYMBOL;
}
readAction( config, f, ver );
}
private void readAction( final IConfigurationElement config,
final ProjectFacet f,
final String version )
{
final String pluginId = config.getContributor().getName();
final ActionDefinition def = new ActionDefinition();
def.setPluginId( pluginId );
final String type = config.getAttribute( ATTR_TYPE );
if( type == null )
{
reportMissingAttribute( config, ATTR_TYPE );
return;
}
// Backwards compatibility of deprecated functionality.
if( type.equals( "runtime-changed" ) ) //$NON-NLS-1$
{
final String msg
= NLS.bind( Resources.deprecatedRuntimeChangedAction, pluginId );
FacetCorePlugin.logWarning( msg, true );
readEventHandler( config, f, version );
return;
}
// End of backwards compatibility code.
def.setActionType( Action.Type.valueOf( type ) );
if( def.getActionType() == null )
{
final String msg
= NLS.bind( Resources.invalidActionType, type ) +
NLS.bind( Resources.usedInPlugin, pluginId );
FacetCorePlugin.log( msg );
return;
}
try
{
def.setVersionExpr( new VersionExpr<ProjectFacetVersion>( f, version, pluginId ) );
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
return;
}
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( EL_CONFIG_FACTORY ) )
{
final String clname = child.getAttribute( ATTR_CLASS );
if( clname == null )
{
reportMissingAttribute( child, ATTR_CLASS );
return;
}
def.setConfigFactoryClassName( clname );
}
else if( childName.equals( EL_DELEGATE ) )
{
final String clname = child.getAttribute( ATTR_CLASS );
if( clname == null )
{
reportMissingAttribute( config, ATTR_CLASS );
return;
}
def.setDelegateClassName( clname );
}
else if( childName.equals( EL_PROPERTY ) )
{
final String name = child.getAttribute( ATTR_NAME );
if( name == null )
{
reportMissingAttribute( child, ATTR_NAME );
return;
}
final String value = child.getAttribute( ATTR_VALUE );
if( value == null )
{
reportMissingAttribute( child, ATTR_VALUE );
return;
}
Object parsedValue = value;
if( name.equals( IActionDefinition.PROP_FROM_VERSIONS ) )
{
try
{
parsedValue = new VersionExpr<ProjectFacetVersion>( f, value, pluginId );
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
return;
}
}
def.setProperty( name, parsedValue );
}
}
String id = config.getAttribute( ATTR_ID );
if( id == null )
{
final StringBuffer buf = new StringBuffer();
buf.append( f.getId() );
buf.append( '#' );
buf.append( version );
buf.append( '#' );
buf.append( def.getActionType().name() );
for( Map.Entry<String,Object> entry : def.getProperties().entrySet() )
{
buf.append( '#' );
buf.append( entry.getKey() );
buf.append( '=' );
buf.append( entry.getValue().toString() );
}
id = buf.toString();
}
def.setId( id );
if( isActionDefined( id ) )
{
final String msg
= NLS.bind( Resources.actionAlreadyDefined, id, pluginId );
FacetCorePlugin.logError( msg );
}
else
{
this.actions.addItemWithKey( def.getId(), def );
f.addActionDefinition( def );
}
}
private void readEventHandler( final IConfigurationElement config )
{
final String fid = config.getAttribute( ATTR_FACET );
if( fid == null )
{
reportMissingAttribute( config, ATTR_FACET );
return;
}
final ProjectFacet f = (ProjectFacet) this.facets.getItemByKey( fid );
if( f == null )
{
ProblemLog.reportMissingFacet( fid, config.getContributor().getName() );
return;
}
String ver = config.getAttribute( ATTR_VERSION );
if( ver == null )
{
ver = IVersionExpr.WILDCARD_SYMBOL;
}
readEventHandler( config, f, ver );
}
/**
* Support for a deprecated extension point.
*/
private void readEventHandler( final IConfigurationElement config,
final ProjectFacet f,
final String version )
{
final String pluginId = config.getContributor().getName();
final String type = config.getAttribute( ATTR_TYPE );
if( type == null )
{
reportMissingAttribute( config, ATTR_TYPE );
return;
}
final IFacetedProjectEvent.Type t;
if( type.equals( "runtime-changed" ) || type.equals( "RUNTIME_CHANGED" )) //$NON-NLS-1$ //$NON-NLS-2$
{
t = IFacetedProjectEvent.Type.PRIMARY_RUNTIME_CHANGED;
}
else
{
t = IFacetedProjectEvent.Type.valueOf( type );
}
if( t == null )
{
final String msg
= NLS.bind( Resources.invalidEventHandlerType, type ) +
NLS.bind( Resources.usedInPlugin, pluginId );
FacetCorePlugin.log( msg );
return;
}
final IVersionExpr vexpr;
try
{
vexpr = new VersionExpr<ProjectFacetVersion>( f, version, pluginId );
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
return;
}
String delegateClassName = null;
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( EL_DELEGATE ) )
{
final String clname = child.getAttribute( ATTR_CLASS );
if( clname == null )
{
reportMissingAttribute( config, ATTR_CLASS );
return;
}
delegateClassName = clname;
}
}
if( delegateClassName == null )
{
reportMissingElement( config, EL_DELEGATE );
return;
}
final LegacyEventHandlerAdapter adapter
= new LegacyEventHandlerAdapter( f, vexpr, pluginId, delegateClassName );
addListener( adapter, t );
}
private void readConstraint( final IConfigurationElement config,
final ProjectFacetVersion fv )
{
final ProblemLog.Policy problemLoggingPolicy
= ProblemLog.Policy.createBasedOnIgnoreProblemsAttr( config );
final IConfigurationElement[] ops = config.getChildren();
final List<IConstraint> parsed = new ArrayList<IConstraint>();
for( int j = 0; j < ops.length; j++ )
{
final IConstraint op = readConstraintHelper( ops[ j ], fv, problemLoggingPolicy );
if( op != null )
{
parsed.add( op );
}
}
if( parsed.size() == 1 )
{
fv.setConstraint( parsed.get( 0 ) );
}
else if( parsed.size() > 1 )
{
final IConstraint and
= new Constraint( fv, IConstraint.Type.AND,
parsed.toArray() );
fv.setConstraint( and );
}
}
private IConstraint readConstraintHelper( final IConfigurationElement root,
final ProjectFacetVersion fv,
final ProblemLog.Policy parentProblemLoggingPolicy )
{
final String pluginId = root.getContributor().getName();
final ProblemLog.Policy localProblemLoggingPolicy
= ProblemLog.Policy.createBasedOnIgnoreProblemsAttr( root, parentProblemLoggingPolicy );
final IConstraint.Type type
= IConstraint.Type.valueOf( root.getName() );
final Object[] operands;
if( type == IConstraint.Type.AND ||
type == IConstraint.Type.OR )
{
final IConfigurationElement[] children = root.getChildren();
final List<Object> operandsList = new ArrayList<Object>( children.length );
for( int i = 0; i < children.length; i++ )
{
final IConstraint operand
= readConstraintHelper( children[ i ], fv, localProblemLoggingPolicy );
if( operand != null )
{
operandsList.add( operand );
}
else
{
if( type == IConstraint.Type.AND )
{
return null;
}
}
}
operands = operandsList.toArray();
}
else if( type == IConstraint.Type.REQUIRES ||
type == IConstraint.Type.CONFLICTS )
{
final String gid = root.getAttribute( ATTR_GROUP );
final String fid = root.getAttribute( ATTR_FACET );
String vexprstr = root.getAttribute( ATTR_VERSION );
final String softStr = root.getAttribute( ATTR_SOFT );
Boolean soft = Boolean.FALSE;
if( softStr != null && softStr.equals( Boolean.TRUE.toString() ) )
{
soft = Boolean.TRUE;
}
if( gid != null && ( fid != null || vexprstr != null ) )
{
final String template
= type == IConstraint.Type.REQUIRES
? Resources.invalidRequiresConstraint
: Resources.invalidConflictsConstraint;
final String msg = NLS.bind( template, pluginId );
FacetCorePlugin.logError( msg, true );
return null;
}
else if( gid != null )
{
if( ! isGroupDefined( gid ) )
{
ProblemLog.reportMissingGroup( gid, pluginId, localProblemLoggingPolicy );
return null;
}
final IGroup group = getGroup( gid );
if( type == IConstraint.Type.REQUIRES )
{
operands = new Object[] { group, soft };
}
else
{
operands = new Object[] { group };
}
}
else if( fid != null )
{
if( ! isProjectFacetDefined( fid ) )
{
ProblemLog.reportMissingFacet( fid, pluginId, localProblemLoggingPolicy );
return null;
}
final IProjectFacet f = getProjectFacet( fid );
VersionExpr vexpr = null;
try
{
if( vexprstr == null || vexprstr.trim().length() == 0 )
{
vexprstr = IVersionExpr.WILDCARD_SYMBOL;
}
vexpr = new VersionExpr( f, vexprstr, pluginId );
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
return null;
}
if( type == IConstraint.Type.REQUIRES )
{
operands = new Object[] { f, vexpr, soft };
}
else
{
operands = new Object[] { f, vexpr };
}
}
else
{
final String msg
= Resources.bind( Resources.missingOneOfTwoAttributes,
pluginId, root.getName(), ATTR_GROUP,
ATTR_FACET );
FacetCorePlugin.logError( msg, true );
return null;
}
}
else
{
throw new IllegalStateException();
}
return new Constraint( fv, type, operands );
}
private static String toString( final Collection<? extends Object> collection )
{
final StringBuffer buf = new StringBuffer();
for( Object obj : collection )
{
if( buf.length() > 0 )
{
buf.append( ", " ); //$NON-NLS-1$
}
if( obj instanceof IProjectFacetVersion )
{
final IProjectFacetVersion fv = (IProjectFacetVersion) obj;
buf.append( fv.getProjectFacet().getId() );
buf.append( ' ' );
buf.append( fv.getVersionString() );
}
else
{
buf.append( obj.toString() );
}
}
return buf.toString();
}
private final class ResourceChangeListener
implements IResourceChangeListener
{
public void register()
{
final IWorkspace ws = ResourcesPlugin.getWorkspace();
ws.addResourceChangeListener( this, IResourceChangeEvent.POST_CHANGE );
}
public void resourceChanged( final IResourceChangeEvent event )
{
final IResourceDelta delta = event.getDelta();
final List<FacetedProject> projectsToRefresh = new ArrayList<FacetedProject>();
synchronized( FacetedProjectFrameworkImpl.this.projects )
{
for( IResourceDelta subdelta : delta.getAffectedChildren( IResourceDelta.REMOVED ) )
{
final String pjname = subdelta.getFullPath().segment( 0 );
final IFacetedProject fpj
= FacetedProjectFrameworkImpl.this.projects.remove( pjname );
if( fpj != null )
{
try
{
( (FacetedProject) fpj ).markDeleted();
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
}
}
}
for( FacetedProject fproj : FacetedProjectFrameworkImpl.this.projects.values() )
{
final IResourceDelta subdelta = delta.findMember( fproj.f.getFullPath() );
if( subdelta != null )
{
projectsToRefresh.add( fproj );
}
}
}
for( FacetedProject fproj : projectsToRefresh )
{
try
{
fproj.refresh();
}
catch( CoreException e )
{
FacetCorePlugin.log( e );
}
}
}
}
public static final class Resources
extends NLS
{
public static String missingOneOfTwoAttributes;
public static String categoryNotDefined;
public static String facetNotDefined;
public static String facetVersionNotDefined;
public static String actionNotDefined;
public static String actionAlreadyDefined;
public static String groupNotDefined;
public static String presetNotDefined;
public static String templateNotDefined;
public static String usedInPlugin;
public static String invalidActionType;
public static String invalidEventHandlerType;
public static String invalidRequiresConstraint;
public static String invalidConflictsConstraint;
public static String deprecatedRuntimeChangedAction;
public static String tracingActionSorting;
public static String tracingFrameworkActivationStarting;
public static String tracingFrameworkActivationFinished;
static
{
initializeMessages( FacetedProjectFrameworkImpl.class.getName(),
Resources.class );
}
public static String bind( final String template,
final Object arg1,
final Object arg2,
final Object arg3 )
{
return NLS.bind( template, new Object[] { arg1, arg2, arg3 } );
}
public static String bind( final String template,
final Object arg1,
final Object arg2,
final Object arg3,
final Object arg4 )
{
return NLS.bind( template, new Object[] { arg1, arg2, arg3, arg4 } );
}
}
}