com.liferay.ide.ui.editor.InputContext.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.ide.ui.editor.InputContext.java

Source

/*******************************************************************************
 *  Copyright (c) 2003, 2008 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.liferay.ide.ui.editor;

import com.liferay.ide.core.model.IBaseModel;
import com.liferay.ide.core.model.IEditable;
import com.liferay.ide.core.model.IEditingModel;
import com.liferay.ide.core.model.IModelChangeProvider;
import com.liferay.ide.core.model.IModelChangedEvent;
import com.liferay.ide.core.model.IModelChangedListener;
import com.liferay.ide.ui.LiferayUIPlugin;
import com.liferay.ide.ui.form.IDEFormEditor;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
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.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MoveSourceEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.ui.editors.text.ForwardingDocumentProvider;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IElementStateListener;

/**
 * This class maintains objects associated with a single editor input.
 */
public abstract class InputContext {

    private IDEFormEditor fEditor;
    private IEditorInput fEditorInput;
    private IBaseModel fModel;
    private IModelChangedListener fModelListener;
    private IDocumentProvider fDocumentProvider;
    private IElementStateListener fElementListener;
    protected ArrayList fEditOperations = new ArrayList();

    private boolean fValidated;
    private boolean fPrimary;
    private boolean fIsSourceMode;
    private boolean fMustSynchronize;

    class ElementListener implements IElementStateListener {
        public void elementContentAboutToBeReplaced(Object element) {
        }

        public void elementContentReplaced(Object element) {
            if (element != null && element.equals(fEditorInput))
                doRevert();
        }

        public void elementDeleted(Object element) {
            if (element != null && element.equals(fEditorInput))
                dispose();
        }

        public void elementDirtyStateChanged(Object element, boolean isDirty) {
            if (element != null && element.equals(fEditorInput))
                fMustSynchronize = true;
        }

        public void elementMoved(Object originalElement, Object movedElement) {
            if (originalElement != null && originalElement.equals(fEditorInput)) {
                dispose();
                fEditor.close(true);
            }
        }
    }

    public InputContext(IDEFormEditor editor, IEditorInput input, boolean primary) {
        this.fEditor = editor;
        this.fEditorInput = input;
        setPrimary(primary);
    }

    public abstract String getId();

    public IEditorInput getInput() {
        return fEditorInput;
    }

    public IDEFormEditor getEditor() {
        return fEditor;
    }

    public IBaseModel getModel() {
        return fModel;
    }

    public IDocumentProvider getDocumentProvider() {
        return fDocumentProvider;
    }

    protected IDocumentProvider createDocumentProvider(IEditorInput input) {
        if (input instanceof IFileEditorInput) {
            return new ForwardingDocumentProvider(getPartitionName(), getDocumentSetupParticipant(),
                    LiferayUIPlugin.getDefault().getTextFileDocumentProvider());
        }
        return null;
    }

    protected IDocumentSetupParticipant getDocumentSetupParticipant() {
        return new IDocumentSetupParticipant() {
            public void setup(IDocument document) {
            }
        };
    }

    protected abstract String getPartitionName();

    protected abstract String getDefaultCharset();

    protected abstract IBaseModel createModel(IEditorInput input) throws CoreException;

    protected void create() {
        fDocumentProvider = createDocumentProvider(fEditorInput);
        try {
            fDocumentProvider.connect(fEditorInput);
            fModel = createModel(fEditorInput);
            if (fModel instanceof IModelChangeProvider) {
                fModelListener = new IModelChangedListener() {
                    public void modelChanged(IModelChangedEvent e) {
                        if (e.getChangeType() != IModelChangedEvent.WORLD_CHANGED) {
                            if (!fEditor.getLastDirtyState())
                                fEditor.fireSaveNeeded(fEditorInput, true);
                            IModelChangeProvider provider = e.getChangeProvider();
                            if (provider instanceof IEditingModel) {
                                // this is to guard against false notifications
                                // when a revert operation is performed, focus is taken away from a FormEntry
                                // and a text edit operation is falsely requested
                                if (((IEditingModel) provider).isDirty())
                                    addTextEditOperation(fEditOperations, e);
                            }
                        }
                    }
                };
                ((IModelChangeProvider) fModel).addModelChangedListener(fModelListener);
            }

            IAnnotationModel amodel = fDocumentProvider.getAnnotationModel(fEditorInput);
            if (amodel != null)
                amodel.connect(fDocumentProvider.getDocument(fEditorInput));
            fElementListener = new ElementListener();
            fDocumentProvider.addElementStateListener(fElementListener);
        } catch (CoreException e) {
            LiferayUIPlugin.logError(e);
        }

    }

