/*******************************************************************************
* Copyright Technophobia Ltd 2012
*
* This file is part of the Substeps Eclipse Plugin.
*
* The Substeps Eclipse Plugin is free software: you can redistribute it and/or modify
* it under the terms of the Eclipse Public License v1.0.
*
* The Substeps Eclipse Plugin 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
* Eclipse Public License for more details.
*
* You should have received a copy of the Eclipse Public License
* along with the Substeps Eclipse Plugin. If not, see <http://www.eclipse.org/legal/epl-v10.html>.
******************************************************************************/
package com.technophobia.substeps;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.preference.IPersistentPreferenceStore;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.technophobia.eclipse.log.PluginLogger;
import com.technophobia.eclipse.preference.PreferenceLookup;
import com.technophobia.eclipse.preference.PreferenceLookupFactory;
import com.technophobia.eclipse.project.ProjectEventType;
import com.technophobia.eclipse.project.ProjectManager;
import com.technophobia.eclipse.project.ProjectObserver;
import com.technophobia.eclipse.project.PropertyBasedProjectManager;
import com.technophobia.eclipse.project.cache.CacheAwareProjectManager;
import com.technophobia.eclipse.supplier.ConstantSupplier;
import com.technophobia.eclipse.transformer.ResourceToProjectTransformer;
import com.technophobia.substeps.editor.preferences.ProjectLocalPreferenceStore;
import com.technophobia.substeps.event.SubstepsFolderChangedListener;
import com.technophobia.substeps.glossary.StepDescriptor;
import com.technophobia.substeps.model.Syntax;
import com.technophobia.substeps.nature.CheckProjectForSubstepsCompatibilityJob;
import com.technophobia.substeps.nature.CompatibilityChecker;
import com.technophobia.substeps.nature.SubstepsCompatibilityChecker;
import com.technophobia.substeps.preferences.SubstepsProjectPreferenceLookupFactory;
import com.technophobia.substeps.render.ParameterisedStepImplementationRenderer;
import com.technophobia.substeps.step.ContextualSuggestionManager;
import com.technophobia.substeps.step.ProjectStepDescriptorProvider;
import com.technophobia.substeps.step.ProjectStepImplementationLoader;
import com.technophobia.substeps.step.ProjectStepImplementationProvider;
import com.technophobia.substeps.step.ProjectSuggestionProvider;
import com.technophobia.substeps.step.ProvidedSuggestionManager;
import com.technophobia.substeps.step.SuggestionSource;
import com.technophobia.substeps.step.provider.ExternalStepImplementationProvider;
import com.technophobia.substeps.step.provider.ProjectSpecificSuggestionProvider;
import com.technophobia.substeps.step.provider.SubstepSuggestionProvider;
import com.technophobia.substeps.supplier.CachingResultTransformer;
import com.technophobia.substeps.supplier.Supplier;
import com.technophobia.substeps.supplier.Transformer;
import com.technophobia.substeps.syntax.CachingProjectToSyntaxTransformer;
/**
* BundleActivator/general bundle aware class for managing things such as
* logging, requesting the bundle context etc
*
* @author sforbes
*
*/
public class FeatureEditorPlugin extends AbstractUIPlugin implements BundleActivator, PluginLogger {
public static final String PLUGIN_ID = "com.technophobia.substeps.editor";
private static final String SUBSTEPS_FOLDER_CHANGED_EXTENSION_POINT_ID = "com.technophobia.substeps.editor.folderMonitorSubsteps";
private static FeatureEditorPlugin pluginInstance;
private BundleContext context;
private ResourceBundle resourceBundle;
private ILog log;
private ProvidedSuggestionManager suggestionManager;
private CachingResultTransformer<IProject, Syntax> projectToSyntaxTransformer;
private ProjectObserver projectObserver;
private final ProjectManager projectManager;
private final PreferenceLookupFactory<IProject> preferenceLookupFactory;
private final CompatibilityChecker<IProject> compatibilityChecker;
@SuppressWarnings("unchecked")
public FeatureEditorPlugin() {
super();
FeatureEditorPlugin.pluginInstance = this;
this.preferenceLookupFactory = new SubstepsProjectPreferenceLookupFactory(PLUGIN_ID,
(IPersistentPreferenceStore) getPreferenceStore());
this.projectManager = new PropertyBasedProjectManager(preferenceLookupFactory);
this.projectToSyntaxTransformer = new CachingProjectToSyntaxTransformer(projectManager, preferenceLookupFactory);
this.projectObserver = new CacheAwareProjectManager(projectToSyntaxTransformer);
this.compatibilityChecker = new SubstepsCompatibilityChecker(
new Transformer<IProject, IPersistentPreferenceStore>() {
@Override
public IPersistentPreferenceStore from(final IProject project) {
return new ProjectLocalPreferenceStore(PLUGIN_ID, project,
(IPersistentPreferenceStore) getPreferenceStore());
}
});
}
@Override
public void start(final BundleContext bundleContext) throws Exception {
context = bundleContext;
log = Platform.getLog(bundleContext.getBundle());
try {
resourceBundle = ResourceBundle.getBundle("com.technophobia.substeps.FeatureEditorResources");
} catch (final MissingResourceException x) {
resourceBundle = null;
}
projectObserver.registerFrameworkListeners();
addSuggestionProviders();
doProjectCompabitilityTesting(allOpenProjectsSupplier());
}
@Override
public void stop(final BundleContext bundleContext) throws Exception {
projectObserver.unregisterFrameworkListeners();
projectObserver = null;
suggestionManager = null;
projectToSyntaxTransformer = null;
resourceBundle = null;
log = null;
}
public BundleContext getBundleContext() {
return context;
}
public ResourceBundle getResourceBundle() {
return resourceBundle;
}
public ProjectManager projectManager() {
return projectManager;
}
public ContextualSuggestionManager getSuggestionManager() {
if (suggestionManager == null) {
suggestionManager = new ProvidedSuggestionManager(new ResourceToProjectTransformer());
// New suggestion providers means syntax should now have changed, re
// update it
refreshAllProjectSyntaxes();
}
return suggestionManager;
}
public ProjectObserver getProjectObserver() {
return projectObserver;
}
public Syntax syntaxFor(final IProject project) {
return projectToSyntaxTransformer.from(project);
}
public ProjectStepImplementationProvider getStepImplementationProvider() {
return (ProjectStepImplementationProvider) getSuggestionManager();
}
public PreferenceLookup preferenceLookupFor(final IProject project) {
return preferenceLookupFactory.preferencesFor(project);
}
public List<String> externalDependencyStepClasses(final IProject project) {
final Collection<ProjectSuggestionProvider> providers = ((ProvidedSuggestionManager) getSuggestionManager())
.providersOfSource(SuggestionSource.EXTERNAL_STEP_IMPLEMENTATION);
final List<String> stepClasses = new ArrayList<String>();
if (providers != null) {
for (final ProjectSuggestionProvider projectSuggestionProvider : providers) {
if (projectSuggestionProvider instanceof ProjectStepImplementationProvider) {
stepClasses.addAll(((ProjectStepImplementationProvider) projectSuggestionProvider)
.stepImplementationClasses(project));
}
}
}
return Collections.unmodifiableList(stepClasses);
}
public List<StepDescriptor> externalStepDescriptorsForClassInProject(final String className, final IProject project) {
final Collection<ProjectSuggestionProvider> providers = ((ProvidedSuggestionManager) getSuggestionManager())
.providersOfSource(SuggestionSource.EXTERNAL_STEP_IMPLEMENTATION);
final List<StepDescriptor> stepDescriptors = new ArrayList<StepDescriptor>();
if (providers != null) {
for (final ProjectSuggestionProvider projectSuggestionProvider : providers) {
if (projectSuggestionProvider instanceof ProjectStepDescriptorProvider) {
stepDescriptors.addAll(((ProjectStepDescriptorProvider) projectSuggestionProvider)
.stepDescriptorsFor(project, className));
}
}
}
return Collections.unmodifiableList(stepDescriptors);
}
public Supplier<IResource> currentResourceSupplier() {
return new Supplier<IResource>() {
@Override
public IResource get() {
final IEditorPart activeEditor = getWorkbench().getActiveWorkbenchWindow().getActivePage()
.getActiveEditor();
if (activeEditor != null) {
return (IResource) activeEditor.getEditorInput().getAdapter(IResource.class);
}
return null;
}
};
}
public Collection<SubstepsFolderChangedListener> substepsFolderChangeListeners() {
return createSubstepsFolderChangeListeners();
}
public void checkSubstepsCompatibilityFor(final IProject project) {
doProjectCompabitilityTesting(new ConstantSupplier<List<IProject>>(Collections.singletonList(project)));
}
@Override
public void info(final String msg) {
instance().log.log(new Status(IStatus.INFO, PLUGIN_ID, msg));
}
@Override
public void warn(final String msg) {
instance().log.log(new Status(IStatus.WARNING, PLUGIN_ID, msg));
}
@Override
public void warn(final String msg, final Throwable ex) {
instance().log.log(new Status(IStatus.WARNING, PLUGIN_ID, msg, ex));
}
@Override
public void error(final String msg) {
instance().log.log(new Status(IStatus.ERROR, PLUGIN_ID, msg));
}
@Override
public void error(final String msg, final Throwable t) {
instance().log.log(new Status(IStatus.ERROR, PLUGIN_ID, msg, t));
}
public static FeatureEditorPlugin instance() {
return pluginInstance;
}
private void addSuggestionProviders() {
final ProvidedSuggestionManager suggestions = (ProvidedSuggestionManager) getSuggestionManager();
final ExternalStepImplementationProvider externalSuggestionProvider = new ExternalStepImplementationProvider(
new ProjectStepImplementationLoader());
suggestions.addProvider(SuggestionSource.EXTERNAL_STEP_IMPLEMENTATION, externalSuggestionProvider);
final ProjectSpecificSuggestionProvider projectSpecificSuggestionProvider = new ProjectSpecificSuggestionProvider(
projectToSyntaxTransformer, new ParameterisedStepImplementationRenderer());
suggestions.addProvider(SuggestionSource.PROJECT_STEP_IMPLEMENTATION, projectSpecificSuggestionProvider);
final SubstepSuggestionProvider substepSuggestionProvider = new SubstepSuggestionProvider(
projectToSyntaxTransformer);
suggestions.addProvider(SuggestionSource.SUBSTEP_DEFINITION, substepSuggestionProvider);
projectObserver.addProjectListener(ProjectEventType.ProjectDependenciesChanged, externalSuggestionProvider);
projectObserver.addProjectListener(ProjectEventType.ProjectInserted, externalSuggestionProvider);
projectObserver.addProjectListener(ProjectEventType.ProjectRemoved, externalSuggestionProvider);
projectObserver.addProjectListener(ProjectEventType.ProjectConfigurationChanged, externalSuggestionProvider);
projectObserver.addProjectListener(ProjectEventType.ProjectConfigurationChanged,
projectSpecificSuggestionProvider);
projectObserver.addProjectListener(ProjectEventType.ProjectConfigurationChanged, substepSuggestionProvider);
projectObserver.addProjectListener(ProjectEventType.SourceFileAnnotationsChanged,
projectSpecificSuggestionProvider);
projectObserver.addSubstepsFileListener(substepSuggestionProvider);
suggestions.load(ResourcesPlugin.getWorkspace());
}
private void refreshAllProjectSyntaxes() {
final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (final IProject project : projects) {
projectToSyntaxTransformer.refreshCacheFor(project);
}
}
private Collection<SubstepsFolderChangedListener> createSubstepsFolderChangeListeners() {
final Collection<SubstepsFolderChangedListener> listeners = new ArrayList<SubstepsFolderChangedListener>();
final IConfigurationElement[] configurationElements = Platform.getExtensionRegistry()
.getConfigurationElementsFor(SUBSTEPS_FOLDER_CHANGED_EXTENSION_POINT_ID);
for (final IConfigurationElement configurationElement : configurationElements) {
final Object folderChangeListener = folderChangeListenerFor(configurationElement);
if (folderChangeListener != null && folderChangeListener instanceof SubstepsFolderChangedListener) {
listeners.add((SubstepsFolderChangedListener) folderChangeListener);
}
}
return Collections.unmodifiableCollection(listeners);
}
private Object folderChangeListenerFor(final IConfigurationElement configurationElement) {
try {
return configurationElement.createExecutableExtension("class");
} catch (final CoreException ex) {
error("Could not create executable extension for configuration element " + configurationElement.getName(),
ex);
return null;
}
}
private void doProjectCompabitilityTesting(final Supplier<List<IProject>> projectSupplier) {
final Job job = new CheckProjectForSubstepsCompatibilityJob(workbenchSupplier(), projectSupplier,
compatibilityChecker, projectToSyntaxTransformer);
job.setRule(ResourcesPlugin.getWorkspace().getRoot());
job.setPriority(Job.SHORT);
job.schedule(200);
}
private Supplier<List<IProject>> allOpenProjectsSupplier() {
return new Supplier<List<IProject>>() {
@Override
public List<IProject> get() {
final List<IProject> openProjects = new ArrayList<IProject>();
final IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (final IProject project : allProjects) {
if (project.isOpen()) {
openProjects.add(project);
}
}
return Collections.unmodifiableList(openProjects);
}
};
}
private Supplier<IWorkbench> workbenchSupplier() {
return new Supplier<IWorkbench>() {
@Override
public IWorkbench get() {
return getWorkbench();
}
};
}
}