Workspace repository now filters exports that project doesn't actually contain in "compile time" resolution mode (FELIX-1502)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@917993 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/common/core/src/org/apache/felix/sigil/repository/ResolutionConfig.java b/sigil/common/core/src/org/apache/felix/sigil/repository/ResolutionConfig.java
index 54571b5..1ffe3ac 100644
--- a/sigil/common/core/src/org/apache/felix/sigil/repository/ResolutionConfig.java
+++ b/sigil/common/core/src/org/apache/felix/sigil/repository/ResolutionConfig.java
@@ -31,6 +31,7 @@
     public static final int INDEXED_ONLY = 8;
     /** Return only bundles that are stored or cached locally */
     public static final int LOCAL_ONLY = 16;
+    public static final int COMPILE_TIME = 32;
 
 
     public ResolutionConfig()
@@ -74,4 +75,10 @@
         // TODO Auto-generated method stub
         return false;
     }
+
+
+    public boolean isCompileTime()
+    {
+        return ( options & COMPILE_TIME ) != 0;
+    }
 }
diff --git a/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepository.java b/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepository.java
index 7d290ad..80df5df 100644
--- a/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepository.java
+++ b/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepository.java
@@ -20,145 +20,140 @@
 package org.apache.felix.sigil.eclipse.internal.repository.eclipse;
 
 
-import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 
 import org.apache.felix.sigil.eclipse.SigilCore;
 import org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel;
 import org.apache.felix.sigil.model.eclipse.ISigilBundle;
+import org.apache.felix.sigil.model.osgi.IPackageExport;
 import org.apache.felix.sigil.repository.AbstractBundleRepository;
 import org.apache.felix.sigil.repository.IRepositoryVisitor;
+import org.apache.felix.sigil.repository.ResolutionConfig;
+import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceChangeEvent;
 import org.eclipse.core.resources.IResourceChangeListener;
 import org.eclipse.core.resources.IResourceDelta;
 import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.runtime.CoreException;