    public synchronized boolean validateEdit() {
        if (!fValidated) {
            if (fEditorInput instanceof IFileEditorInput) {
                IFile file = ((IFileEditorInput) fEditorInput).getFile();
                if (file.isReadOnly()) {
                    Shell shell = fEditor.getEditorSite().getShell();
                    IStatus validateStatus = LiferayUIPlugin.getWorkspace().validateEdit(new IFile[] { file },
                            shell);
                    fValidated = true; // to prevent loops
                    if (validateStatus.getSeverity() != IStatus.OK)
                        ErrorDialog.openError(shell, fEditor.getTitle(), null, validateStatus);
                    return validateStatus.getSeverity() == IStatus.OK;
                }
            }
        }
        return true;
    }

    public void doSave(IProgressMonitor monitor) {
        try {
            IDocument doc = fDocumentProvider.getDocument(fEditorInput);
            fDocumentProvider.aboutToChange(fEditorInput);
            flushModel(doc);
            fDocumentProvider.saveDocument(monitor, fEditorInput, doc, true);
            fDocumentProvider.changed(fEditorInput);
            fValidated = false;
        } catch (CoreException e) {
            LiferayUIPlugin.logError(e);
        }
    }

    protected abstract void addTextEditOperation(ArrayList ops, IModelChangedEvent event);

    public void flushEditorInput() {
        if (fEditOperations.size() > 0) {
            IDocument doc = fDocumentProvider.getDocument(fEditorInput);
            fDocumentProvider.aboutToChange(fEditorInput);
            flushModel(doc);
            fDocumentProvider.changed(fEditorInput);
            fValidated = false;
        } else if ((fModel instanceof IEditable) && ((IEditable) fModel).isDirty()) {
            // When text edit operations are made that cancel each other out,
            // the editor is not undirtied
            // e.g. Extensions page:  Move an element up and then move it down
            // back in the same position:  Bug # 197831
            ((IEditable) fModel).setDirty(false);
        }
    }

    protected void flushModel(IDocument doc) {
        boolean flushed = true;
        if (fEditOperations.size() > 0) {
            try {
                MultiTextEdit edit = new MultiTextEdit();
                if (isNewlineNeeded(doc))
                    insert(edit, new InsertEdit(doc.getLength(), TextUtilities.getDefaultLineDelimiter(doc)));
                for (int i = 0; i < fEditOperations.size(); i++) {
                    insert(edit, (TextEdit) fEditOperations.get(i));
                }
                if (fModel instanceof IEditingModel)
                    ((IEditingModel) fModel).setStale(true);
                edit.apply(doc);
                fEditOperations.clear();
            } catch (MalformedTreeException e) {
                LiferayUIPlugin.logError(e);
                flushed = false;
            } catch (BadLocationException e) {
                LiferayUIPlugin.logError(e);
                flushed = false;
            }
        }
        // If no errors were encountered flushing the model, then undirty the
        // model.  This needs to be done regardless of whether there are any
        // edit operations or not; since, the contributed actions need to be
        // updated and the editor needs to be undirtied
        if (flushed && (fModel instanceof IEditable)) {
            ((IEditable) fModel).setDirty(false);
        }
    }

    public static int getInsertOffset(IDocument doc) {
        int offset = doc.getLength();
        for (int i = doc.getNumberOfLines() - 1; i >= 0; i--) {
            try {
                if (doc.get(doc.getLineOffset(i), doc.getLineLength(i)).trim().length() > 0) {
                    break;
                }
                offset = doc.getLineOffset(i);
            } catch (BadLocationException e) {
            }
        }
        return offset;
    }

    public static boolean isNewlineNeeded(IDocument doc) throws BadLocationException {
        int line = doc.getLineOfOffset(getInsertOffset(doc));
        return doc.get(doc.getLineOffset(line), doc.getLineLength(line)).trim().length() > 0;
    }

