add support for exported package refactoring (FELIX-2481)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@966522 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/eclipse/ui/plugin.properties b/sigil/eclipse/ui/plugin.properties
index 3e6d20d..532db80 100644
--- a/sigil/eclipse/ui/plugin.properties
+++ b/sigil/eclipse/ui/plugin.properties
@@ -2,4 +2,5 @@
 newtonRepoPrefs=Newton Repository
 librariesPrefsPage=OSGi Libraries
 commandConvertProject=Convert Project To Sigil Project
-commandRefreshClasspath=Refresh bundle classpath
\ No newline at end of file
+commandRefreshClasspath=Refresh bundle classpath
+RenamePackageParticipant.name=Rename Package Participant
\ No newline at end of file
diff --git a/sigil/eclipse/ui/plugin.xml b/sigil/eclipse/ui/plugin.xml
index f0cf1a8..1a76b9f 100644
--- a/sigil/eclipse/ui/plugin.xml
+++ b/sigil/eclipse/ui/plugin.xml
@@ -264,5 +264,22 @@
             </with>
          </activeWhen>
       </handler>      
-   </extension>   
+   </extension>
+   <extension point="org.eclipse.ltk.core.refactoring.renameParticipants">
+     <renameParticipant
+        id="org.apache.felix.sigil.renamePackageParticipant"
+        name="%RenamePackageParticipant.name" 
+        class="org.apache.felix.sigil.ui.eclipse.refactor.RenamePackageParticipant">
+       <enablement>
+         <with variable="affectedNatures">
+           <iterate operator="or">
+             <equals value="org.apache.felix.sigil.sigilnature"/>
+           </iterate>
+         </with>
+         <with variable="element">
+           <instanceof value="org.eclipse.jdt.core.IPackageFragment"/>
+         </with>
+       </enablement>
+     </renameParticipant>
+   </extension>  
 </plugin>
diff --git a/sigil/eclipse/ui/sigil.properties b/sigil/eclipse/ui/sigil.properties
index acfc5b8..bcce8bd 100644
--- a/sigil/eclipse/ui/sigil.properties
+++ b/sigil/eclipse/ui/sigil.properties
@@ -9,9 +9,9 @@
 	org.apache.felix.sigil.eclipse.ui, \
 
 -resources: \
+	icons=icons, \
 	plugin.properties, \
 	plugin.xml, \
-    icons=icons,\
 	schema, \
 	schema/org.apache.felix.sigil.ui.repositorywizard.exsd, \
 
@@ -48,6 +48,7 @@
 	org.eclipse.core.commands, \
 	org.eclipse.core.commands.common, \
 	org.eclipse.core.resources, \
+	org.eclipse.core.resources.mapping, \
 	org.eclipse.debug.core, \
 	org.eclipse.debug.ui, \
 	org.eclipse.draw2d, \
@@ -75,6 +76,7 @@
 	org.eclipse.jface.window, \
 	org.eclipse.jface.wizard, \
 	org.eclipse.ltk.core.refactoring, \
+	org.eclipse.ltk.core.refactoring.participants, \
 	org.eclipse.ui.editors.text, \
 	org.eclipse.ui.forms, \
 	org.eclipse.ui.forms.editor, \
