# HG changeset patch # User Omair Majid # Date 1401983586 14400 # Node ID 31d0ab4467adccb1bee43ba24504a53149ebfd3e # Parent eef52fe805538a8af65cc14708e94c2093055d08 Add an export wizard to export entire plugin Introduce an export wizard that can export the entire plugin projects into a Thermostat installation. Add 'tags' to projects to keep track of what the root project (to export) is. There's barely any feedback for the export process right now. Errors and issues are effectively ignored. diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/plugin.xml --- a/com.redhat.thermostat.tools.eclipse.plugin/plugin.xml Tue Jun 03 17:32:08 2014 -0400 +++ b/com.redhat.thermostat.tools.eclipse.plugin/plugin.xml Thu Jun 05 11:53:06 2014 -0400 @@ -57,5 +57,17 @@ + + + + Export a Thermostat plugin into a target Thermostat installation + + + diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/ProjectFinder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/ProjectFinder.java Thu Jun 05 11:53:06 2014 -0400 @@ -0,0 +1,39 @@ +package com.redhat.thermostat.tools.eclipse.plugin; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; + +public class ProjectFinder { + + // TODO replace with j.u.f.Predicate + public static interface Criteria { + public boolean matches(IProject project); + } + + public List find(Criteria criteria) { + List result = new ArrayList<>(); + + IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + for (IProject project : projects) { + if (criteria.matches(project)) { + result.add(project); + } + } + return result; + } + + public List findProjectNames(Criteria criteria) { + List result = new ArrayList<>(); + + IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + for (IProject project : projects) { + if (criteria.matches(project)) { + result.add(project.getName()); + } + } + return result; + } +} diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/ProjectPreferences.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/ProjectPreferences.java Thu Jun 05 11:53:06 2014 -0400 @@ -0,0 +1,42 @@ +package com.redhat.thermostat.tools.eclipse.plugin; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ProjectScope; +import org.osgi.service.prefs.BackingStoreException; +import org.osgi.service.prefs.Preferences; + +public class ProjectPreferences { + + /** A boolean tag to indicate the root/parent project */ + public static final String PLUGIN_PROJECT = "pluginProject"; + + /** A boolean tag to indicate the project containing the {@code thermostat-plugin.xml} file*/ + public static final String PLUGIN_DISTRIBUTION_PROJECT = "pluginDistributionProject"; + + /** The last thermostat install location that plugin was installed to */ + public static final String THERMOSTAT_LOCATION = "thermostatLocation"; + + private Preferences node; + + public ProjectPreferences(IProject project) { + ProjectScope scope = new ProjectScope(project); + Preferences node = scope.getNode(Activator.PLUGIN_ID); + if (node == null) { + throw new AssertionError("Null prefs node not implemented"); + } + this.node = node; + } + + public void add(String key, String value) { + node.put(key, value); + } + + public String get(String key) { + return node.get(key, null); + } + + public void flush() throws BackingStoreException { + node.flush(); + } + +} diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/SelectionUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/SelectionUtils.java Thu Jun 05 11:53:06 2014 -0400 @@ -0,0 +1,31 @@ +package com.redhat.thermostat.tools.eclipse.plugin; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; + +public class SelectionUtils { + + /** + * Returns an IProject that corresponds to the currently selected project. + * Returns {@code null} if no project was selected or multiple projects were + * selected + */ + public static IProject getSelectedProject(ISelection selection) { + if (selection instanceof IStructuredSelection && selection.isEmpty() == false) { + IStructuredSelection ssel = (IStructuredSelection) selection; + if (ssel.size() > 1) { + return null; + } + + Object obj = ssel.getFirstElement(); + if (obj instanceof IAdaptable) { + IProject container = (IProject)((IAdaptable)obj).getAdapter(IProject.class); + return container; + } + } + + return null; + } +} diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/Unzipper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/Unzipper.java Thu Jun 05 11:53:06 2014 -0400 @@ -0,0 +1,43 @@ +package com.redhat.thermostat.tools.eclipse.plugin; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class Unzipper { + + public void unzip(ZipFile zipFile, File targetDirectory) throws IOException { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + + if (entry.isDirectory()) { + continue; + } + + File targetFile = new File(targetDirectory, entry.getName()); + targetFile.getParentFile().mkdirs(); + + copyStream(zipFile.getInputStream(entry), new FileOutputStream(targetFile)); + } + } + + private void copyStream(InputStream input, OutputStream output) throws IOException { + byte[] buffer = new byte[1024]; + try (InputStream in = new BufferedInputStream(input)) { + try (OutputStream out = new BufferedOutputStream(output)) { + int read = 0; + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + } + } + } +} diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/ExportPluginPage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/ExportPluginPage.java Thu Jun 05 11:53:06 2014 -0400 @@ -0,0 +1,211 @@ +package com.redhat.thermostat.tools.eclipse.plugin.wizards; + +import java.io.File; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.fieldassist.SimpleContentProposalProvider; +import org.eclipse.jface.fieldassist.TextContentAdapter; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import com.redhat.thermostat.tools.eclipse.plugin.ProjectFinder; +import com.redhat.thermostat.tools.eclipse.plugin.ProjectFinder.Criteria; +import com.redhat.thermostat.tools.eclipse.plugin.ProjectPreferences; +import com.redhat.thermostat.tools.eclipse.plugin.SelectionUtils; + +public class ExportPluginPage extends WizardPage { + + static final String PAGE_NAME = "exportPlugin"; //$NON-NLS-1$ + + private IProject project; + + private Text projectNameText; + private Text installationLocationText; + + + private ISelection selection; + + public ExportPluginPage(ISelection selection) { + super(PAGE_NAME); + setTitle("Export Plugin"); + setDescription("Export a plugin to a Thermostat installation"); + this.selection = selection; + } + + public void createControl(Composite parent) { + GridData gd; + + Composite container = new Composite(parent, SWT.NULL); + GridLayout layout = new GridLayout(3, false); + container.setLayout(layout); + + Label projectName = new Label(container, SWT.NONE); + projectName.setText("Project Name"); + + projectNameText = new Text(container, SWT.BORDER | SWT.SINGLE); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 2; + projectNameText.setLayoutData(gd); + projectNameText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + dialogChanged(); + } + }); + + Label targetDirectory = new Label(container, SWT.NONE); + targetDirectory.setText("Installation Location"); + + installationLocationText = new Text(container, SWT.BORDER | SWT.SINGLE); + gd = new GridData(GridData.FILL_HORIZONTAL); + installationLocationText.setLayoutData(gd); + installationLocationText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + dialogChanged(); + } + }); + + Button browse = new Button(container, SWT.PUSH); + browse.setText("Browse"); + browse.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + handleBrowse(); + } + }); + + String[] proposals = getProjectNamesForExporting().toArray(new String[0]); + SimpleContentProposalProvider projectProposalProvider = new SimpleContentProposalProvider(proposals); + TextContentAdapter contentAdapter = new TextContentAdapter(); + ContentProposalAdapter proposalAdapter = new ContentProposalAdapter(projectNameText, contentAdapter, projectProposalProvider, null, null); + proposalAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); + + initialize(); + dialogChanged(); + + setControl(container); + } + + private void initialize() { + IProject project = SelectionUtils.getSelectedProject(selection); + if (project != null) { + ProjectPreferences prefs = new ProjectPreferences(project); + if (Boolean.valueOf(prefs.get(ProjectPreferences.PLUGIN_PROJECT))) { + this.project = project; + projectNameText.setText(project.getName()); + } + } + } + + private void dialogChanged() { + setErrorMessage(null); + setPageComplete(isProjectValid() && isTargetLocationValid()); + } + + /** Side-effect: Update error status message */ + private boolean isProjectValid() { + String containerName = getProjectName(); + + if (containerName.length() == 0) { + setErrorMessage("Project name must be specified"); + return false; + } + + IResource container = ResourcesPlugin + .getWorkspace() + .getRoot() + .findMember(new Path(containerName)); + + if (container == null + || (container.getType() & (IResource.PROJECT | IResource.FOLDER)) == 0) { + setErrorMessage("Project must exist"); + return false; + } + if (!container.isAccessible()) { + setErrorMessage("Project must be readable"); + return false; + } + + return true; + + } + + /** Side-effect: Update error status message */ + private boolean isTargetLocationValid() { + File thermostatHome = new File(installationLocationText.getText()); + if (!thermostatHome.isDirectory()) { + setErrorMessage("Not a valid thermostat directory"); + return false; + } + + File pluginDir = new File(thermostatHome, "plugins"); + if (!pluginDir.isDirectory()) { + setErrorMessage("Not a valid thermostat directory"); + return false; + } + + String pluginName = projectNameText.getText().replace("-distribution", ""); + File pluginHome = new File(pluginDir, pluginName); + if (pluginHome.isDirectory()) { + setErrorMessage("Directory " + pluginHome + " already exists."); + return false; + } + + return true; + } + + private void handleBrowse() { + DirectoryDialog dialog = new DirectoryDialog(getShell()); + String selectedDir = dialog.open(); + if (selectedDir != null) { + installationLocationText.setText(selectedDir); + } + } + + public IProject getProject() { + return this.project; + } + + public String getProjectName() { + return this.project.getName(); + } + + public String getInstallationLocation() { + return installationLocationText.getText(); + } + + private List getProjectNamesForExporting() { + ProjectFinder finder = new ProjectFinder(); + return finder.findProjectNames(new ValidPluginProjectMatcher()); + } + + private static class ValidPluginProjectMatcher implements Criteria { + @Override + public boolean matches(IProject project) { + if (!project.isOpen()) { + return false; + } + + ProjectPreferences prefs = new ProjectPreferences(project); + return Boolean.valueOf(prefs.get(ProjectPreferences.PLUGIN_PROJECT)); + } + } + + +} diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/ExportPluginWizard.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/ExportPluginWizard.java Thu Jun 05 11:53:06 2014 -0400 @@ -0,0 +1,127 @@ +package com.redhat.thermostat.tools.eclipse.plugin.wizards; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.zip.ZipFile; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.ui.IExportWizard; +import org.eclipse.ui.IWorkbench; + +import com.redhat.thermostat.tools.eclipse.plugin.Activator; +import com.redhat.thermostat.tools.eclipse.plugin.Unzipper; + +public class ExportPluginWizard extends Wizard implements IExportWizard { + + private IStructuredSelection selection; + private ExportPluginPage exportPage; + + @Override + public void init(IWorkbench workbench, IStructuredSelection selection) { + setWindowTitle("Export Thermostat Plugin"); + setNeedsProgressMonitor(true); + + this.selection = selection; + } + + @Override + public void addPages() { + exportPage = new ExportPluginPage(selection); + addPage(exportPage); + } + + @Override + public boolean performFinish() { + IProject project = exportPage.getProject(); + String targetLocation = exportPage.getInstallationLocation(); + + Job export = new ExportPlugin(project, targetLocation); + export.schedule(); + + return true; + } + + private static class ExportPlugin extends Job { + + private IProject project; + private String targetLocation; + + public ExportPlugin(IProject project, String targetLocation) { + super("Exporting Thermostat plugin"); + this.project = project; + this.targetLocation = targetLocation; + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + // mvn clean package + new MavenRunner().run(project, "package"); + } catch (CoreException e) { + e.printStackTrace(); + return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to run 'mvn clean package'", e); + } + + final String distributionModuleName = project.getName() + "-distribution"; + // TODO unzip and extract to thermostatLocation + File projectDir = project.getLocation().toFile(); + // FIXME don't hardcode distribution project name + File distributionDir = new File(projectDir, distributionModuleName); + File distributionTargetDir = new File(distributionDir, "target"); + String[] files = distributionTargetDir.list(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.matches("\\Q" + distributionModuleName + "\\E.*\\.zip"); + } + }); + + String zipFile = files[0]; + try { + ZipFile pluginZip = new ZipFile(new File(distributionTargetDir, zipFile)); + File installationDirectory = new File(targetLocation, "plugins"); + new Unzipper().unzip(pluginZip, installationDirectory); + System.out.println("Done installing " + project.getName() + " to " + installationDirectory); + } catch (IOException e) { + e.printStackTrace(); + return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to install plugin in target location", e); + } + + return Status.OK_STATUS; + } + } + + private static class MavenRunner { + + public void run(IProject project, String goal) throws CoreException { + try { + System.out.println("Starting packaging"); + ProcessBuilder processBuilder = new ProcessBuilder("mvn", "clean", goal); + processBuilder.directory(project.getLocation().toFile()); + processBuilder.redirectError(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); + Process process = processBuilder.start(); + + // FIXME make this non-blocking. Add tons of feedback. + + boolean result = process.waitFor(5, TimeUnit.MINUTES); + if (!result) { + throw new AssertionError("An error invoking maven. Maybe try 'mvn clean package' on command line?"); + } + System.out.println("Done packaging"); + } catch (IOException | InterruptedException e) { + IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error running maven", e); + throw new CoreException(status); + } + } + + } +} diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/PluginXmlProjectSelectionPage.java --- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/PluginXmlProjectSelectionPage.java Tue Jun 03 17:32:08 2014 -0400 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/PluginXmlProjectSelectionPage.java Thu Jun 05 11:53:06 2014 -0400 @@ -1,18 +1,15 @@ package com.redhat.thermostat.tools.eclipse.plugin.wizards; -import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.Path; import org.eclipse.jface.fieldassist.ContentProposalAdapter; import org.eclipse.jface.fieldassist.SimpleContentProposalProvider; import org.eclipse.jface.fieldassist.TextContentAdapter; import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; @@ -28,6 +25,9 @@ import org.eclipse.ui.dialogs.ContainerSelectionDialog; import com.redhat.thermostat.tools.eclipse.plugin.Messages; +import com.redhat.thermostat.tools.eclipse.plugin.ProjectFinder; +import com.redhat.thermostat.tools.eclipse.plugin.ProjectFinder.Criteria; +import com.redhat.thermostat.tools.eclipse.plugin.SelectionUtils; public class PluginXmlProjectSelectionPage extends WizardPage { @@ -45,12 +45,12 @@ } public void createControl(Composite parent) { - Composite container = new Composite(parent, SWT.NULL); + Composite container = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); container.setLayout(layout); layout.numColumns = 3; layout.verticalSpacing = 9; - Label label = new Label(container, SWT.NULL); + Label label = new Label(container, SWT.NONE); label.setText(Messages.PluginXmlProjectSelectionPage_projectNameLabel); containerText = new Text(container, SWT.BORDER | SWT.SINGLE); @@ -62,7 +62,7 @@ } }); - String[] proposals = getOpenProjectNames().toArray(new String[0]); + String[] proposals = getProjectNamesForPluginXmlFile().toArray(new String[0]); SimpleContentProposalProvider projectProposalProvider = new SimpleContentProposalProvider(proposals); TextContentAdapter contentAdapter = new TextContentAdapter(); ContentProposalAdapter proposalAdapter = new ContentProposalAdapter(containerText, contentAdapter, projectProposalProvider, null, null); @@ -82,18 +82,9 @@ } private void initialize() { - if (selection != null && selection.isEmpty() == false - && selection instanceof IStructuredSelection) { - IStructuredSelection ssel = (IStructuredSelection) selection; - if (ssel.size() > 1) - return; - Object obj = ssel.getFirstElement(); - - if (obj instanceof IAdaptable) { - IProject container = (IProject)((IAdaptable)obj).getAdapter(IProject.class); - - containerText.setText(container.getName().toString()); - } + IProject project = SelectionUtils.getSelectedProject(selection); + if (project != null) { + containerText.setText(project.getName()); } } @@ -141,16 +132,13 @@ return containerText.getText(); } - private List getOpenProjectNames() { - List result = new ArrayList<>(); - - IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); - for (IProject project : projects) { - if (project.isOpen()) { - result.add(project.getName()); - } - } - - return result; + private List getProjectNamesForPluginXmlFile() { + ProjectFinder finder = new ProjectFinder(); + return finder.findProjectNames(new Criteria() { + @Override + public boolean matches(IProject project) { + return project.isOpen(); + } + }); } } diff -r eef52fe80553 -r 31d0ab4467ad com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/ProjectCreator.java --- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/ProjectCreator.java Tue Jun 03 17:32:08 2014 -0400 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/wizards/ProjectCreator.java Thu Jun 05 11:53:06 2014 -0400 @@ -32,8 +32,10 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.handlers.IHandlerService; +import org.osgi.service.prefs.BackingStoreException; import com.redhat.thermostat.tools.eclipse.plugin.Activator; +import com.redhat.thermostat.tools.eclipse.plugin.ProjectPreferences; import com.redhat.thermostat.tools.eclipse.plugin.wizards.PluginXmlCreator.Decisions; /** Creates a Thermostat project using the preferred thermostat defaults, settings, and style */ @@ -164,6 +166,8 @@ String projectName = artifactId; Project project = createMavenProject(null, projectName); + addTag(project, ProjectPreferences.PLUGIN_PROJECT, Boolean.TRUE.toString()); + String pomContents = "" + POM_HEADER + " " + groupId + "\n" @@ -303,6 +307,8 @@ Project projects = createJavaAndMavenProject(parent, artifactId); + addTag(projects, ProjectPreferences.PLUGIN_DISTRIBUTION_PROJECT, Boolean.TRUE.toString()); + String pomContents = "" + POM_HEADER + createParentSection(groupId, parentId, version) @@ -486,6 +492,17 @@ + " \n"; } + private void addTag(Project project, String key, String value) throws IOException { + IProject theProject = project.project; + ProjectPreferences prefs = new ProjectPreferences(theProject); + prefs.add(key, value); + try { + prefs.flush(); + } catch (BackingStoreException e) { + throw new IOException(e); + } + } + private void refreshProjects(List projects) throws CoreException { // Refresh projects ResourcesPlugin.getWorkspace().getRoot().refreshLocal(IWorkspaceRoot.DEPTH_INFINITE, new NullProgressMonitor());