/*
* SonarLint for Eclipse
* Copyright (C) 2015-2017 SonarSource SA
* sonarlint@sonarsource.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonarlint.eclipse.ui.internal;
import javax.annotation.CheckForNull;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeEvent;
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.jface.util.IPropertyChangeListener;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.progress.UIJob;
import org.osgi.framework.BundleContext;
import org.sonarlint.eclipse.core.SonarLintLogger;
import org.sonarlint.eclipse.core.internal.PreferencesUtils;
import org.sonarlint.eclipse.core.internal.SonarLintCorePlugin;
import org.sonarlint.eclipse.core.internal.TriggerType;
import org.sonarlint.eclipse.core.internal.adapter.Adapters;
import org.sonarlint.eclipse.core.internal.jobs.LogListener;
import org.sonarlint.eclipse.core.internal.markers.MarkerUtils;
import org.sonarlint.eclipse.core.internal.server.IServer;
import org.sonarlint.eclipse.core.internal.utils.SonarLintUtils;
import org.sonarlint.eclipse.core.resource.ISonarLintFile;
import org.sonarlint.eclipse.core.resource.ISonarLintProject;
import org.sonarlint.eclipse.ui.internal.console.SonarLintConsole;
import org.sonarlint.eclipse.ui.internal.job.CheckForUpdatesJob;
import org.sonarlint.eclipse.ui.internal.popup.ServerStorageNeedUpdatePopup;
import org.sonarlint.eclipse.ui.internal.server.actions.JobUtils;
public class SonarLintUiPlugin extends AbstractUIPlugin {
public static final String PLUGIN_ID = "org.sonarlint.eclipse.ui";
// The shared instance
private static SonarLintUiPlugin plugin;
private IPropertyChangeListener prefListener;
private LogListener logListener;
private SonarLintConsole console;
private static final SonarLintPartListener SONARLINT_PART_LISTENER = new SonarLintPartListener();
private static final SonarLintChangeListener SONARLINT_CHANGE_LISTENER = new SonarLintChangeListener();
public SonarLintUiPlugin() {
plugin = this;
}
private class SonarLintConsoleLogger implements LogListener {
@Override
public void info(String msg, boolean fromAnalyzer) {
if (PlatformUI.isWorkbenchRunning()) {
getSonarConsole().info(msg, fromAnalyzer);
}
}
@Override
public void error(String msg, boolean fromAnalyzer) {
if (PlatformUI.isWorkbenchRunning()) {
getSonarConsole().error(msg, fromAnalyzer);
}
}
@Override
public void debug(String msg, boolean fromAnalyzer) {
if (PlatformUI.isWorkbenchRunning()) {
getSonarConsole().debug(msg, fromAnalyzer);
}
}
}
@Override
public void start(final BundleContext context) throws Exception {
super.start(context);
ResourcesPlugin.getWorkspace().addResourceChangeListener(SONARLINT_CHANGE_LISTENER, IResourceChangeEvent.POST_CHANGE);
logListener = new SonarLintConsoleLogger();
SonarLintLogger.get().addLogListener(logListener);
addSonarLintPartListener();
prefListener = event -> {
if (event.getProperty().equals(PreferencesUtils.PREF_MARKER_SEVERITY)) {
try {
MarkerUtils.updateAllSonarMarkerSeverity();
} catch (CoreException e) {
SonarLintLogger.get().error("Unable to update marker severity", e);
}
}
};
getPreferenceStore().addPropertyChangeListener(prefListener);
SonarLintLogger.get().info("Starting SonarLint for Eclipse " + SonarLintUtils.getPluginVersion());
checkServersStatus();
new CheckForUpdatesJob().schedule((long) 10 * 1000);
analyzeCurrentFile();
}
private static void checkServersStatus() {
for (final IServer server : SonarLintCorePlugin.getServersManager().getServers()) {
if (!server.isStorageUpdated()) {
Display.getDefault().asyncExec(() -> {
ServerStorageNeedUpdatePopup popup = new ServerStorageNeedUpdatePopup(Display.getCurrent(), server);
popup.create();
popup.open();
});
}
}
}
@Override
public void stop(final BundleContext context) throws Exception {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(SONARLINT_CHANGE_LISTENER);
SonarLintLogger.get().removeLogListener(logListener);
try {
getPreferenceStore().removePropertyChangeListener(prefListener);
} finally {
super.stop(context);
}
}
/**
* @return the shared instance
*/
public static SonarLintUiPlugin getDefault() {
return plugin;
}
public synchronized SonarLintConsole getSonarConsole() {
if (console != null) {
return console;
}
// no console found, so create a new one
console = new SonarLintConsole(SonarLintImages.SONARLINT_CONSOLE_IMG_DESC);
return console;
}
public synchronized void closeSonarConsole() {
if (console != null) {
IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager();
manager.removeConsoles(new IConsole[] {console});
this.console = null;
}
}
private static class RegisterSonarLintPartListenerJob extends UIJob {
RegisterSonarLintPartListenerJob() {
super("Register SonarLint part listener");
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
addListenerToAllPages(window);
}
// Handle future opened/closed windows
PlatformUI.getWorkbench().addWindowListener(new WindowOpenCloseListener());
return Status.OK_STATUS;
}
static class WindowOpenCloseListener implements IWindowListener {
@Override
public void windowOpened(IWorkbenchWindow window) {
addListenerToAllPages(window);
}
@Override
public void windowDeactivated(IWorkbenchWindow window) {
// Nothing to do
}
@Override
public void windowClosed(IWorkbenchWindow window) {
removeListenerToAllPages(window);
}
@Override
public void windowActivated(IWorkbenchWindow window) {
// Nothing to do
}
private static void removeListenerToAllPages(IWorkbenchWindow window) {
for (IWorkbenchPage page : window.getPages()) {
page.removePartListener(SONARLINT_PART_LISTENER);
}
}
}
private static void addListenerToAllPages(IWorkbenchWindow window) {
for (IWorkbenchPage page : window.getPages()) {
page.addPartListener(SONARLINT_PART_LISTENER);
}
}
}
private static class AnalyzeCurrentFileJob extends UIJob {
AnalyzeCurrentFileJob() {
super("Analyze current file");
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
JobUtils.scheduleAnalysisOfOpenFiles((ISonarLintProject) null, TriggerType.STARTUP);
return Status.OK_STATUS;
}
}
@CheckForNull
public static ISonarLintFile findCurrentEditedFile() {
IEditorPart editor = findActiveEditor();
if (editor == null) {
return null;
}
IFile file = Adapters.adapt(editor.getEditorInput(), IFile.class);
if (file != null) {
return Adapters.adapt(file, ISonarLintFile.class);
}
return null;
}
@CheckForNull
public static IEditorPart findActiveEditor() {
// Super defensing programming because we don't really understand what is initialized at startup (SLE-122)
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
return null;
}
IWorkbenchPage page = window.getActivePage();
if (page == null) {
return null;
}
return page.getActiveEditor();
}
public static void analyzeCurrentFile() {
// SLE-122 Delay a little bit to let the time to the workspace to initialize (and avoid NPE)
new AnalyzeCurrentFileJob().schedule(2000);
}
public static void addSonarLintPartListener() {
new RegisterSonarLintPartListenerJob().schedule();
}
}