    protected static void insert(TextEdit parent, TextEdit edit) {
        if (!parent.hasChildren()) {
            parent.addChild(edit);
            if (edit instanceof MoveSourceEdit) {
                parent.addChild(((MoveSourceEdit) edit).getTargetEdit());
            }
            return;
        }
        TextEdit[] children = parent.getChildren();
        // First dive down to find the right parent.
        for (int i = 0; i < children.length; i++) {
            TextEdit child = children[i];
            if (covers(child, edit)) {
                insert(child, edit);
                return;
            }
        }
        // We have the right parent. Now check if some of the children have to
        // be moved under the new edit since it is covering it.
        for (int i = children.length - 1; i >= 0; i--) {
            TextEdit child = children[i];
            if (covers(edit, child)) {
                parent.removeChild(i);
                edit.addChild(child);
            }
        }
        parent.addChild(edit);
        if (edit instanceof MoveSourceEdit) {
            parent.addChild(((MoveSourceEdit) edit).getTargetEdit());
        }
    }

    protected static boolean covers(TextEdit thisEdit, TextEdit otherEdit) {
        if (thisEdit.getLength() == 0) // an insertion point can't cover anything
            return false;

        int thisOffset = thisEdit.getOffset();
        int thisEnd = thisEdit.getExclusiveEnd();
        if (otherEdit.getLength() == 0) {
            int otherOffset = otherEdit.getOffset();
            return thisOffset < otherOffset && otherOffset < thisEnd;
        }
        int otherOffset = otherEdit.getOffset();
        int otherEnd = otherEdit.getExclusiveEnd();
        return thisOffset <= otherOffset && otherEnd <= thisEnd;
    }

    public boolean mustSave() {
        if (!fIsSourceMode) {
            if (fModel instanceof IEditable) {
                if (((IEditable) fModel).isDirty()) {
                    return true;
                }
            }
        }
        return fEditOperations.size() > 0 || fDocumentProvider.canSaveDocument(fEditorInput);
    }

    public void dispose() {
        IAnnotationModel amodel = fDocumentProvider.getAnnotationModel(fEditorInput);
        if (amodel != null)
            amodel.disconnect(fDocumentProvider.getDocument(fEditorInput));
        fDocumentProvider.removeElementStateListener(fElementListener);
        fDocumentProvider.disconnect(fEditorInput);
        if (fModelListener != null && fModel instanceof IModelChangeProvider) {
            ((IModelChangeProvider) fModel).removeModelChangedListener(fModelListener);
            //if (undoManager != null)
            //undoManager.disconnect((IModelChangeProvider) model);
        }
        if (fModel != null)
            fModel.dispose();
    }

    /**
     * @return Returns the primary.
     */
    public boolean isPrimary() {
        return fPrimary;
    }

    /**
     * @param primary The primary to set.
     */
    public void setPrimary(boolean primary) {
        this.fPrimary = primary;
    }

    public boolean setSourceEditingMode(boolean sourceMode) {
        fIsSourceMode = sourceMode;
        if (sourceMode) {
            // entered source editing mode; in this mode,
            // this context's document will be edited directly
            // in the source editor. All changes in the model
            // are caused by reconciliation and should not be 
            // fired to the world.
            flushModel(fDocumentProvider.getDocument(fEditorInput));
            fMustSynchronize = true;
            return true;
        }
        // leaving source editing mode; if the document
        // has been modified while in this mode,
        // fire the 'world changed' event from the model
        // to cause all the model listeners to become stale.
        return synchronizeModelIfNeeded();
    }

    private boolean synchronizeModelIfNeeded() {
        if (fMustSynchronize) {
            boolean result = synchronizeModel(fDocumentProvider.getDocument(fEditorInput));
            fMustSynchronize = false;
            return result;
        }
        return true;
    }

    public void doRevert() {
        fMustSynchronize = true;
        synchronizeModelIfNeeded();
        /*
        if (model instanceof IEditable) {
           ((IEditable)model).setDirty(false);
        }
        */
    }

    public boolean isInSourceMode() {
        return fIsSourceMode;
    }

    public boolean isModelCorrect() {
        synchronizeModelIfNeeded();
        return fModel != null ? fModel.isValid() : false;
    }

    protected boolean synchronizeModel(IDocument doc) {
        return true;
    }

    public boolean matches(IResource resource) {
        if (fEditorInput instanceof IFileEditorInput) {
            IFileEditorInput finput = (IFileEditorInput) fEditorInput;
            IFile file = finput.getFile();
            if (file.equals(resource))
                return true;
        }
        return false;
    }

    /**
     * @return Returns the validated.
     */
    public boolean isValidated() {
        return fValidated;
    }

    /**
     * @param validated The validated to set.
     */
    public void setValidated(boolean validated) {
        this.fValidated = validated;
    }

