/*******************************************************************************
* 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.editor;
import java.util.ResourceBundle;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.TextOperationAction;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import com.technophobia.eclipse.editor.FormattableEditorPart;
import com.technophobia.substeps.FeatureEditorPlugin;
import com.technophobia.substeps.colour.ColourManager;
import com.technophobia.substeps.document.content.ContentTypeDefinitionFactory;
import com.technophobia.substeps.document.content.assist.AutoActivatingContentAssistantDecorator;
import com.technophobia.substeps.document.content.assist.ContentAssistantFactory;
import com.technophobia.substeps.document.content.assist.ProcessedContentAssistantFactory;
import com.technophobia.substeps.document.content.assist.feature.StepImplementationProcessorSupplier;
import com.technophobia.substeps.document.content.feature.FeatureContentTypeDefinitionFactory;
import com.technophobia.substeps.document.content.partition.ContentTypeRuleBasedPartitionScannerFactory;
import com.technophobia.substeps.document.content.view.ContentTypeViewConfiguration;
import com.technophobia.substeps.document.formatting.FormattingContextFactory;
import com.technophobia.substeps.document.formatting.partition.PartitionedFormattingContextFactory;
import com.technophobia.substeps.document.partition.EditorInputPartitionContext;
import com.technophobia.substeps.document.partition.PartitionContext;
import com.technophobia.substeps.document.partition.PartitionScannedDocumentProvider;
import com.technophobia.substeps.editor.outline.OutlineLabelProvider;
import com.technophobia.substeps.editor.outline.SubstepsContentOutlinePage;
import com.technophobia.substeps.editor.outline.feature.FileToFeatureElementTransformer;
import com.technophobia.substeps.editor.outline.model.AbstractModelElement;
import com.technophobia.substeps.editor.outline.substeps.ProjectFile;
import com.technophobia.substeps.supplier.Callback1;
import com.technophobia.substeps.supplier.Supplier;
import com.technophobia.substeps.supplier.Transformer;
/**
* TextEditor whose text is configured to view as a feature file
*
* @author sforbes
*
*/
public class FeatureEditor extends TextEditor implements FormattableEditorPart, IPartListener2 {
private static final String SUBSTEPS_CONTEXT = "com.technophobia.substeps.editor.SubstepsContext";
private final ColourManager colourManager;
private IContextActivation currentActivateContext;
private SubstepsContentOutlinePage outlinePage;
private IEditorInput editorInput;
@SuppressWarnings("unchecked")
public FeatureEditor() {
final ContentTypeDefinitionFactory contentTypeDefinitionFactory = contentTypeDefinitionFactory();
final FormattingContextFactory formattingContextFactory = new PartitionedFormattingContextFactory(
contentTypeDefinitionFactory);
final ContentAssistantFactory contentAssistantFactory = new ProcessedContentAssistantFactory(
processorSupplier(), (Callback1<IContentAssistant>) new AutoActivatingContentAssistantDecorator());
colourManager = new ColourManager();
final Supplier<PartitionContext> partitionContextSupplier = partitionContextSupplier();
setSourceViewerConfiguration(new ContentTypeViewConfiguration(colourManager, contentTypeDefinitionFactory,
formattingContextFactory, contentAssistantFactory, partitionContextSupplier));
setDocumentProvider(new PartitionScannedDocumentProvider(new ContentTypeRuleBasedPartitionScannerFactory(
contentTypeDefinitionFactory), partitionContextSupplier));
}
@Override
public void doFormat() {
((SourceViewer) getSourceViewer()).doOperation(ISourceViewer.FORMAT);
}
@Override
public void doSave(final IProgressMonitor progressMonitor) {
super.doSave(progressMonitor);
FeatureEditorPlugin.instance().info("Saving file " + ((FileEditorInput) editorInput).getFile().getLocation());
if (outlinePage != null) {
outlinePage.update();
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(final Class required) {
if (IContentOutlinePage.class.equals(required)) {
if (outlinePage == null) {
outlinePage = new SubstepsContentOutlinePage(this, new OutlineLabelProvider(),
fileToModelTransformer(), documentOffsetToLineNumber());
if (getEditorInput() != null)
outlinePage.setInput(getEditorInput());
}
return outlinePage;
}
return super.getAdapter(required);
}
public IDocument getCurrentDocument() {
final PartitionScannedDocumentProvider docProvider = (PartitionScannedDocumentProvider) getDocumentProvider();
return docProvider.getDocuemnt();
}
@Override
public void partActivated(final IWorkbenchPartReference partRef) {
if (getEditorSite().getId().equals(partRef.getId())) {
activateContext();
}
}
@Override
public void partBroughtToTop(final IWorkbenchPartReference partRef) {
if (getEditorSite().getId().equals(partRef.getId())) {
activateContext();
}
}
@Override
public void partClosed(final IWorkbenchPartReference partRef) {
if (getEditorSite().getId().equals(partRef.getId())) {
deactivateContext();
}
}
@Override
public void partDeactivated(final IWorkbenchPartReference partRef) {
if (getEditorSite().getId().equals(partRef.getId())) {
deactivateContext();
}
}
@Override
public void partOpened(final IWorkbenchPartReference partRef) {
if (getEditorSite().getId().equals(partRef.getId())) {
activateContext();
}
}
@Override
public void partHidden(final IWorkbenchPartReference partRef) {
if (getEditorSite().getId().equals(partRef.getId())) {
deactivateContext();
}
}
@Override
public void partVisible(final IWorkbenchPartReference partRef) {
// beware you get LOADS of these, to the point where it doesn't really
// stop when running eclipse via eclipse (during plugin dev)
}
@Override
public void partInputChanged(final IWorkbenchPartReference partRef) {
// not used yet... ?
}
@Override
public void dispose() {
colourManager.dispose();
if (outlinePage != null) {
outlinePage.setInput(null);
}
final IPartService partService = (IPartService) this.getSite().getService(IPartService.class);
partService.removePartListener(this);
super.dispose();
}
@Override
protected void createActions() {
super.createActions();
final ResourceBundle resourceBundle = FeatureEditorPlugin.instance().getResourceBundle();
final TextOperationAction action = new TextOperationAction(resourceBundle, "ContentFormatProposal.", this,
ISourceViewer.FORMAT);
setAction("ContentFormatProposal", action);
getEditorSite().getActionBars().setGlobalActionHandler("ContentFormatProposal", action);
}
@Override
protected void doSetInput(final IEditorInput input) throws CoreException {
super.doSetInput(input);
this.editorInput = input;
}
/**
* {@inheritDoc}
*/
@Override
protected void setSite(final IWorkbenchPartSite site) {
super.setSite(site);
// add an activation listener
final IPartService partService = (IPartService) site.getService(IPartService.class);
partService.addPartListener(this);
}
/**
* Return a new {@link ContentTypeDefinitionFactory}
*
* @return
*/
protected ContentTypeDefinitionFactory contentTypeDefinitionFactory() {
return new FeatureContentTypeDefinitionFactory();
}
protected Transformer<ProjectFile, AbstractModelElement> fileToModelTransformer() {
return new FileToFeatureElementTransformer(lineNumberToDocumentOffset());
}
protected Transformer<Integer, Position> lineNumberToDocumentOffset() {
return new Transformer<Integer, Position>() {
@Override
public Position from(final Integer lineNumber) {
if (editorInput != null) {
try {
final IDocument document = getDocumentProvider().getDocument(editorInput);
return new Position(document.getLineOffset(lineNumber.intValue()));
} catch (final BadLocationException e) {
FeatureEditorPlugin.instance().info(
"Couldn't get offset for line " + lineNumber + " in document");
}
}
return new Position(0);
}
};
}
protected Transformer<Position, Integer> documentOffsetToLineNumber() {
return new Transformer<Position, Integer>() {
@Override
public Integer from(final Position offset) {
if (editorInput != null) {
try {
final IDocument document = getDocumentProvider().getDocument(editorInput);
return Integer.valueOf(document.getLineOfOffset(offset.getOffset()));
} catch (final BadLocationException e) {
FeatureEditorPlugin.instance().info(
"Couldn't get line number for offset " + offset + " in document");
}
}
return Integer.valueOf(-1);
}
};
}
/**
* Return a new {@link Supplier} of type {@link IContentAssistProcessor}
*
* @return
*/
private Supplier<IContentAssistProcessor> processorSupplier() {
return new Supplier<IContentAssistProcessor>() {
@Override
public IContentAssistProcessor get() {
return new StepImplementationProcessorSupplier(getSite(), FeatureEditorPlugin.instance()
.getSuggestionManager()).get();
}
};
}
private Supplier<PartitionContext> partitionContextSupplier() {
return new Supplier<PartitionContext>() {
@Override
public PartitionContext get() {
return new EditorInputPartitionContext(getEditorInput(), FeatureEditorPlugin.instance()
.getSuggestionManager());
}
};
}
private void activateContext() {
if (currentActivateContext == null) {
final IContextService contextService = (IContextService) this.getSite().getWorkbenchWindow()
.getService(IContextService.class);
currentActivateContext = contextService.activateContext(SUBSTEPS_CONTEXT);
}
// else we're already active
}
private void deactivateContext() {
if (currentActivateContext != null) {
final IContextService contextService = (IContextService) this.getSite().getWorkbenchWindow()
.getService(IContextService.class);
contextService.deactivateContext(currentActivateContext);
currentActivateContext = null;
}
}
}