org.eclipse.vex.ui.internal.editor.VexEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.vex.ui.internal.editor.VexEditor.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2014 John Krasnay 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:
 *     John Krasnay - initial API and implementation
 *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
 *     Carsten Hiesserich - added styleChanged event
 *     Carsten Hiesserich - remove listeners on dispose (bug 413878)
 *     Carsten Hiesserich - use JFaceDocument as intermediate between VexDocument
 *                          and filesystem
 *******************************************************************************/
package org.eclipse.vex.ui.internal.editor;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.editors.text.IStorageDocumentProvider;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.ui.services.ISourceProviderService;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.DocumentProviderRegistry;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IDocumentProviderExtension;
import org.eclipse.ui.texteditor.IDocumentProviderExtension3;
import org.eclipse.ui.texteditor.IElementStateListener;
import org.eclipse.ui.texteditor.ITextEditorExtension;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.IPropertySourceProvider;
import org.eclipse.ui.views.properties.PropertySheetPage;
import org.eclipse.vex.core.internal.core.ListenerList;
import org.eclipse.vex.core.internal.css.CssWhitespacePolicy;
import org.eclipse.vex.core.internal.dom.DocumentTextPosition;
import org.eclipse.vex.core.internal.io.DocumentReader;
import org.eclipse.vex.core.internal.io.DocumentWriter;
import org.eclipse.vex.core.internal.validator.WTPVEXValidator;
import org.eclipse.vex.core.internal.widget.swt.VexWidget;
import org.eclipse.vex.core.provisional.dom.AttributeChangeEvent;
import org.eclipse.vex.core.provisional.dom.BaseNodeVisitorWithResult;
import org.eclipse.vex.core.provisional.dom.ContentChangeEvent;
import org.eclipse.vex.core.provisional.dom.ContentPosition;
import org.eclipse.vex.core.provisional.dom.IComment;
import org.eclipse.vex.core.provisional.dom.IDocument;
import org.eclipse.vex.core.provisional.dom.IDocumentListener;
import org.eclipse.vex.core.provisional.dom.IElement;
import org.eclipse.vex.core.provisional.dom.IIncludeNode;
import org.eclipse.vex.core.provisional.dom.INode;
import org.eclipse.vex.core.provisional.dom.INodeVisitorWithResult;
import org.eclipse.vex.core.provisional.dom.IProcessingInstruction;
import org.eclipse.vex.core.provisional.dom.IValidator;
import org.eclipse.vex.core.provisional.dom.NamespaceDeclarationChangeEvent;
import org.eclipse.vex.ui.internal.Messages;
import org.eclipse.vex.ui.internal.VexPlugin;
import org.eclipse.vex.ui.internal.VexPreferences;
import org.eclipse.vex.ui.internal.config.ConfigEvent;
import org.eclipse.vex.ui.internal.config.ConfigurationRegistry;
import org.eclipse.vex.ui.internal.config.DocumentType;
import org.eclipse.vex.ui.internal.config.IConfigListener;
import org.eclipse.vex.ui.internal.config.Style;
import org.eclipse.vex.ui.internal.handlers.ConvertElementHandler;
import org.eclipse.vex.ui.internal.handlers.RemoveTagHandler;
import org.eclipse.vex.ui.internal.outline.DocumentOutlinePage;
import org.eclipse.vex.ui.internal.property.DocumentPropertySource;
import org.eclipse.vex.ui.internal.property.ElementPropertySource;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

/*
 * Vex uses a IDocumentProvider as an intermediate between the Vex document and the filesystem.
 * This way, we allow multiple editor instances for the same document (e.g. an XML editor together with Vex).
 *
 * When a new Vex Editor is opened:
 * - If the document is already open in another editor, Vex does not read the input file but synchronizes with
 *   the open document (see setInputFromProvider).
 * - If Vex is the first instance to open the document, we connect the EditorInput to a DocumentProvider, so
 *   other editor instances will use our unsaved document.
 *
 * Document synchronization:
 * - When Vex looses focus and the document is modified by Vex, the internal document is rewritten to the
 *   DocumentProvider (see synDocumentProvider).
 * - When Vex gets activated and the document has been changed externally, we reread the document from the
 *   DocumentProvider (see handleEditorInputChanged).
 */

/**
 * Editor for editing XML file using the VexWidget.
 */
public class VexEditor extends EditorPart {

    /**
     * ID of this editor extension.
     */
    public static final String ID = "org.eclipse.vex.ui.internal.editor.VexEditor"; //$NON-NLS-1$

    private final boolean debugging;
    private final ConfigurationRegistry configurationRegistry;
    private final VexPreferences preferences;

    private Composite parentControl;
    private Text loadingLabel;

    private boolean loaded;
    private DocumentType doctype;
    private IDocument document;
    private Style style;

    private VexWidget vexWidget;

    private boolean dirty;

    private final ListenerList<IVexEditorListener, VexEditorEvent> vexEditorListeners = new ListenerList<IVexEditorListener, VexEditorEvent>(
            IVexEditorListener.class);

    private final SelectionProvider selectionProvider = new SelectionProvider();

    private IDocumentProvider documentProvider;

    private ActivationListener activationListener;
    private IElementStateListener elementStateListener = new ElementStateListener();
    private final org.eclipse.jface.text.IDocumentListener jFaceDocumentListener = new JFaceDocumentListener();
    /** Indicates activation should be handled. */
    private boolean handleActivation = true;
    /** Indicates JFace Document change should be handled */
    private boolean handleDocumentChange = true;
    /** Tells whether this editor has been activated at least once. */
    private boolean hasBeenActivated;
    /** Indicates the document has been modified outside of VEX */
    private boolean externalModified = false;
    /** Indicates the document has been modified by VEX */
    private boolean internalModified = false;
    /** The modification stamp of the current document */
    private long docModificationStamp;

    private DocumentTextPosition positionOfCurrentNode = null;