    public String getLineDelimiter() {
        if (fDocumentProvider != null) {
            IDocument document = fDocumentProvider.getDocument(fEditorInput);
            if (document != null) {
                return TextUtilities.getDefaultLineDelimiter(document);
            }
        }
        return System.getProperty("line.separator"); //$NON-NLS-1$
    }

    /**
     * @param input
     * @throws CoreException
     */
    private void updateInput(IEditorInput newInput) throws CoreException {
        deinitializeDocumentProvider();
        fEditorInput = newInput;
        initializeDocumentProvider();
    }

    /**
     * 
     */
    private void deinitializeDocumentProvider() {
        IAnnotationModel amodel = fDocumentProvider.getAnnotationModel(fEditorInput);
        if (amodel != null) {
            amodel.disconnect(fDocumentProvider.getDocument(fEditorInput));
        }
        fDocumentProvider.removeElementStateListener(fElementListener);
        fDocumentProvider.disconnect(fEditorInput);
    }

    /**
     * @throws CoreException
     */
    private void initializeDocumentProvider() throws CoreException {
        fDocumentProvider.connect(fEditorInput);
        IAnnotationModel amodel = fDocumentProvider.getAnnotationModel(fEditorInput);
        if (amodel != null) {
            amodel.connect(fDocumentProvider.getDocument(fEditorInput));
        }
        fDocumentProvider.addElementStateListener(fElementListener);
    }

    /**
     * @param monitor
     */
    public void doSaveAs(IProgressMonitor monitor) throws Exception {
        // Get the editor shell
        Shell shell = getEditor().getSite().getShell();
        // Create the save as dialog
        SaveAsDialog dialog = new SaveAsDialog(shell);
        // Set the initial file name to the original file name
        IFile file = null;
        if (fEditorInput instanceof IFileEditorInput) {
            file = ((IFileEditorInput) fEditorInput).getFile();
            dialog.setOriginalFile(file);
        }
        // Create the dialog
        dialog.create();
        // Warn the user if the underlying file does not exist
        if (fDocumentProvider.isDeleted(fEditorInput) && (file != null)) {
            String message = NLS.bind("File does not exist: {0}", file.getName()); //$NON-NLS-1$
            dialog.setErrorMessage(null);
            dialog.setMessage(message, IMessageProvider.WARNING);
        }
        // Open the dialog
        if (dialog.open() == Window.OK) {
            // Get the path to where the new file will be stored
            IPath path = dialog.getResult();
            handleSaveAs(monitor, path);
        }
    }

    /**
     * @param monitor
     * @param path
     * @throws Exception
     * @throws CoreException
     * @throws InterruptedException
     * @throws InvocationTargetException
     */
    private void handleSaveAs(IProgressMonitor monitor, IPath path)
            throws Exception, CoreException, InterruptedException, InvocationTargetException {
        // Ensure a new location was selected
        if (path == null) {
            monitor.setCanceled(true);
            throw new Exception("Location not set."); //$NON-NLS-1$
        }
        // Resolve the new file location   
        IWorkspace workspace = ResourcesPlugin.getWorkspace();
        IFile newFile = workspace.getRoot().getFile(path);
        // Create the new editor input
        final IEditorInput newInput = new FileEditorInput(newFile);
        // Send notice of editor input changes
        fDocumentProvider.aboutToChange(newInput);
        // Flush any unsaved changes
        flushModel(fDocumentProvider.getDocument(fEditorInput));
        try {
            // Execute the workspace modification in a separate thread
            PlatformUI.getWorkbench().getProgressService()
                    .busyCursorWhile(createWorkspaceModifyOperation(newInput));
            monitor.setCanceled(false);
            // Store the new editor input in this context
            updateInput(newInput);
        } catch (InterruptedException e) {
            monitor.setCanceled(true);
            throw e;
        } catch (InvocationTargetException e) {
            monitor.setCanceled(true);
            throw e;
        } finally {
            fDocumentProvider.changed(newInput);
        }
    }

    /**
     * @param newInput
     * @return
     */
    private WorkspaceModifyOperation createWorkspaceModifyOperation(final IEditorInput newInput) {
        WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
            public void execute(final IProgressMonitor monitor) throws CoreException {
                // Save the old editor input content to the new editor input
                // location
                fDocumentProvider.saveDocument(monitor,
                        // New editor input location 
                        newInput,
                        // Old editor input content
                        fDocumentProvider.getDocument(fEditorInput), true);
            }
        };
        return operation;
    }

}