/**
* Copyright (c) 2010, 2013 Darmstadt University of Technology.
* 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:
* Marcel Bruch - initial API and implementation.
*/
package org.eclipse.recommenders.internal.rcp;
import static com.google.inject.Scopes.SINGLETON;
import static org.apache.commons.lang3.ArrayUtils.contains;
import static org.eclipse.recommenders.internal.rcp.l10n.LogMessages.*;
import static org.eclipse.recommenders.utils.Logs.log;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.eclipse.core.internal.net.ProxyManager;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.recommenders.internal.rcp.l10n.Messages;
import org.eclipse.recommenders.jdt.AstBindings;
import org.eclipse.recommenders.rcp.IAstProvider;
import org.eclipse.recommenders.rcp.IRcpService;
import org.eclipse.recommenders.rcp.JavaElementResolver;
import org.eclipse.recommenders.rcp.SharedImages;
import org.eclipse.recommenders.rcp.utils.ASTNodeUtils;
import org.eclipse.recommenders.rcp.utils.ASTStringUtils;
import org.eclipse.recommenders.utils.Logs;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;
import org.eclipse.ui.progress.UIJob;
import org.osgi.framework.Bundle;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
import com.google.inject.AbstractModule;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
@SuppressWarnings("restriction")
public class RcpModule extends AbstractModule {
@Override
protected void configure() {
bind(JavaElementResolver.class).in(SINGLETON);
requestStaticInjection(ASTStringUtils.class);
requestStaticInjection(ASTNodeUtils.class);
requestStaticInjection(AstBindings.class);
bind(Helper.class).asEagerSingleton();
bind(SharedImages.class).in(SINGLETON);
configureAstProvider();
bindRcpServiceListener();
checkBundleResolution();
}
private void configureAstProvider() {
final CachingAstProvider p = new CachingAstProvider();
JavaCore.addElementChangedListener(p);
bind(IAstProvider.class).toInstance(p);
}
private void bindRcpServiceListener() {
bindListener(new RcpServiceMatcher(), new Listener());
}
@Singleton
@Provides
public JavaModelEventsService provideJavaModelEventsProvider(final EventBus bus, final IWorkspaceRoot workspace) {
final JavaModelEventsService p = new JavaModelEventsService(bus, workspace);
JavaCore.addElementChangedListener(p);
return p;
}
@Provides
public IProxyService provideProxyService() {
return ProxyManager.getProxyManager();
}
@Provides
public IWebBrowser provideWebBrowser(IWorkbench wb) throws PartInitException {
return wb.getBrowserSupport().getExternalBrowser();
}
@Provides
@Singleton
public JavaElementSelectionService provideJavaSelectionProvider(final EventBus bus) {
final JavaElementSelectionService provider = new JavaElementSelectionService(bus);
new UIJob(Messages.JOB_NAME_SELECTION_LISTENER_REGISTRATION) {
{
schedule();
}
@Override
public IStatus runInUIThread(final IProgressMonitor monitor) {
final IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
final ISelectionService service = (ISelectionService) ww.getService(ISelectionService.class);
service.addPostSelectionListener(provider);
return Status.OK_STATUS;
}
};
return provider;
}
@Provides
public IWorkspaceRoot provideWorkspaceRoot() {
return ResourcesPlugin.getWorkspace().getRoot();
}
@Provides
public IWorkspace provideWorkspace() {
return ResourcesPlugin.getWorkspace();
}
@Provides
public Display provideDisplay() {
Display d = Display.getCurrent();
if (d == null) {
d = Display.getDefault();
}
return d;
}
@Provides
public IWorkbench provideWorkbench() {
return PlatformUI.getWorkbench();
}
@Provides
public IWorkbenchPage provideActiveWorkbenchPage(final IWorkbench wb) {
if (isRunningInUiThread()) {
return wb.getActiveWorkbenchWindow().getActivePage();
}
return runUiFinder().activePage;
}
@Provides
public Shell provideActiveShell(IWorkbench wb) {
return wb.getActiveWorkbenchWindow().getShell();
}
private ActivePageFinder runUiFinder() {
final ActivePageFinder finder = new ActivePageFinder();
try {
if (isRunningInUiThread()) {
finder.call();
} else {
final FutureTask<IWorkbenchPage> task = new FutureTask<IWorkbenchPage>(finder);
Display.getDefault().asyncExec(task);
task.get(2, TimeUnit.SECONDS);
}
} catch (final Exception e) {
log(ERROR_ACTIVE_PAGE_FINDER_TOO_EARLY, e);
}
return finder;
}
private boolean isRunningInUiThread() {
return Display.getCurrent() != null;
}
@Provides
public IJavaModel provideJavaModel() {
return JavaModelManager.getJavaModelManager().getJavaModel();
}
@Provides
public JavaModelManager provideJavaModelManger() {
return JavaModelManager.getJavaModelManager();
}
@Provides
public IExtensionRegistry provideRegistry() {
return Platform.getExtensionRegistry();
}
private void checkBundleResolution() {
Bundle[] bundles = RcpPlugin.getDefault().getBundle().getBundleContext().getBundles();
final Collection<Bundle> unresolvedBundles = Lists.newArrayList();
for (Bundle bundle : bundles) {
if (bundle.getSymbolicName().startsWith("org.eclipse.recommenders")) { //$NON-NLS-1$
if (bundle.getState() == Bundle.INSTALLED) {
unresolvedBundles.add(bundle);
}
}
}
if (!unresolvedBundles.isEmpty()) {
final Display display = Display.getDefault();
display.asyncExec(new Runnable() {
@Override
public void run() {
BundleResolutionFailureDialog dialog = new BundleResolutionFailureDialog(display.getActiveShell(),
RcpPlugin.getDefault().getBundle().getVersion(), unresolvedBundles);
if (!dialog.isIgnored()) {
dialog.open();
}
}
});
}
}
static class RcpServiceMatcher extends AbstractMatcher<Object> {
@Override
public boolean matches(Object t) {
if (t instanceof TypeLiteral<?>) {
Class<?> rawType = ((TypeLiteral<?>) t).getRawType();
Class<?>[] implemented = rawType.getInterfaces();
return contains(implemented, IRcpService.class);
}
return false;
}
}
/**
* Ensures that the services are created
*/
static class Helper {
@Inject
JavaElementSelectionService provider;
@Inject
JavaModelEventsService javaModelEventsService;
}
static class Listener implements TypeListener {
@Override
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
final Provider<EventBus> provider = encounter.getProvider(EventBus.class);
encounter.register(new InjectionListener<I>() {
@Override
public void afterInjection(final Object i) {
registerWithEventBus(i);
for (final Method m : i.getClass().getDeclaredMethods()) {
boolean hasPostConstruct = m.getAnnotation(PostConstruct.class) != null;
boolean hasPreDestroy = m.getAnnotation(PreDestroy.class) != null;
if (hasPreDestroy) {
registerPreDestroyHook(i, m);
}
if (hasPostConstruct) {
executeMethod(i, m);
}
}
}
private void executeMethod(final Object i, final Method m) {
try {
m.setAccessible(true);
m.invoke(i);
} catch (Exception e) {
Logs.log(ERROR_EXCEPTION_OCCURRED_IN_SERVICE_HOOK, e, m);
}
}
private void registerPreDestroyHook(final Object i, final Method m) {
PlatformUI.getWorkbench().addWorkbenchListener(new IWorkbenchListener() {
@Override
public boolean preShutdown(IWorkbench workbench, boolean forced) {
return true;
}
@Override
public void postShutdown(IWorkbench workbench) {
executeMethod(i, m);
}
});
}
private void registerWithEventBus(final Object i) {
EventBus bus = provider.get();
bus.register(i);
}
});
}
}
private static final class ActivePageFinder implements Callable<IWorkbenchPage> {
private IWorkbench workbench;
private IWorkbenchWindow activeWorkbenchWindow;
private IWorkbenchPage activePage;
@Override
public IWorkbenchPage call() throws Exception {
workbench = PlatformUI.getWorkbench();
activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
activePage = activeWorkbenchWindow.getActivePage();
return activePage;
}
}
}