    /**
     * Class constructor.
     */
    public VexEditor() {
        debugging = VexPlugin.getDefault().isDebugging()
                && "true".equalsIgnoreCase(Platform.getDebugOption(VexPlugin.ID + "/debug/layout")); //$NON-NLS-1$ //$NON-NLS-2$
        configurationRegistry = VexPlugin.getDefault().getConfigurationRegistry();
        preferences = VexPlugin.getDefault().getPreferences();
    }

    /**
     * Add a VexEditorListener to the notification list.
     *
     * @param listener
     *            VexEditorListener to be added.
     */
    public void addVexEditorListener(final IVexEditorListener listener) {
        vexEditorListeners.add(listener);
    }

    @Override
    public void dispose() {
        super.dispose();

        if (activationListener != null) {
            activationListener.dispose();
            activationListener = null;
        }

        if (document != null) {
            document.removeDocumentListener(documentListener);
        }

        getEditorSite().getSelectionProvider().removeSelectionChangedListener(selectionChangedListener);

        if (parentControl != null) {
            // createPartControl was called, so we must de-register from config
            // events
            configurationRegistry.removeConfigListener(configListener);
        }

        disposeDocumentProvider();
    }

    @Override
    public void doSave(final IProgressMonitor monitor) {

        final IDocumentProvider p = getDocumentProvider();
        if (p == null) {
            return;
        }

        if (p.isDeleted(getEditorInput())) {
            if (isSaveAsAllowed()) {
                performSaveAs(monitor);
            } else {
                final Shell shell = getSite().getShell();
                final String title = Messages.getString("VexEditor.docDeleted.canNotSave.title");
                final String msg = Messages.getString("VexEditor.docDeleted.canNotSave.title");
                MessageDialog.openError(shell, title, msg);
            }
        } else {
            performSave(false, monitor);
        }
    }

    /**
     * Performs the save and handles errors appropriately.
     *
     * @param overwrite
     *            indicates whether or not overwriting is allowed
     * @param progressMonitor
     *            the monitor in which to run the operation
     */
    protected void performSave(final boolean overwrite, final IProgressMonitor progressMonitor) {
        final IDocumentProvider provider = getDocumentProvider();
        if (provider == null) {
            return;
        }

        handleActivation = false;
        try {
            provider.aboutToChange(getEditorInput());
            syncDocumentProvider();
            final IEditorInput input = getEditorInput();
            provider.saveDocument(progressMonitor, input, getDocumentProvider().getDocument(input), overwrite);
        } catch (final CoreException ex) {
            final String title = Messages.getString("VexEditor.errorSaving.title"); //$NON-NLS-1$
            final String message = MessageFormat.format(Messages.getString("VexEditor.errorSaving.message"), //$NON-NLS-1$
                    getEditorInput().getName(), ex.getMessage());
            MessageDialog.openError(getEditorSite().getShell(), title, message);
            VexPlugin.getDefault().log(IStatus.ERROR, message, ex);
        } finally {
            provider.changed(getEditorInput());
            setClean();
            handleActivation = true;
        }
    }

    /**
     * Asks the user for the workspace path of a file resource and saves the document there.
     *
     * @param progressMonitor
     *            the monitor in which to run the operation
     */
    protected void performSaveAs(final IProgressMonitor progressMonitor) {
        final Shell shell = PlatformUI.getWorkbench().getModalDialogShellProvider().getShell();
        final IDocumentProvider provider = getDocumentProvider();
        final IEditorInput input = getEditorInput();
        final IEditorInput newInput;

        if (input instanceof IURIEditorInput && !(input instanceof IFileEditorInput)) {
            final FileDialog dialog = new FileDialog(shell, SWT.SAVE);
            final IPath oldPath = URIUtil.toPath(((IURIEditorInput) input).getURI());
            if (oldPath != null) {
                dialog.setFileName(oldPath.lastSegment());
                dialog.setFilterPath(oldPath.toOSString());
            }
            final String path = dialog.open();
            if (path == null) {
                if (progressMonitor != null) {
                    progressMonitor.setCanceled(true);
                }
                return;
            }

            // Check whether file exists and if so, confirm overwrite
            final File localFile = new File(path);
            if (localFile.exists()) {
                final String title = Messages.getString("VexEditor.saveAs.overwrite.title");
                final String message = MessageFormat
                        .format(Messages.getString("VexEditor.saveAs.overwrite.message"), path);
                final MessageDialog overwriteDialog = new MessageDialog(shell, title, null, message,
                        MessageDialog.WARNING,
                        new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 1); // 'No' is the default
                if (overwriteDialog.open() != Window.OK) {
                    if (progressMonitor != null) {
                        progressMonitor.setCanceled(true);
                        return;
                    }
                }
            }

            IFileStore fileStore;
            try {
                fileStore = EFS.getStore(localFile.toURI());
            } catch (final CoreException ex) {
                VexPlugin.getDefault().log(IStatus.ERROR, ex.getLocalizedMessage(), ex);
                final String title = Messages.getString("VexEditor.saveAs.error.title");
                final String msg = MessageFormat.format(Messages.getString("VexEditor.saveAs.error.message"),
                        ex.getLocalizedMessage());
                MessageDialog.openError(shell, title, msg);
                return;
            }

            final IFile file = getWorkspaceFile(fileStore);
            if (file != null) {
                newInput = new FileEditorInput(file);
            } else {
                newInput = new FileStoreEditorInput(fileStore);
            }
        } else {
            final SaveAsDialog dialog = new SaveAsDialog(shell);

            final IFile original = input instanceof IFileEditorInput ? ((IFileEditorInput) input).getFile() : null;
            if (original != null) {
                dialog.setOriginalFile(original);
            } else {
                dialog.setOriginalName(input.getName());
            }

            dialog.create();

            if (provider.isDeleted(input) && original != null) {
                final String msg = MessageFormat.format(Messages.getString("VexEditor.saveAs.deleted"),
                        original.getName());
                dialog.setErrorMessage(null);
                dialog.setMessage(msg, IMessageProvider.WARNING);
            }

            if (dialog.open() == Window.CANCEL) {
                if (progressMonitor != null) {
                    progressMonitor.setCanceled(true);
                }
                return;
            }

            final IPath filePath = dialog.getResult();
            if (filePath == null) {
                if (progressMonitor != null) {
                    progressMonitor.setCanceled(true);
                }
                return;
            }

            final IWorkspace workspace = ResourcesPlugin.getWorkspace();
            final IFile file = workspace.getRoot().getFile(filePath);
            newInput = new FileEditorInput(file);
        }

        if (provider == null) {
            // editor has programmatically been  closed while the dialog was open
            return;
        }

        boolean success = false;
        try {
            provider.aboutToChange(newInput);
            syncDocumentProvider();
            provider.saveDocument(progressMonitor, newInput, provider.getDocument(input), true);
            success = true;
        } catch (final CoreException ex) {
            final IStatus status = ex.getStatus();
            if (status == null || status.getSeverity() != IStatus.CANCEL) {
                VexPlugin.getDefault().log(IStatus.ERROR, ex.getLocalizedMessage(), ex);
                final String title = Messages.getString("VexEditor.saveAs.error.title");
                final String msg = MessageFormat.format(Messages.getString("VexEditor.saveAs.error.message"),
                        ex.getLocalizedMessage());
                MessageDialog.openError(shell, title, msg);
            }
        } finally {
            provider.changed(newInput);
            if (success) {
                setInput(newInput);
                setClean();
            }
        }

        if (progressMonitor != null) {
            progressMonitor.setCanceled(!success);
        }
    }