diff --git a/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/ExportPackageChange.java b/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/ExportPackageChange.java
new file mode 100644
index 0000000..96b8c2e
--- /dev/null
+++ b/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/ExportPackageChange.java
@@ -0,0 +1,71 @@
+package org.apache.felix.sigil.ui.eclipse.refactor;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.apache.felix.sigil.eclipse.SigilCore;
+import org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel;
+import org.apache.felix.sigil.model.ModelElementFactory;
+import org.apache.felix.sigil.model.osgi.IBundleModelElement;
+import org.apache.felix.sigil.model.osgi.IPackageExport;
+import org.apache.felix.sigil.model.osgi.IPackageImport;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+public class ExportPackageChange extends Change
+{
+
+    private final ISigilProjectModel sigil;
+    private final IPackageExport oldExport;
+    private final IPackageExport newExport;
+
+    public ExportPackageChange(ISigilProjectModel sigil, IPackageExport oldExport, IPackageExport newExport)
+    {
+        this.sigil = sigil;
+        this.oldExport = oldExport;
+        this.newExport = newExport;
+    }
+
+    @Override
+    public Object getModifiedElement()
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getName()
+    {
+        return "Export package update";
+    }
+
+    @Override
+    public void initializeValidationData(IProgressMonitor progress)
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public RefactoringStatus isValid(IProgressMonitor progress) throws CoreException,
+        OperationCanceledException
+    {
+        // TODO check project is synchronized
+        return new RefactoringStatus();
+    }
+
+    @Override
+    public Change perform(IProgressMonitor progress) throws CoreException
+    {   
+        sigil.getBundle().getBundleInfo().removeChild(oldExport);
+        sigil.getBundle().getBundleInfo().addExport(newExport);
+        
+        sigil.save(progress);
+        
+        return new ExportPackageChange(sigil, newExport, oldExport);
+    }
+
+}
diff --git a/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/ImportPackageChange.java b/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/ImportPackageChange.java
new file mode 100644
index 0000000..289a2ac
--- /dev/null
+++ b/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/ImportPackageChange.java
@@ -0,0 +1,64 @@
+package org.apache.felix.sigil.ui.eclipse.refactor;
+
+import org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel;
+import org.apache.felix.sigil.model.osgi.IPackageImport;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+public class ImportPackageChange extends Change
+{
+
+    private final ISigilProjectModel sigil;
+    private final IPackageImport oldImport;
+    private final IPackageImport newImport;
+
+    public ImportPackageChange(ISigilProjectModel sigil, IPackageImport oldImport, IPackageImport newImport)
+    {
+        this.sigil = sigil;
+        this.oldImport = oldImport;
+        this.newImport = newImport;
+    }
+
+    @Override
+    public Object getModifiedElement()
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getName()
+    {
+        return "Import package update";
+    }
+
+    @Override
+    public void initializeValidationData(IProgressMonitor arg0)
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public RefactoringStatus isValid(IProgressMonitor progress) throws CoreException,
+        OperationCanceledException
+    {
+        // TODO check project is synchronized
+        return new RefactoringStatus();
+    }
+
+    @Override
+    public Change perform(IProgressMonitor progress) throws CoreException
+    {
+        sigil.getBundle().getBundleInfo().removeImport(oldImport);
+        sigil.getBundle().getBundleInfo().addImport(newImport);
+        
+        sigil.save(progress);
+        
+        return new ImportPackageChange(sigil, newImport, oldImport);
+    }
+
+}
diff --git a/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/RenamePackageParticipant.java b/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/RenamePackageParticipant.java
new file mode 100644
index 0000000..43ff150
--- /dev/null
+++ b/sigil/eclipse/ui/src/org/apache/felix/sigil/ui/eclipse/refactor/RenamePackageParticipant.java
@@ -0,0 +1,162 @@
+package org.apache.felix.sigil.ui.eclipse.refactor;
+
+import java.util.Collection;
+import java.util.LinkedList;
+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.IModelElement;
+import org.apache.felix.sigil.model.IModelWalker;
+import org.apache.felix.sigil.model.ModelElementFactory;
+import org.apache.felix.sigil.model.osgi.IBundleModelElement;
+import org.apache.felix.sigil.model.osgi.IPackageExport;
+import org.apache.felix.sigil.model.osgi.IPackageImport;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.NullChange;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
+import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
+import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
+
+public class RenamePackageParticipant extends RenameParticipant
+{
+    private IPackageFragment packageFragment;
+    private List<Change> changes = new LinkedList<Change>();
+
+    @Override
+    public RefactoringStatus checkConditions(IProgressMonitor pm,
+        CheckConditionsContext context) throws OperationCanceledException
+    {   
+        RefactoringStatus status = new RefactoringStatus();
+        
+        try
+        {
+            ISigilProjectModel sigil = SigilCore.create(packageFragment.getJavaProject().getProject());
+            final String packageName = packageFragment.getElementName();
+            
+            SigilCore.log("Rename checkConditions " + packageName);
+            
+            final IPackageExport[] found = new IPackageExport[1];
+            sigil.visit(new IModelWalker()
+            {            
+                public boolean visit(IModelElement element)
+                {
+                    if (element instanceof IPackageExport) {
+                        IPackageExport pe = (IPackageExport) element;
+                        if (pe.getPackageName().equals(packageName)) {
+                            found[0] = pe;
+                        }
+                    }
+                    return found[0] == null;
+                }
+            });
+            
+            if (found[0] != null) {
+                // record change to check if out of sync...
+                touch(context, sigil);
+                            
+                status = RefactoringStatus.createWarningStatus("Package " + packageName + " is exported. Renaming this package may effect bundles outside of this workspace");
+                SigilCore.log("Export Package " + packageName + " renamed to " + getArguments().getNewName());
+
+                IPackageExport oldExport = found[0];
+                IPackageExport newExport = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+                newExport.setPackageName(getArguments().getNewName());
+                newExport.setVersion(oldExport.getVersion());
+
+                changes.add(new ExportPackageChange(sigil, oldExport, newExport));                    
+
+                for ( ISigilProjectModel other : SigilCore.getRoot().getProjects() ) {
+                    if ( !sigil.equals(other) ) {
+                        // record change to check if out of sync...
+                        touch(context, other);
+                    }
+                    changes.add(createImportChange(status, other, oldExport, newExport));
+                }
+            }
+        }
+        catch (CoreException e)
+        {
+            SigilCore.warn("Failed to create export package refactor", e);
+            throw new OperationCanceledException("Failed to create export package refactor");
+        }
+        
+        return status;
+    }
+
+    @Override
+    public Change createChange(IProgressMonitor pm) throws CoreException,
+        OperationCanceledException
+    {
+        if (changes.isEmpty()) {
+            return new NullChange();
+        }
+        else 
+        {
+            CompositeChange ret = new CompositeChange("Export-Package update");
+            
+            ret.addAll(changes.toArray(new Change[changes.size()]));
+            
+            return ret;
+        }
+    }
+
+    @Override
+    public String getName()
+    {
+        return "Sigil export package rename participant";
+    }
+
+    @Override
+    protected boolean initialize(Object element)
+    {
+        this.packageFragment = (IPackageFragment) element;
+        return true;
+    }
+
+    private static final void touch(CheckConditionsContext context, ISigilProjectModel sigil)
+    {
+        ResourceChangeChecker checker = (ResourceChangeChecker) context.getChecker(ResourceChangeChecker.class);
+        IResourceChangeDescriptionFactory deltaFactory= checker.getDeltaFactory();        
+        IFile file = sigil.getProject().getFile(SigilCore.SIGIL_PROJECT_FILE);
+        deltaFactory.change(file);
+    }
+
+    private Change createImportChange(RefactoringStatus status, ISigilProjectModel sigil, IPackageExport oldExport, IPackageExport newExport)
+    {
+        IBundleModelElement info = sigil.getBundle().getBundleInfo();
+        Collection<IPackageImport> imports = info.getImports();
+        
+        for (IPackageImport oldImport : imports) {
+            if (oldImport.accepts(oldExport)) {
+                IPackageImport newImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+                
+                newImport.setPackageName(newExport.getPackageName());
+                newImport.setVersions(oldImport.getVersions());
+                
+                status.addInfo(buildImportChangeMsg(sigil, oldImport, newImport));
+                
+                return new ImportPackageChange(sigil, oldImport, newImport);
+            }
+        }
+        
+        // ok no change
+        return new NullChange();
+    }
+
+    private static final String buildImportChangeMsg(ISigilProjectModel sigil,
+        IPackageImport oldImport, IPackageImport newImport)
+    {
+        return "Updating import " + oldImport.getPackageName() + " version " + oldImport.getVersions() +
+            " to " + newImport.getPackageName() + " version " + newImport.getVersions() +
+            " in project " + sigil.getSymbolicName() + " version " + sigil.getVersion();
+    }
+
+}