blob: 5b72161759ef0bce2cc45524cc73702f3e66d762 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2010 Oracle
* All rights reserved. This program and the accompanying materials
* are made available under the terms 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 implementation and ongoing maintenance
******************************************************************************/
package org.eclipse.jst.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.ListIterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.wst.common.project.facet.core.IProjectFacet;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
@SuppressWarnings( "restriction" )
public final class ClasspathUtil
{
public static final String LEGACY_METADATA_FILE_NAME
= ".settings/org.eclipse.jst.common.project.facet.core.prefs"; //$NON-NLS-1$
public static final Object SYSTEM_OWNER = new Object();
private static final String OWNER_PROJECT_FACETS_ATTR = "owner.project.facets"; //$NON-NLS-1$
private ClasspathUtil() {}
public static List<IClasspathEntry> getProjectClasspath( final IJavaProject project )
throws CoreException
{
final List<IClasspathEntry> result = new ArrayList<IClasspathEntry>();
for( IClasspathEntry cpe : project.getRawClasspath() )
{
result.add( cpe );
}
return result;
}
public static void setProjectClasspath( final IJavaProject project,
final List<IClasspathEntry> cp )
throws CoreException
{
validateClasspathEdit( project );
project.setRawClasspath( cp.toArray( new IClasspathEntry[ cp.size() ] ), null );
}
public static void addClasspathEntry( final IProject project,
final IClasspathEntry cpe )
throws CoreException
{
addClasspathEntry( JavaCore.create( project ), cpe );
}
public static void addClasspathEntry( final IJavaProject project,
final IClasspathEntry cpe )
throws CoreException
{
validateClasspathEdit( project );
final IClasspathEntry[] cpOld = project.getRawClasspath();
final IClasspathEntry[] cpNew = new IClasspathEntry[ cpOld.length + 1 ];
System.arraycopy( cpOld, 0, cpNew, 0, cpOld.length );
cpNew[ cpOld.length ] = cpe;
project.setRawClasspath( cpNew, null );
}
public static List<IClasspathEntry> getClasspathEntries( final IProject project,
final IProjectFacet facet )
throws CoreException
{
final IJavaProject jproj = JavaCore.create( project );
return getClasspathEntries( jproj, facet );
}
public static List<IClasspathEntry> getClasspathEntries( final IJavaProject project,
final IProjectFacet facet )
throws CoreException
{
final List<IClasspathEntry> result = new ArrayList<IClasspathEntry>();
for( IClasspathEntry cpe : project.getRawClasspath() )
{
final Set<Object> owners = getOwners( project, cpe );
if( owners.contains( facet ) )
{
result.add( cpe );
}
}
return result;
}
public static void addClasspathEntries( final IProject project,
final IProjectFacet facet,
final List<IClasspathEntry> cpentries )
throws CoreException
{
final IJavaProject jproj = JavaCore.create( project );
addClasspathEntries( jproj, facet, cpentries );
}
public static void addClasspathEntries( final IJavaProject project,
final IProjectFacet facet,
final List<IClasspathEntry> cpentries )
throws CoreException
{
validateClasspathEdit( project );
convertLegacyMetadata( project );
final List<IClasspathEntry> cp = getProjectClasspath( project );
for( IClasspathEntry cpe : cpentries )
{
IClasspathEntry existingClasspathEntry = null;
for( IClasspathEntry x : cp )
{
if( x.getPath().equals( cpe.getPath() ) )
{
existingClasspathEntry = x;
break;
}
}
final Set<Object> owners = getOwners( project, existingClasspathEntry );
owners.add( facet );
if( existingClasspathEntry != null )
{
final IClasspathEntry annotatedEntry = setOwners( existingClasspathEntry, owners );
final int existingIndex = cp.indexOf( existingClasspathEntry );
cp.set( existingIndex, annotatedEntry );
}
else
{
final IClasspathEntry annotatedEntry = setOwners( cpe, owners );
cp.add( annotatedEntry );
}
}
setProjectClasspath( project, cp );
}
public static void removeClasspathEntries( final IProject project,
final IProjectFacet facet )
throws CoreException
{
final IJavaProject jproj = JavaCore.create( project );
removeClasspathEntries( jproj, facet );
}
public static void removeClasspathEntries( final IJavaProject project,
final IProjectFacet facet )
throws CoreException
{
validateClasspathEdit( project );
convertLegacyMetadata( project );
final List<IClasspathEntry> cp = getProjectClasspath( project );
boolean cpchanged = false;
for( ListIterator<IClasspathEntry> itr = cp.listIterator(); itr.hasNext(); )
{
final IClasspathEntry cpe = itr.next();
final Set<Object> owners = getOwners( project, cpe );
if( owners.remove( facet ) )
{
if( owners.size() == 0 )
{
itr.remove();
}
else
{
itr.set( setOwners( cpe, owners ) );
}
cpchanged = true;
}
}
if( cpchanged )
{
setProjectClasspath( project, cp );
}
}
public static void removeClasspathEntries( final IProject project,
final IProjectFacet facet,
final List<IClasspathEntry> cpentries )
throws CoreException
{
final IJavaProject jproj = JavaCore.create( project );
removeClasspathEntries( jproj, facet, cpentries );
}
public static void removeClasspathEntries( final IJavaProject project,
final IProjectFacet facet,
final List<IClasspathEntry> cpentries )
throws CoreException
{
validateClasspathEdit( project );
convertLegacyMetadata( project );
final List<IClasspathEntry> cp = getProjectClasspath( project );
boolean cpchanged = false;
for( ListIterator<IClasspathEntry> itr = cp.listIterator(); itr.hasNext(); )
{
final IClasspathEntry cpe = itr.next();
if( cpentries.contains( cpe ) )
{
final Set<Object> owners = getOwners( project, cpe );
if( owners.remove( facet ) )
{
if( owners.size() == 0 )
{
itr.remove();
}
else
{
itr.set( setOwners( cpe, owners ) );
}
cpchanged = true;
}
}
}
if( cpchanged )
{
setProjectClasspath( project, cp );
}
}
private static Set<Object> getOwners( final IJavaProject project,
final IClasspathEntry cpe )
throws CoreException
{
final Set<Object> owners = new HashSet<Object>();
if( cpe != null )
{
for( IClasspathAttribute attr : cpe.getExtraAttributes() )
{
if( attr.getName().equals( OWNER_PROJECT_FACETS_ATTR ) )
{
owners.addAll( decodeOwnersString( attr.getValue() ) );
break;
}
}
owners.addAll( getOwnersFromLegacyMetadata( project, cpe ) );
if( owners.isEmpty() )
{
owners.add( SYSTEM_OWNER );
}
}
return owners;
}
private static Set<Object> getOwnersFromLegacyMetadata( final IJavaProject project,
final IClasspathEntry cpe )
throws CoreException
{
final IProject pj = project.getProject();
final IFile legacyMetadataFile = pj.getFile( LEGACY_METADATA_FILE_NAME );
if( legacyMetadataFile.exists() )
{
final ProjectScope scope = new ProjectScope( pj );
final IEclipsePreferences pluginRoot = scope.getNode( FacetCorePlugin.PLUGIN_ID );
final Preferences root = pluginRoot.node( "classpath.helper" ); //$NON-NLS-1$
final String[] keys;
try
{
keys = root.childrenNames();
}
catch( BackingStoreException e )
{
throw new CoreException( FacetCorePlugin.createErrorStatus( e.getMessage(), e ) );
}
for( String key : keys )
{
final Preferences node = root.node( key );
final String owners = node.get( "owners", null ); //$NON-NLS-1$
if( owners != null )
{
final IPath path = new Path( key.replaceAll( "::", "/" ) ); //$NON-NLS-1$ //$NON-NLS-2$
if( cpe.getPath().equals( path ) )
{
return decodeOwnersString( owners );
}
}
}
}
return Collections.emptySet();
}
private static IClasspathEntry setOwners( final IClasspathEntry cpe,
final Set<Object> owners )
{
if( owners.size() == 1 && owners.iterator().next() == SYSTEM_OWNER )
{
owners.clear();
}
final String ownersString = ( owners.size() == 0 ? null : encodeOwnersString( owners ) );
return setOwners( cpe, ownersString );
}
private static IClasspathEntry setOwners( final IClasspathEntry cpe,
final String owners )
{
final List<IClasspathAttribute> attrs = new ArrayList<IClasspathAttribute>();
for( IClasspathAttribute attr : cpe.getExtraAttributes() )
{
if( ! attr.getName().equals( OWNER_PROJECT_FACETS_ATTR ) )
{
attrs.add( attr );
}
}
if( owners != null )
{
attrs.add( JavaCore.newClasspathAttribute( OWNER_PROJECT_FACETS_ATTR, owners ) );
}
return new ClasspathEntry( cpe.getContentKind(), cpe.getEntryKind(), cpe.getPath(),
cpe.getInclusionPatterns(), cpe.getExclusionPatterns(),
cpe.getSourceAttachmentPath(), cpe.getSourceAttachmentRootPath(),
cpe.getOutputLocation(), cpe.isExported(), cpe.getAccessRules(),
cpe.combineAccessRules(),
attrs.toArray( new IClasspathAttribute[ attrs.size() ] ) );
}
private static String encodeOwnersString( final Set<Object> owners )
{
final StringBuilder buf = new StringBuilder();
for( Object owner : owners )
{
if( buf.length() > 0 )
{
buf.append( ';' );
}
if( owner == SYSTEM_OWNER )
{
buf.append( "#system#" ); //$NON-NLS-1$
}
else
{
final IProjectFacet facet = (IProjectFacet) owner;
buf.append( facet.getId() );
}
}
return buf.toString();
}
private static Set<Object> decodeOwnersString( final String str )
{
final Set<Object> owners = new HashSet<Object>();
final String[] split = str.split( ";" ); //$NON-NLS-1$
for( int j = 0; j < split.length; j++ )
{
final String segment = split[ j ];
if( segment.equals( "#system#" ) ) //$NON-NLS-1$
{
owners.add( SYSTEM_OWNER );
}
else
{
String facetId = segment;
final int colon = facetId.indexOf( ':' );
if( colon != -1 )
{
facetId = facetId.substring( 0, colon );
}
owners.add( ProjectFacetsManager.getProjectFacet( facetId ) );
}
}
return owners;
}
private static void convertLegacyMetadata( final IJavaProject project )
throws CoreException
{
final IProject pj = project.getProject();
final IFile legacyMetadataFile = pj.getFile( LEGACY_METADATA_FILE_NAME );
if( legacyMetadataFile.exists() )
{
final ProjectScope scope = new ProjectScope( pj );
final IEclipsePreferences pluginRoot = scope.getNode( FacetCorePlugin.PLUGIN_ID );
final Preferences root = pluginRoot.node( "classpath.helper" ); //$NON-NLS-1$
final Map<IPath,String> metadata = new HashMap<IPath,String>();
final String[] keys;
try
{
keys = root.childrenNames();
}
catch( BackingStoreException e )
{
throw new CoreException( FacetCorePlugin.createErrorStatus( e.getMessage(), e ) );
}
for( String key : keys )
{
final Preferences node = root.node( key );
final String owners = node.get( "owners", null ); //$NON-NLS-1$
if( owners != null )
{
metadata.put( new Path( key.replaceAll( "::", "/" ) ), owners ); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if( ! metadata.isEmpty() )
{
final List<IClasspathEntry> cp = getProjectClasspath( project );
boolean cpchanged = false;
for( ListIterator<IClasspathEntry> itr = cp.listIterator(); itr.hasNext(); )
{
final IClasspathEntry cpe = itr.next();
final String ownersString = metadata.get( cpe.getPath() );
if( ownersString != null )
{
final Set<Object> owners = decodeOwnersString( ownersString );
itr.set( setOwners( cpe, encodeOwnersString( owners ) ) );
cpchanged = true;
}
}
if( cpchanged )
{
setProjectClasspath( project, cp );
}
}
legacyMetadataFile.delete( true, null );
}
}
private static void validateClasspathEdit( final IJavaProject jproject )
throws CoreException
{
final IWorkspace ws = ResourcesPlugin.getWorkspace();
final IProject project = jproject.getProject();
final IStatus st = ws.validateEdit( new IFile[] { project.getFile( ".classpath" ) }, IWorkspace.VALIDATE_PROMPT ); //$NON-NLS-1$
if( st.getSeverity() == IStatus.ERROR )
{
throw new CoreException( st );
}
}
}