    private IFile getWorkspaceFile(final IFileStore fileStore) {
        final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
        final IFile[] files = workspaceRoot.findFilesForLocationURI(fileStore.toURI());
        if (files != null && files.length == 1) {
            return files[0];
        }
        return null;
    }

    /**
     * Write the current state of the document to the DocumentProvider if the document has been modfied by Vex.
     */
    private void syncDocumentProvider() {
        if (!loaded) {
            return;
        }

        final IDocumentProvider provider = getDocumentProvider();
        if (provider == null) {
            return;
        }

        handleDocumentChange = false; // Disable document change handling

        final org.eclipse.jface.text.IDocument jFaceDoc = getDocumentProvider().getDocument(getEditorInput());
        final org.eclipse.jface.text.IDocument doc;
        if (internalModified) {
            doc = jFaceDoc;
        } else {
            // Document is not modfied, so we don't touch it
            // The dummy document is used to store the caret position
            doc = new org.eclipse.jface.text.Document();
        }

        try {
            provider.aboutToChange(getEditorInput());

            if (positionOfCurrentNode != null) {
                jFaceDoc.removePosition(positionOfCurrentNode);
            }

            positionOfCurrentNode = createDocumentWriter().write(document, doc, vexWidget.getCurrentNode());
            positionOfCurrentNode.setOffsetInNode(vexWidget.getCaretPosition().getOffset()
                    - vexWidget.getCurrentNode().getStartPosition().getOffset());

            try {
                jFaceDoc.addPosition(positionOfCurrentNode);
            } catch (final BadLocationException e) {
                // That should not happen
                e.printStackTrace();
            }
        } finally {
            provider.changed(getEditorInput());
            internalModified = false;
            handleDocumentChange = true;
        }
    }

    private DocumentWriter createDocumentWriter() {
        final DocumentWriter result = new DocumentWriter();
        result.setWhitespacePolicy(vexWidget.getWhitespacePolicy());
        result.setIndent(preferences.getIndentationPattern());
        result.setWrapColumn(preferences.getLineWidth());
        return result;
    }

    @Override
    public void doSaveAs() {
        performSaveAs(getProgressMonitor());
    }

    /**
     * Returns the progress monitor related to this editor.
     *
     * @return the progress monitor related to this editor
     */
    protected IProgressMonitor getProgressMonitor() {
        IProgressMonitor pm = null;
        final IStatusLineManager manager = getEditorSite().getActionBars().getStatusLineManager();
        ;
        if (manager != null) {
            pm = manager.getProgressMonitor();
        }
        return pm != null ? pm : new NullProgressMonitor();
    }

    /**
     * Returns the DocumentType associated with this editor.
     */
    public DocumentType getDocumentType() {
        return doctype;
    }

    /**
     * Returns the Style currently associated with the editor. May be null.
     */
    public Style getStyle() {
        return style;
    }

    /**
     * Returns the VexWidget that implements this editor.
     */
    public VexWidget getVexWidget() {
        return vexWidget;
    }

    public void gotoMarker(final IMarker marker) {
        // TODO Auto-generated method stub

    }

    @Override
    public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {

        setSite(site);
        setInput(input);

        getEditorSite().setSelectionProvider(selectionProvider);
        getEditorSite().getSelectionProvider().addSelectionChangedListener(selectionChangedListener);

        activationListener = new ActivationListener(site.getWorkbenchWindow().getPartService());
    }

    /**
     * @see ITextEditorExtension#isEditorInputReadOnly()
     */
    public boolean isEditorInputReadOnly() {
        final IDocumentProvider provider = getDocumentProvider();
        if (provider instanceof IDocumentProviderExtension) {
            final IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
            return extension.isReadOnly(getEditorInput());
        }
        return true;
    }

    private void setDirty() {
        internalModified = true;

        if (dirty) {
            return;
        }
        dirty = true;
        firePropertyChange(PROP_DIRTY);

        makeJFaceDocumentDirty();
    }

    /**
     * Mark the document in the DocumentProvider dirty.
     */
    private void makeJFaceDocumentDirty() {
        // Replace a part of the JFace document to mark it dirty. This is a simple hack.
        // The best behavior would be to directly modify the JFace document with the changes
        // made in VEX.
        final org.eclipse.jface.text.IDocument doc = getDocumentProvider().getDocument(getEditorInput());
        try {
            handleDocumentChange = false; // Disable change handling
            doc.replace(0, 1, doc.get(0, 1));
        } catch (final BadLocationException e) {
            e.printStackTrace();
        } finally {
            handleDocumentChange = true;
        }
    }

    private void setClean() {
        if (!dirty) {
            return;
        }

        dirty = false;
        firePropertyChange(PROP_DIRTY);
    }

    @Override
    public boolean isDirty() {
        return dirty;
    }

    /**
     * Returns true if this editor has finished loading its document.
     */
    public boolean isLoaded() {
        return loaded;
    }