-
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.content.IContentTypeManager;
 
 public class WorkspaceRepository extends AbstractBundleRepository implements IResourceChangeListener
 {
 
     private static final int UPDATE_MASK = IResourceDelta.CONTENT | IResourceDelta.DESCRIPTION | IResourceDelta.OPEN;
-    private ISigilBundle[] bundles;
-
 
     public WorkspaceRepository( String id )
     {
         super( id );
-    }
-
-
+    }    
+      
+    
     @Override
     public void accept( IRepositoryVisitor visitor, int options )
     {
-        synchronized ( this )
+        List<ISigilProjectModel> models = SigilCore.getRoot().getProjects();
+        for ( ISigilProjectModel project : models )
         {
-            if ( bundles == null )
+            ISigilBundle b = project.getBundle();
+            if ( b == null )
             {
-                List<ISigilProjectModel> models = SigilCore.getRoot().getProjects();
-                ArrayList<ISigilBundle> tmp = new ArrayList<ISigilBundle>( models.size() );
-                for ( ISigilProjectModel n : models )
-                {
-                    ISigilBundle b = n.getBundle();
-                    if ( b == null )
-                    {
-                        SigilCore.error( "No bundle found for project " + n.getProject().getName() );
-                    }
-                    else
-                    {
-                        tmp.add( b );
-                    }
+                SigilCore.error( "No bundle found for project " + project.getProject().getName() );
+            }
+            else
+            {
+                if ( (options & ResolutionConfig.COMPILE_TIME) != 0 ) {
+                    b = compileTimeFilter(project, b);
                 }
-                bundles = tmp.toArray( new ISigilBundle[tmp.size()] );
+                visitor.visit( b );
             }
         }
-
-        for ( ISigilBundle b : bundles )
-        {
-            visitor.visit( b );
-        }
     }
 
 
+    private ISigilBundle compileTimeFilter(ISigilProjectModel project, ISigilBundle bundle)
+    {
+        bundle = (ISigilBundle) bundle.clone();
+
+        Collection<String> packages = findPackages(project);
+        
+        for ( IPackageExport pe : bundle.getBundleInfo().getExports() ) {
+            final String packagePath = pe.getPackageName().replace('.', '/');
+            if ( !packages.contains(packagePath) ) {
+                bundle.getBundleInfo().removeExport(pe);
+            }
+        }
+        
+        return bundle;
+    }
+
+
+
+    private Collection<String> findPackages(ISigilProjectModel project)
+    {
+        final IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
+        final IContentType javaContentType = contentTypeManager.getContentType("org.eclipse.jdt.core.javaSource");
+        final HashSet<String> packages = new HashSet<String>();
+        
+        try
+        {
+            project.getProject().accept(new IResourceVisitor()
+            {
+                public boolean visit(IResource resource) throws CoreException
+                {
+                    if ( resource instanceof IFile ) {
+                        IFile f = (IFile) resource;
+                        IContentType ct = contentTypeManager.findContentTypeFor(f.getName());
+                        if ( ct != null && ct.isKindOf(javaContentType) ) {
+                            IPath p = f.getProjectRelativePath();
+                            p = p.removeLastSegments(1);
+                            p = p.removeFirstSegments(1);
+                            packages.add( p.toString() );
+                        }
+                    }
+                    
+                    return true;
+                }
+            });
+        }
+        catch (CoreException e)
+        {
+            SigilCore.error( "Failed to read packages for " + project.getProject().getName() );
+        }
+        
+        return packages;
+     }
+
+
+
     public void refresh()
     {
-        synchronized ( this )
-        {
-            bundles = null;
-        }
+        // no action
+        // TODO this method is used to prompt repository to update caches - 
+        // however caches are complex to maintain in this workspace as the bundle is actively being developed...
+        // potential performance improvement in future?
     }
 
-
-    @Override
-    protected void notifyChange()
-    {
-        refresh();
-        super.notifyChange();
-    }
-
-
     public void resourceChanged( IResourceChangeEvent event )
     {
         try
         {
-            event.getDelta().accept( new IResourceDeltaVisitor()
-            {
-                public boolean visit( IResourceDelta delta ) throws CoreException
-                {
-                    boolean result;
-
-                    IResource resource = delta.getResource();
-                    if ( resource instanceof IWorkspaceRoot )
-                    {
-                        result = true;
-                    }
-                    else if ( resource instanceof IProject )
-                    {
-                        IProject project = ( IProject ) resource;
-                        if ( SigilCore.isSigilProject( project ) )
-                        {
-                            switch ( delta.getKind() )
-                            {
-                                case IResourceDelta.CHANGED:
-                                    if ( ( delta.getFlags() & UPDATE_MASK ) == 0 )
-                                    {
-                                        break;
-                                    }
-                                    // else 
-                                    // fall through on purpose
-                                case IResourceDelta.ADDED: // fall through on purpose
-                                case IResourceDelta.REMOVED: // fall through on purpose
-                                    notifyChange();
-                                    break;
-                            }
-                            result = true;
-                        }
-                        else
-                        {
-                            result = false;
-                        }
-                    }
-                    else if ( resource.getName().equals( SigilCore.SIGIL_PROJECT_FILE ) )
-                    {
-                        switch ( delta.getKind() )
-                        {
-                            case IResourceDelta.CHANGED:
-                            case IResourceDelta.ADDED:
-                            case IResourceDelta.REMOVED:
-                                notifyChange();
-                        }
-                        result = false;
-                    }
-                    else
-                    {
-                        result = false;
-                    }
-                    return result;
-                }
-            } );
+            switch (event.getType()) {
+                case IResourceChangeEvent.PRE_REFRESH:
+                    handleRefresh(event);
+                    break;
+                case IResourceChangeEvent.POST_CHANGE:
+                    handleChange(event);
+                    break;
+            }
         }
         catch ( CoreException e )
         {
@@ -166,4 +161,71 @@
         }
     }
 
+
+    private void handleRefresh(IResourceChangeEvent event)
+    {
+        SigilCore.log("Refreshing workspace repository");
+        notifyChange();
+    }
+
+
+    private void handleChange(IResourceChangeEvent event) throws CoreException
+    {
+        event.getDelta().accept( new IResourceDeltaVisitor()
+        {
+            public boolean visit( IResourceDelta delta ) throws CoreException
+            {
+                boolean result;
+
+                IResource resource = delta.getResource();
+                if ( resource instanceof IWorkspaceRoot )
+                {
+                    result = true;
+                }
+                else if ( resource instanceof IProject )
+                {
+                    IProject project = ( IProject ) resource;
+                    if ( SigilCore.isSigilProject( project ) )
+                    {
+                        switch ( delta.getKind() )
+                        {
+                            case IResourceDelta.CHANGED:
+                                if ( ( delta.getFlags() & UPDATE_MASK ) == 0 )
+                                {
+                                    break;
+                                }
+                                // else 
+                                // fall through on purpose
+                            case IResourceDelta.ADDED: // fall through on purpose
+                            case IResourceDelta.REMOVED: // fall through on purpose
+                                notifyChange();
+                                break;
+                        }
+                        result = true;
+                    }
+                    else
+                    {
+                        result = false;
+                    }
+                }
+                else if ( resource.getName().equals( SigilCore.SIGIL_PROJECT_FILE ) )
+                {
+                    switch ( delta.getKind() )
+                    {
+                        case IResourceDelta.CHANGED:
+                        case IResourceDelta.ADDED:
+                        case IResourceDelta.REMOVED:
+                            notifyChange();
+                    }
+                    result = false;
+                }
+                else
+                {
+                    result = false;
+                }
+                return result;
+            }
+        } );
+    }
+
 }
