blob: c84d17414bf165e4be3b51f5d80513ab7c7e186b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Red Hat Inc. and others.
* 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:
* Mickael Istria (Red Hat Inc.) - initial API and implementation
*******************************************************************************/
package org.eclipse.epp.internal.mpc.ui;
import java.util.Collection;
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.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.ResourcesPlugin;
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.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.core.runtime.jobs.JobGroup;
import org.eclipse.epp.mpc.core.model.INode;
import org.eclipse.epp.mpc.core.model.ISearchResult;
import org.eclipse.epp.mpc.core.service.IMarketplaceService;
import org.eclipse.epp.mpc.core.service.IMarketplaceServiceLocator;
import org.eclipse.epp.mpc.ui.IMarketplaceClientConfiguration;
import org.eclipse.epp.mpc.ui.IMarketplaceClientService;
import org.eclipse.epp.mpc.ui.MarketplaceClient;
import org.eclipse.equinox.internal.p2.ui.discovery.DiscoveryImages;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.LayoutConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IStartup;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.UIJob;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
public class MissingNatureDetector implements IStartup {
private static final class CollectMissingNaturesVisitor implements IResourceDeltaVisitor {
private final Set<String> missingNatures = new HashSet<String>();
public boolean visit(IResourceDelta delta) throws CoreException {
if (delta.getResource().getType() == IResource.PROJECT
|| delta.getResource().getType() == IResource.ROOT) {
return true;
}
if (delta.getResource().getType() == IResource.FILE
&& IProjectDescription.DESCRIPTION_FILE_NAME
.equals(delta.getResource().getName())) {
if (delta.getKind() == IResourceDelta.ADDED
|| delta.getKind() == IResourceDelta.CHANGED) {
IProject project = delta.getResource().getProject();
for (String natureId : project.getDescription().getNatureIds()) {
if (project.getWorkspace().getNatureDescriptor(natureId) == null) {
this.missingNatures.add(natureId);
}
}
}
}
return false;
}
public Set<String> getMissingNatures() {
return this.missingNatures;
}
}
private static final class LookupByNatureJob extends Job {
private final String natureId;
private List<? extends INode> nodes;
private LookupByNatureJob(String name, String natureId) {
super(name);
this.natureId = natureId;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
BundleContext bundleContext = MarketplaceClientUiPlugin.getBundleContext();
ServiceReference<IMarketplaceServiceLocator> locatorReference = bundleContext
.getServiceReference(IMarketplaceServiceLocator.class);
IMarketplaceServiceLocator locator = bundleContext.getService(locatorReference);
IMarketplaceService marketplaceService = locator.getDefaultMarketplaceService();
String fileExtensionTag = "nature_" + natureId; //$NON-NLS-1$]
try {
ISearchResult searchResult = marketplaceService.tagged(fileExtensionTag, monitor);
nodes = searchResult.getNodes();
} catch (CoreException ex) {
return new Status(IStatus.ERROR,
MarketplaceClientUiPlugin.getInstance().getBundle().getSymbolicName(), ex.getMessage(), ex);
}
return Status.OK_STATUS;
}
public Collection<INode> getCandidates() {
return (List<INode>) this.nodes;
}
}
private final JobGroup allJobs = new JobGroup(Messages.MissingNatureDetector_Title, 3, 0);
private final Map<String, LookupByNatureJob> lookupJobs = new HashMap<String, LookupByNatureJob>();
private final UIJob showCandidatesJob = new UIJob(PlatformUI.getWorkbench().getDisplay(),
Messages.MissingNatureDetector_Desc) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
TitleAreaDialog dialog = new TitleAreaDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()) {
private Image wizban;
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(Messages.MissingNatureDetector_Title);
}
@Override
public Control createDialogArea(Composite parent) {
setTitle(Messages.MissingNatureDetector_Title);
setMessage(Messages.MissingNatureDetector_Desc);
wizban = DiscoveryImages.BANNER_DISOVERY.createImage();
setTitleImage(wizban);
Composite res = new Composite(parent, SWT.NONE);
GridDataFactory.fillDefaults()
.grab(true, true)
.hint(SWT.DEFAULT, SWT.DEFAULT)
.applyTo(res);
GridLayoutFactory.fillDefaults().margins(LayoutConstants.getMargins()).equalWidth(false).applyTo(
res);
Label label = new Label(res, SWT.WRAP);
label.setText(Messages.MissingNatureDetector_Message);
return res;
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
super.createButtonsForButtonBar(parent);
getButton(IDialogConstants.OK_ID).setText(Messages.MissingNatureDetector_ShowSolutions);
}
@Override
public boolean close() {
if (super.close()) {
wizban.dispose();
}
return false;
}
};
if (dialog.open() == IDialogConstants.CANCEL_ID) {
return Status.CANCEL_STATUS;
}
IMarketplaceClientService marketplaceClientService = MarketplaceClient.getMarketplaceClientService();
IMarketplaceClientConfiguration config = marketplaceClientService.newConfiguration();
Set<INode> allNodes = new HashSet<INode>();
for (LookupByNatureJob job : lookupJobs.values()) {
allNodes.addAll(job.getCandidates());
}
marketplaceClientService.open(config, allNodes);
return Status.OK_STATUS;
}
};
public void earlyStartup() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(new IResourceChangeListener() {
public void resourceChanged(IResourceChangeEvent event) {
if (event.getDelta() == null) {
return;
}
try {
CollectMissingNaturesVisitor visitor = new CollectMissingNaturesVisitor();
event.getDelta().accept(visitor);
for (String natureId : visitor.getMissingNatures()) {
triggerNatureLookup(natureId);
}
if (!visitor.getMissingNatures().isEmpty()) {
showProposalsIfReady();
}
} catch (CoreException e) {
MarketplaceClientUiPlugin.getInstance()
.getLog()
.log(new Status(IStatus.ERROR,
MarketplaceClientUiPlugin.getInstance().getBundle().getSymbolicName(),
e.getLocalizedMessage(), e));
}
}
});
}
private void triggerNatureLookup(final String natureId) {
LookupByNatureJob mpcJob = null;
synchronized (lookupJobs) {
if (lookupJobs.containsKey(natureId)) {
return;
} else {
mpcJob = new LookupByNatureJob(Messages.AskMarketPlaceForFileSupportStrategy_jobName, natureId);
lookupJobs.put(natureId, mpcJob);
}
}
mpcJob.setSystem(false);
mpcJob.setUser(false);
mpcJob.setJobGroup(allJobs);
mpcJob.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
showProposalsIfReady();
}
});
lookupJobs.put(natureId, mpcJob);
mpcJob.schedule();
}
protected boolean hasCandidates() {
for (LookupByNatureJob job : this.lookupJobs.values()) {
if (!job.getCandidates().isEmpty()) {
return true;
}
}
return false;
}
private void showProposalsIfReady() {
if (allJobs.getActiveJobs().isEmpty() && hasCandidates() && showCandidatesJob.getState() == Job.NONE) {
showCandidatesJob.schedule();
}
}
}