    @Override
    public boolean isSaveAsAllowed() {
        return true;
    }

    @Override
    public boolean isSaveOnCloseNeeded() {
        // do not save if there is no document loaded
        // this method is not called in some situations. see bug Bug 411465
        // as a workaround we call setClean when no document is loaded.
        return isLoaded() && isDirty();
    }

    @Override
    public void createPartControl(final Composite parent) {
        parentControl = parent;

        configurationRegistry.addConfigListener(configListener);
        if (configurationRegistry.isLoaded()) {
            setInputFromProvider();
        } else {
            showLabel(Messages.getString("VexEditor.loading")); //$NON-NLS-1$
        }
    }

    /**
     * Remove a VexEditorListener from the notification list.
     *
     * @param listener
     *            VexEditorListener to be removed.
     */
    public void removeVexEditorListener(final IVexEditorListener listener) {
        vexEditorListeners.remove(listener);
    }

    @Override
    public void setFocus() {
        if (vexWidget != null) {
            vexWidget.setFocus();
            setStatus(getLocationPath());
        }
    }

    /**
     * Link the given IEditorInput with the IDocumentProvider. To make the new input available to Vex, the document has
     * to be loaded from the provider. See {@link #setInputFromProvider()}
     */
    @Override
    protected void setInput(final IEditorInput input) {
        final IEditorInput oldInput = getEditorInput();
        if (oldInput != null) {
            getDocumentProvider().getDocument(oldInput).removeDocumentListener(jFaceDocumentListener);
            getDocumentProvider().disconnect(oldInput);
        }

        super.setInput(input);

        setPartName(input.getName());
        setContentDescription(input.getName()); // Content description is displayed in the top line of the view

        updateDocumentProvider(input);
        final IDocumentProvider provider = getDocumentProvider();
        if (provider == null) {
            final String msg = MessageFormat.format(Messages.getString("VexEditor.noProvider"), //$NON-NLS-1$
                    new Object[] { input.getClass() });
            showLabel(msg);
            return;
        }

        try {
            provider.connect(input);
        } catch (final CoreException e) {
            e.printStackTrace();
        }

        provider.getDocument(input).addDocumentListener(jFaceDocumentListener);
    }

    /**
     * Read the input from the document provider and create the Vex-Document.
     */
    protected void setInputFromProvider() {

        loaded = false;
        dirty = false; // Mark the document clean, to prevent the 'Save...' dialog when loading fails

        final IDocumentProvider provider = getDocumentProvider();

        IValidator validator = null;
        VexDocumentContentModel documentContentModel;

        try {
            if (vexWidget != null) {
                vexEditorListeners.fireEvent("documentUnloaded", new VexEditorEvent(this)); //$NON-NLS-1$
            }
            if (document != null) {
                document.removeDocumentListener(documentListener);
                validator = document.getValidator();
            }

            // Reuse the validator from current document
            if (validator != null) {
                documentContentModel = (VexDocumentContentModel) validator.getDocumentContentModel();
            } else {
                documentContentModel = new VexDocumentContentModel(getSite().getShell());
                validator = new WTPVEXValidator(documentContentModel);
            }

            final DocumentReader reader = new DocumentReader();
            reader.setDebugging(debugging);
            reader.setValidator(validator);
            reader.setStyleSheetProvider(VexPlugin.getDefault().getPreferences());
            reader.setWhitespacePolicyFactory(CssWhitespacePolicy.FACTORY);

            final org.eclipse.jface.text.IDocument jFaceDoc = provider.getDocument(getEditorInput());

            if (provider instanceof IDocumentProviderExtension) {
                final IDocumentProviderExtension extension = (IDocumentProviderExtension) getDocumentProvider();
                final IStatus status = extension.getStatus(getEditorInput());
                if (status != null && status.getSeverity() == IStatus.ERROR) {
                    // There's something wrong with the input. Show the message.
                    // Typically this happes when the input is not in sync with the file system
                    showLabel(status.getMessage());
                    return;
                }
            }

            String encoding;
            if (provider instanceof IStorageDocumentProvider) {
                // Try to get the encoding from the DocumentProvider
                encoding = ((IStorageDocumentProvider) provider).getEncoding(getEditorInput());
            } else {
                encoding = "UTF-8";
            }

            // A Reader is used here to avoid an in memory copy of the documents content
            final InputSource is = new InputSource(new DocumentInputReader(jFaceDoc));
            is.setEncoding(encoding);

            // Set the systemId of the InputSource to resolve relative URIs
            final IEditorInput input = getEditorInput();
            if (input instanceof IFileEditorInput) {
                final IFile file = ((IFileEditorInput) input).getFile();
                is.setSystemId(file.getLocationURI().toString());
            } else if (input instanceof IStorageEditorInput) {
                final IStorage storage = ((IStorageEditorInput) input).getStorage();
                is.setSystemId(storage.getFullPath().toString());
            } else if (input instanceof IURIEditorInput) {
                final URI uri = ((IURIEditorInput) input).getURI();
                is.setSystemId(uri.toString());
            } else {
                final String msg = MessageFormat.format(Messages.getString("VexEditor.unknownInputClass"), //$NON-NLS-1$
                        new Object[] { input.getClass() });
                showLabel(msg);
                return;
            }

            if (positionOfCurrentNode != null) {
                positionOfCurrentNode.computePosition(jFaceDoc);
                reader.setCaretPosition(positionOfCurrentNode);
            }
            document = reader.read(is);
            if (document == null) {
                showLabel(MessageFormat.format(Messages.getString("VexEditor.noContent"),
                        getEditorInput().getName()));
                return;
            }

            doctype = documentContentModel.getDocumentType();
            style = documentContentModel.getStyle();
            // The document reader uses the style sheet before the document is completely loaded
            // This results in imcomplete styles in the cache
            style.getStyleSheet().flushAllStyles(document);

            document.setValidator(validator);

            if (documentContentModel.shouldAssignInferredDocumentType()) {
                // The user has selected to apply the selected DocType to the document
                // FIXME This does currently work for DTD's only and fails for namespaces
                document.setPublicID(doctype.getPublicId());
                document.setSystemID(doctype.getSystemId());
                internalModified = true;
                syncDocumentProvider();
                setDirty();
            }

            showVexWidget();

            document.addDocumentListener(documentListener);

            vexWidget.setDebugging(debugging);
            vexWidget.setWhitespacePolicy(reader.getWhitespacePolicy());
            vexWidget.setDocument(document, style.getStyleSheet());
            vexWidget.setReadOnly(isEditorInputReadOnly());

            final INode nodeAtCaret = reader.getNodeAtCaret();
            if (nodeAtCaret != null) {
                final int offsetInNode = Math.min(
                        nodeAtCaret.getStartOffset() + positionOfCurrentNode.getOffsetInNode(),
                        nodeAtCaret.getEndOffset());
                vexWidget.moveTo(new ContentPosition(null, offsetInNode));
            }

            loaded = true;
            // Check if the loaded document is in sync with the filesystem
            if (provider.canSaveDocument(getEditorInput())) {
                dirty = true;
            }
            vexEditorListeners.fireEvent("documentLoaded", new VexEditorEvent(this)); //$NON-NLS-1$
        } catch (final SAXParseException ex) {
            if (ex.getException() instanceof NoRegisteredDoctypeException) {
                // TODO doc did not have document type and the user
                // declined to select another one. Should fail silently.
                String msg;
                final NoRegisteredDoctypeException ex2 = (NoRegisteredDoctypeException) ex.getException();
                if (ex2.getPublicId() == null) {
                    msg = Messages.getString("VexEditor.noDoctype"); //$NON-NLS-1$
                } else {
                    msg = MessageFormat.format(Messages.getString("VexEditor.unknownDoctype"), ex2.getPublicId()); //$NON-NLS-1$
                }
                showLabel(msg);
            } else if (ex.getException() instanceof NoStyleForDoctypeException) {
                final String msg = MessageFormat.format(Messages.getString("VexEditor.noStyles"), //$NON-NLS-1$
                        doctype.getPublicId());
                showLabel(msg);
            } else {
                String file = ex.getSystemId();
                if (file == null) {
                    file = getEditorInput().getName();
                }
                final String msg = MessageFormat.format(Messages.getString("VexEditor.parseError"), //$NON-NLS-1$
                        Integer.valueOf(ex.getLineNumber()), file, ex.getLocalizedMessage());
                showLabel(msg);
                VexPlugin.getDefault().log(IStatus.ERROR, msg, ex);
            }
        } catch (final NoRegisteredDoctypeException ex) {
            if (ex.isUserCanceled()) {
                PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(this, true);
            } else {
                final String msg = MessageFormat.format(Messages.getString("VexEditor.unexpectedError"), //$NON-NLS-1$
                        getEditorInput().getName());
                VexPlugin.getDefault().log(IStatus.ERROR, msg, ex);
                showLabel(msg, ex);
            }
        } catch (final Exception ex) {
            final String msg = MessageFormat.format(Messages.getString("VexEditor.unexpectedError"), //$NON-NLS-1$
                    getEditorInput().getName());
            VexPlugin.getDefault().log(IStatus.ERROR, msg, ex);
            showLabel(msg, ex);
        }

    }