diff --git a/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepositoryProvider.java b/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepositoryProvider.java
index d03bef4..140fe5a 100644
--- a/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepositoryProvider.java
+++ b/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/internal/repository/eclipse/WorkspaceRepositoryProvider.java
@@ -44,7 +44,7 @@
         if ( repository == null )
         {
             repository = new WorkspaceRepository( id );
-            ResourcesPlugin.getWorkspace().addResourceChangeListener( repository, IResourceChangeEvent.POST_CHANGE );
+            ResourcesPlugin.getWorkspace().addResourceChangeListener( repository, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_REFRESH );
         }
         return repository;
     }
diff --git a/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/model/util/JavaHelper.java b/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/model/util/JavaHelper.java
index 86ca1e9..23b4a73 100644
--- a/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/model/util/JavaHelper.java
+++ b/sigil/eclipse/core/src/org/apache/felix/sigil/eclipse/model/util/JavaHelper.java
@@ -363,7 +363,7 @@
         ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
 
         ResolutionConfig config = new ResolutionConfig( ResolutionConfig.INCLUDE_OPTIONAL
-            | ResolutionConfig.IGNORE_ERRORS | ResolutionConfig.INDEXED_ONLY | ResolutionConfig.LOCAL_ONLY );
+            | ResolutionConfig.IGNORE_ERRORS | ResolutionConfig.INDEXED_ONLY | ResolutionConfig.LOCAL_ONLY | ResolutionConfig.COMPILE_TIME );
 
         IResolution resolution;
         try
@@ -410,6 +410,7 @@
         Set<ISigilBundle> all, List<IModelElement> requirements, IProgressMonitor monitor ) throws CoreException
     {
         IAccessRule[] rules = buildAccessRules( project, provider, all, requirements );
+        IClasspathAttribute[] attrs = new IClasspathAttribute[0];
 
         ISigilProjectModel other = provider.getAncestor( ISigilProjectModel.class );
 
@@ -418,11 +419,11 @@
             if ( other == null )
             {
                 provider.synchronize( monitor );
-                return newBundleEntry( provider, rules, null, false );
+                return newBundleEntry( provider, rules, attrs, false );
             }
             else
             {
-                return newProjectEntry( other, rules, null, false );
+                return newProjectEntry( other, rules, attrs, false );
             }
         }
         catch ( IOException e )
@@ -432,62 +433,9 @@
     }
 
 