    public void setStatus(final String text) {
        // this.statusLabel.setText(text);
        getEditorSite().getActionBars().getStatusLineManager().setMessage(text);
    }

    /**
     * Sets the style for this editor.
     *
     * @param style
     *            Style to use.
     */
    public void setStyle(final Style style) {
        this.style = style;
        if (vexWidget != null) {
            vexWidget.setStyleSheet(style.getStyleSheet());
            preferences.setPreferredStyleId(doctype, style.getUniqueId());
        }
        vexEditorListeners.fireEvent("styleChanged", new VexEditorEvent(this)); //$NON-NLS-1$
    }

    /**
     * Dispose the VexWidget and display a message instead.
     *
     * @param message
     *            The message to display.
     * @param ex
     *            The Exception to display
     */
    private void showLabel(final String message, final Exception ex) {
        if (loadingLabel == null) {
            if (vexWidget != null) {
                vexWidget.dispose();
                vexWidget = null;
            }
            final GridLayout layout = new GridLayout();
            layout.numColumns = 1;
            layout.verticalSpacing = 0;
            layout.marginHeight = 0;
            layout.marginWidth = 0;
            parentControl.setLayout(layout);

            loadingLabel = new Text(parentControl, SWT.WRAP | SWT.V_SCROLL);
            loadingLabel.setEditable(false);
            final GridData gd = new GridData();
            gd.grabExcessHorizontalSpace = true;
            gd.grabExcessVerticalSpace = true;
            gd.horizontalAlignment = GridData.FILL;
            gd.verticalAlignment = GridData.FILL;
            loadingLabel.setLayoutData(gd);
        }
        final StringWriter sw = new StringWriter();
        sw.append(message);
        if (ex != null) {
            sw.append("\n\n");
            ex.printStackTrace(new PrintWriter(sw));
        }
        loadingLabel.setText(sw.toString());
        parentControl.layout(true);
    }

    /**
     * Dispose the VexWidget and display a message instead.
     *
     * @param message
     *            The message to display.
     */
    private void showLabel(final String message) {
        showLabel(message, null);
    }

    private void showVexWidget() {

        if (vexWidget != null) {
            return;
        }

        if (loadingLabel != null) {
            loadingLabel.dispose();
            loadingLabel = null;
        }

        final GridLayout layout = new GridLayout();
        layout.numColumns = 1;
        layout.verticalSpacing = 0;
        layout.marginHeight = 0;
        layout.marginWidth = 0;
        parentControl.setLayout(layout);
        GridData gd;

        // StatusPanel statusPanel = new StatusPanel(this.parentControl);

        // Composite statusPanel = new Composite(this.parentControl, SWT.NONE);
        // statusPanel.setLayout(new GridLayout());
        // gd = new GridData();
        // gd.grabExcessHorizontalSpace = true;
        // gd.horizontalAlignment = GridData.FILL;
        // statusPanel.setLayoutData(gd);

        // this.statusLabel = new Label(statusPanel, SWT.NONE);
        // gd = new GridData();
        // gd.grabExcessHorizontalSpace = true;
        // gd.horizontalAlignment = GridData.FILL;
        // this.statusLabel.setLayoutData(gd);

        gd = new GridData();
        gd.grabExcessHorizontalSpace = true;
        gd.grabExcessVerticalSpace = true;
        gd.horizontalAlignment = GridData.FILL;
        gd.verticalAlignment = GridData.FILL;

        vexWidget = new VexWidget(parentControl, SWT.V_SCROLL);
        gd = new GridData();
        gd.grabExcessHorizontalSpace = true;
        gd.grabExcessVerticalSpace = true;
        gd.horizontalAlignment = GridData.FILL;
        gd.verticalAlignment = GridData.FILL;
        vexWidget.setLayoutData(gd);

        final MenuManager menuManager = new MenuManager();
        getSite().registerContextMenu("org.eclipse.vex.ui.popup", menuManager, vexWidget);
        vexWidget.setMenu(menuManager.createContextMenu(vexWidget));

        setClean();

        // new for scopes
        final IContextService cs = (IContextService) getSite().getService(IContextService.class);
        cs.activateContext("org.eclipse.vex.ui.VexEditorContext");

        vexWidget.addSelectionChangedListener(selectionProvider);

        parentControl.layout(true);

    }

    // Listen for stylesheet changes and respond appropriately
    private final IConfigListener configListener = new IConfigListener() {
        @Override
        public void configChanged(final ConfigEvent e) {
            Display.getDefault().asyncExec(new Runnable() {
                @Override
                public void run() {
                    if (style == null) {
                        return;
                    }

                    final String styleId = style.getUniqueId();
                    final Style newStyle = configurationRegistry.getStyle(styleId);
                    if (newStyle == null) {
                        // Oops, style went bye-bye
                        // Let's just hold on to it in case it comes back later
                    } else {
                        vexWidget.setStyleSheet(newStyle.getStyleSheet());
                        style = newStyle;
                    }
                }
            });
        }

        @Override
        public void configLoaded(final ConfigEvent e) {
            Display.getDefault().asyncExec(new Runnable() {
                @Override
                public void run() {
                    setInputFromProvider();
                }
            });
        }
    };

    private final ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() {
        @Override
        public void selectionChanged(final SelectionChangedEvent event) {
            setStatus(getLocationPath());

            // update dynamic UI element labels
            final IEditorSite editorSite = VexEditor.this.getEditorSite();
            final IWorkbenchWindow window = editorSite.getWorkbenchWindow();
            if (window instanceof IServiceLocator) {
                final IServiceLocator serviceLocator = window;
                final ICommandService commandService = (ICommandService) serviceLocator
                        .getService(ICommandService.class);
                commandService.refreshElements(ConvertElementHandler.COMMAND_ID, null);
                commandService.refreshElements(RemoveTagHandler.COMMAND_ID, null);
            }

            // update context service
            final ISourceProviderService service = (ISourceProviderService) window
                    .getService(ISourceProviderService.class);
            final DocumentContextSourceProvider contextProvider = (DocumentContextSourceProvider) service
                    .getSourceProvider(DocumentContextSourceProvider.IS_COLUMN);
            contextProvider.fireUpdate(vexWidget);
        }
    };