-    private static IAccessRule[] buildExportRules( ISigilBundle bundle, Set<ISigilBundle> all,
-        List<IModelElement> requirements )
-    {
-        Set<IPackageExport> ex = mergeExports( bundle, all, requirements );
-
-        IAccessRule[] rules = new IAccessRule[ex.size() + 1];
-
-        Iterator<IPackageExport> iter = ex.iterator();
-        for ( int i = 0; i < rules.length - 1; i++ )
-        {
-            IPackageExport p = iter.next();
-            rules[i] = JavaCore.newAccessRule( new Path( p.getPackageName().replace( '.', '/' ) ).append( "*" ),
-                IAccessRule.K_ACCESSIBLE );
-        }
-
-        rules[rules.length - 1] = DENY_RULE;
-
-        return rules;
-    }
-
-
-    private static Set<IPackageExport> mergeExports( ISigilBundle bundle, Set<ISigilBundle> all,
-        List<IModelElement> requirements )
-    {
-        IBundleModelElement headers = bundle.getBundleInfo();
-        // FIXME treeset as PackageExport does not implement equals/hashCode
-        TreeSet<IPackageExport> exports = new TreeSet<IPackageExport>( headers.getExports() );
-        IRequiredBundle host = headers.getFragmentHost();
-        if ( host != null )
-        {
-            for ( ISigilBundle b : all )
-            {
-                if ( host.accepts( b.getBundleInfo() ) )
-                {
-                    exports.addAll( b.getBundleInfo().getExports() );
-                    break;
-                }
-            }
-        }
-        return exports;
-    }
-
-
     private static Collection<IClasspathEntry> newProjectEntry( ISigilProjectModel n, IAccessRule[] rules,
         IClasspathAttribute[] attributes, boolean export ) throws CoreException
     {
-        //		if (rules == null) {
-        //			rules = JavaHelper.buildExportRules(n.getBundle());
-        //		}
-
-        if ( attributes == null )
-        {
-            attributes = new IClasspathAttribute[]
-                {};
-        }
-
         ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
         entries.add( JavaCore.newProjectEntry( n.getProject().getFullPath(), rules, false, attributes, export ) );
         for ( IClasspathEntry e : n.getJavaModel().getRawClasspath() )
@@ -518,16 +466,6 @@
     {
         String name = bundle.getBundleInfo().getSymbolicName();
 
-        //		if (rules == null) {
-        //			rules = JavaHelper.buildExportRules(bundle);
-        //		}
-
-        if ( attributes == null )
-        {
-            attributes = new IClasspathAttribute[]
-                {};
-        }
-
         if ( bundle.getBundleInfo().getVersion() != null )
         {
             name += "_version_" + bundle.getBundleInfo().getVersion();
@@ -547,7 +485,7 @@
 
                 if ( path == null )
                 {
-                    SigilCore.error( "Found null path for " + bundle.getBundleInfo().getSymbolicName() );
+                    SigilCore.error( "Found null path for " + bundle.getSymbolicName() );
                     entries = Collections.emptyList();
                 }
                 else
@@ -820,6 +758,48 @@
 
         return rules.toArray( new IAccessRule[rules.size()] );
     }
+    
+    private static IAccessRule[] buildExportRules( ISigilBundle bundle, Set<ISigilBundle> all,
+        List<IModelElement> requirements )
+    {
+        Set<IPackageExport> ex = mergeExports( bundle, all, requirements );
+
+        IAccessRule[] rules = new IAccessRule[ex.size() + 1];
+
+        Iterator<IPackageExport> iter = ex.iterator();
+        for ( int i = 0; i < rules.length - 1; i++ )
+        {
+            IPackageExport p = iter.next();
+            rules[i] = JavaCore.newAccessRule( new Path( p.getPackageName().replace( '.', '/' ) ).append( "*" ),
+                IAccessRule.K_ACCESSIBLE );
+        }
+
+        rules[rules.length - 1] = DENY_RULE;
+
+        return rules;
+    }
+
+
+    private static Set<IPackageExport> mergeExports( ISigilBundle bundle, Set<ISigilBundle> all,
+        List<IModelElement> requirements )
+    {
+        IBundleModelElement headers = bundle.getBundleInfo();
+        // FIXME treeset as PackageExport does not implement equals/hashCode
+        TreeSet<IPackageExport> exports = new TreeSet<IPackageExport>( headers.getExports() );
+        IRequiredBundle host = headers.getFragmentHost();
+        if ( host != null )
+        {
+            for ( ISigilBundle b : all )
+            {
+                if ( host.accepts( b.getBundleInfo() ) )
+                {
+                    exports.addAll( b.getBundleInfo().getExports() );
+                    break;
+                }
+            }
+        }
+        return exports;
+    }    
 
 
     /*