    private final IDocumentListener documentListener = new IDocumentListener() {

        @Override
        public void attributeChanged(final AttributeChangeEvent e) {
            setDirty();
        }

        @Override
        public void namespaceChanged(final NamespaceDeclarationChangeEvent e) {
            setDirty();
        }

        @Override
        public void beforeContentDeleted(final ContentChangeEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void beforeContentInserted(final ContentChangeEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void contentDeleted(final ContentChangeEvent e) {
            setDirty();
        }

        @Override
        public void contentInserted(final ContentChangeEvent e) {
            setDirty();
        }

    };

    /**
     * Visitor to return the path elements displayed in the status line
     */
    private final INodeVisitorWithResult<String> nodePathVisitor = new BaseNodeVisitorWithResult<String>("") {
        @Override
        public String visit(final IElement element) {
            return element.getPrefixedName();
        };

        @Override
        public String visit(final IProcessingInstruction pi) {
            return Messages.getString("VexEditor.Path.ProcessingInstruction");
        };

        @Override
        public String visit(final IComment comment) {
            return Messages.getString("VexEditor.Path.Comment");
        };

        @Override
        public String visit(final IIncludeNode include) {
            return include.getReference().getPrefixedName();
        }
    };

    private String getLocationPath() {
        final List<String> path = new ArrayList<String>();
        INode node = vexWidget.getCurrentNode();
        while (node != null) {
            path.add(node.accept(nodePathVisitor));
            node = node.getParent();
        }
        Collections.reverse(path);

        if (path.isEmpty()) {
            return "/";
        }

        final StringBuilder pathString = new StringBuilder(path.size() * 15);
        for (final String part : path) {
            if (!part.isEmpty()) {
                pathString.append('/');
                pathString.append(part);
            }
        }
        return pathString.toString();
    }

    @Override
    public Object getAdapter(@SuppressWarnings("rawtypes") final Class adapter) {
        if (adapter == IContentOutlinePage.class) {
            return new DocumentOutlinePage(this);
        } else if (adapter == IPropertySheetPage.class) {
            final PropertySheetPage page = new PropertySheetPage();
            page.setPropertySourceProvider(new IPropertySourceProvider() {
                @Override
                public IPropertySource getPropertySource(final Object object) {
                    if (object instanceof IElement) {
                        final IStructuredSelection selection = (IStructuredSelection) vexWidget.getSelection();
                        final boolean multipleElementsSelected = selection != null && selection.size() > 1;
                        final IValidator validator = vexWidget.getDocument().getValidator();
                        return new ElementPropertySource((IElement) object, validator, multipleElementsSelected);
                    }
                    if (object instanceof IIncludeNode) {
                        final IStructuredSelection selection = (IStructuredSelection) vexWidget.getSelection();
                        final boolean multipleElementsSelected = selection != null && selection.size() > 1;
                        final IValidator validator = vexWidget.getDocument().getValidator();
                        return new ElementPropertySource(((IIncludeNode) object).getReference(), validator,
                                multipleElementsSelected);
                    }
                    if (object instanceof IDocument) {
                        return new DocumentPropertySource((IDocument) object);
                    }
                    return null;
                }
            });
            return page;
        } else if (adapter == IFindReplaceTarget.class) {
            return new AbstractRegExFindReplaceTarget() {

                @Override
                protected int getSelectionStart() {
                    return getVexWidget().getSelectedRange().getStartOffset();
                }

                @Override
                protected int getSelectionEnd() {
                    return getVexWidget().getSelectedRange().getEndOffset();
                }

                @Override
                protected void setSelection(final int start, final int end) {
                    getVexWidget().moveTo(new ContentPosition(document, start));
                    getVexWidget().moveTo(new ContentPosition(document, end), true);
                }

                @Override
                protected CharSequence getDocument() {
                    return document.getContent();
                }

                @Override
                protected void inDocumentReplaceSelection(final CharSequence text) {
                    final VexWidget vexWidget = getVexWidget();

                    // because of Undo this action must be atomic
                    vexWidget.beginWork();
                    try {
                        vexWidget.deleteSelection();
                        vexWidget.insertText(text.toString());
                    } finally {
                        vexWidget.endWork(true);
                    }
                }

            };
        } else {
            return super.getAdapter(adapter);
        }
    }

    private class ElementStateListener implements IElementStateListener {

        private Display display;

        @Override
        public void elementDirtyStateChanged(final Object element, final boolean isDirty) {
            if (element != null && element.equals(getEditorInput()) && dirty != isDirty) {
                final Runnable r = new Runnable() {
                    @Override
                    public void run() {
                        handleActivation = true;
                        if (isDirty) {
                            // Do not use setDirty() here, as this method will change the jFaceDocument to mark
                            // is also as dirty. With multiple Vex editors open, that would trigger an infinite loop.
                            dirty = true;
                            firePropertyChange(PROP_DIRTY);
                        } else {
                            setClean();
                        }
                    }
                };
                execute(r);
            }
        }

        @Override
        public void elementContentAboutToBeReplaced(final Object element) {

        }

        @Override
        public void elementContentReplaced(final Object element) {

        }

        @Override
        public void elementDeleted(final Object element) {
            // The default behavior of Eclipse editors is to simply close when the document is deleted
            getSite().getPage().closeEditor(VexEditor.this, false);
        }

        @Override
        public void elementMoved(final Object originalElement, final Object movedElement) {
            if (originalElement != null && originalElement.equals(getEditorInput())) {

                // TODO: The undo/redo history gets lost during the reload
                final Runnable r = new Runnable() {
                    @Override
                    public void run() {
                        if (movedElement == null || movedElement instanceof IEditorInput) {

                            // Store the current content if it is modified by VEX
                            final IDocumentProvider provider = getDocumentProvider();
                            final String previousContent;
                            if (internalModified) {
                                syncDocumentProvider();
                                previousContent = provider.getDocument(getEditorInput()).get();
                            } else {
                                previousContent = null;
                            }

                            setInput((IEditorInput) movedElement);

                            // Apply the stored changes to the new element
                            if (previousContent != null) {
                                getDocumentProvider().getDocument(getEditorInput()).set(previousContent);
                            }

                        }
                    }
                };
                execute(r);

            }
        }

        /**
         * Executes the given runnable in the UI thread.
         *
         * @param runnable
         *            runnable to be executed
         */
        private void execute(final Runnable runnable) {
            if (Display.getCurrent() == null) {
                if (display == null) {
                    display = getSite().getShell().getDisplay();
                }
                display.asyncExec(runnable);
            } else {
                runnable.run();
            }
        }

    }

    private class JFaceDocumentListener implements org.eclipse.jface.text.IDocumentListener {

        @Override
        public void documentAboutToBeChanged(final DocumentEvent event) {
        }

        @Override
        public void documentChanged(final DocumentEvent event) {

            if (handleDocumentChange) {
                externalModified = true;
                // Do not use setDirty() here, as that would mark as internalModfied
                if (!dirty) {
                    dirty = true;
                    firePropertyChange(PROP_DIRTY);
                }
            }
        }
    }

    /**
     * Handles the activation of this editor.
     */
    private void handleEditorActivated() {
        final IDocumentProvider p = getDocumentProvider();
        if (p == null) {
            return;
        }

        final boolean fileChange = checkDocumentState();
        if (fileChange && hasBeenActivated) {
            handleEditorInputChanged();
        } else if (externalModified) {
            setInputFromProvider();
        }
        externalModified = false;
    }

    /**
     * Handle an external change of the editor's input
     */
    private void handleEditorInputChanged() {
        final IDocumentProvider provider = getDocumentProvider();
        final IEditorInput input = getEditorInput();

        if (provider.isDeleted(input)) {
            final String title = Messages.getString("VexEditor.docDeleted.title");
            final String message = MessageFormat.format(Messages.getString("VexEditor.docDeleted.message"), //$NON-NLS-1$
                    input.getName());
            final String[] buttons = { Messages.getString("VexEditor.docDeleted.save"),
                    Messages.getString("VexEditor.docDeleted.discard") };
            final MessageDialog dialog = new MessageDialog(getSite().getShell(), title, null, message,
                    MessageDialog.QUESTION, buttons, 0);

            if (dialog.open() == 0) {
                final IProgressMonitor pm = getProgressMonitor();
                performSaveAs(pm);
                if (pm.isCanceled()) {
                    handleEditorInputChanged();
                }
            } else {
                getEditorSite().getPage().closeEditor(this, false);
            }

        } else {
            final String title = Messages.getString("VexEditor.docChanged.title");
            final String message = MessageFormat.format(Messages.getString("VexEditor.docChanged.message"), //$NON-NLS-1$
                    getEditorInput().getName());

            // Ask the user if the document should be reloaded from the modfied input
            if (MessageDialog.openQuestion(getSite().getShell(), title, message)) {
                if (provider instanceof IDocumentProviderExtension) {
                    final IDocumentProviderExtension extension = (IDocumentProviderExtension) provider;
                    try {
                        extension.synchronize(input);
                    } catch (final CoreException e) {
                        e.printStackTrace();
                    }
                }
                setInputFromProvider();
            } else if (!isDirty()) {
                // Trigger dummy change to dirty the editor.
                try {
                    final org.eclipse.jface.text.IDocument document = provider.getDocument(input);
                    if (document != null) {
                        document.replace(0, 0, ""); //$NON-NLS-1$
                    }
                } catch (final BadLocationException e) {
                    // Ignore as this can't happen
                }
            }
        }
    }

    /**
     * Checks the state of the current document against the filesystem.
     *
     * @return <code>true</code> if the document has been modified on the filesystem
     */
    private boolean checkDocumentState() {

        final IDocumentProvider p = getDocumentProvider();
        if (p == null) {
            return false;
        }

        final IEditorInput input = getEditorInput();

        if (p instanceof IDocumentProviderExtension3) {

            final IDocumentProviderExtension3 p3 = (IDocumentProviderExtension3) p;

            final long stamp = p.getModificationStamp(input);
            if (stamp != docModificationStamp) {
                docModificationStamp = stamp;
                if (!p3.isSynchronized(input)) {
                    return true;
                }
            }
        } else {
            if (docModificationStamp == -1) {
                docModificationStamp = p.getSynchronizationStamp(input);
            }

            final long stamp = p.getModificationStamp(input);
            if (stamp != docModificationStamp) {
                docModificationStamp = stamp;
                if (stamp != p.getSynchronizationStamp(input)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Internal part and shell activation listener for triggering state validation.<br />
     * This class is copied from {@link org.eclipse.ui.texteditor.AbstractTextEditor}
     */
    class ActivationListener implements IPartListener, IWindowListener {

        /** Cache of the active workbench part. */
        private IWorkbenchPart fActivePart;
        /** The part service. */
        private IPartService fPartService;

        /**
         * Creates this activation listener.
         *
         * @param partService
         *            the part service on which to add the part listener
         */
        public ActivationListener(final IPartService partService) {
            fPartService = partService;
            fPartService.addPartListener(this);
            PlatformUI.getWorkbench().addWindowListener(this);
        }

        /**
         * Disposes this activation listener.
         */
        public void dispose() {
            fPartService.removePartListener(this);
            PlatformUI.getWorkbench().removeWindowListener(this);
            fPartService = null;
        }

        /*
         * @see IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
         */
        @Override
        public void partActivated(final IWorkbenchPart part) {
            fActivePart = part;
            handleActivation();
        }

        /*
         * @see IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
         */
        @Override
        public void partBroughtToTop(final IWorkbenchPart part) {
        }

        /*
         * @see IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
         */
        @Override
        public void partClosed(final IWorkbenchPart part) {
        }

        /*
         * @see IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
         */
        @Override
        public void partDeactivated(final IWorkbenchPart part) {
            if (fActivePart == VexEditor.this
                    || fActivePart != null && fActivePart.getAdapter(AbstractTextEditor.class) == VexEditor.this) {
                syncDocumentProvider();
            }
            fActivePart = null;
        }

        /*
         * @see IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
         */
        @Override
        public void partOpened(final IWorkbenchPart part) {

        }

        /**
         * Handles the activation triggering a element state check in the editor.
         */
        private void handleActivation() {
            if (!handleActivation) {
                return;
            }

            if (fActivePart == VexEditor.this
                    || fActivePart != null && fActivePart.getAdapter(AbstractTextEditor.class) == VexEditor.this) {
                handleActivation = false;
                try {
                    handleEditorActivated();
                } finally {
                    handleActivation = true;
                    hasBeenActivated = true;
                }
            }
        }

        /*
         * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
         */
        @Override
        public void windowActivated(final IWorkbenchWindow window) {
            if (handleActivation && window == getEditorSite().getWorkbenchWindow()) {
                /*
                 * Workaround for problem described in http://dev.eclipse.org/bugs/show_bug.cgi?id=11731 Will be removed
                 * when SWT has solved the problem.
                 */
                window.getShell().getDisplay().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        handleActivation();
                    }
                });
            }
        }

        /*
         * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
         */
        @Override
        public void windowDeactivated(final IWorkbenchWindow window) {
            if (window == getEditorSite().getWorkbenchWindow()) {
                syncDocumentProvider();
            }
        }

        /*
         * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
         */
        @Override
        public void windowClosed(final IWorkbenchWindow window) {
        }

        /*
         * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
         */
        @Override
        public void windowOpened(final IWorkbenchWindow window) {
        }
    }

    /**
     * Sets the DocumentProvider for the given EditorInput.
     */
    protected void setDocumentProvider(final IEditorInput input) {
        documentProvider = DocumentProviderRegistry.getDefault().getDocumentProvider(input);
    }

    /**
     * If there is no explicit document provider set, the implicit one is re-initialized based on the given editor
     * input.
     *
     * @param input
     *            the editor input.
     */
    private void updateDocumentProvider(final IEditorInput input) {
        IDocumentProvider provider = getDocumentProvider();
        if (provider != null) {
            provider.removeElementStateListener(elementStateListener);
        }

        setDocumentProvider(input);

        provider = getDocumentProvider();
        if (provider != null) {
            provider.addElementStateListener(elementStateListener);
        }
    }

    /*
     * @see org.eclipse.ui.texteditor.ITextEditor#getDocumentProvider()
     */
    public IDocumentProvider getDocumentProvider() {
        return documentProvider;
    }

    /*
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#disposeDocumentProvider()
     */
    protected void disposeDocumentProvider() {
        final IDocumentProvider provider = getDocumentProvider();
        if (provider != null) {
            final IEditorInput input = getEditorInput();
            if (input != null) {
                provider.getDocument(input).removeDocumentListener(jFaceDocumentListener);
                provider.disconnect(input);
            }

            if (elementStateListener != null) {
                provider.removeElementStateListener(elementStateListener);
                elementStateListener = null;
            }
        }

    }
}