com.nextep.designer.sqlgen.ui.editors.SQLEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.nextep.designer.sqlgen.ui.editors.SQLEditor.java

Source

/*******************************************************************************
 * Copyright (c) 2011 neXtep Software and contributors.
 * All rights reserved.
 *
 * This file is part of neXtep designer.
 *
 * NeXtep designer is free software: you can redistribute it 
 * and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation, either 
 * version 3 of the License, or any later version.
 *
 * NeXtep designer is distributed in the hope that it will be 
 * useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     neXtep Softwares - initial API and implementation
 *******************************************************************************/
package com.nextep.designer.sqlgen.ui.editors;

import java.io.File;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.TextOperationAction;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import com.nextep.datadesigner.Designer;
import com.nextep.datadesigner.dbgm.gui.editors.ISQLEditorInput;
import com.nextep.datadesigner.dbgm.model.IParseData;
import com.nextep.datadesigner.dbgm.model.IProcedure;
import com.nextep.datadesigner.dbgm.services.DBGMHelper;
import com.nextep.datadesigner.model.ChangeEvent;
import com.nextep.datadesigner.model.IEventListener;
import com.nextep.datadesigner.model.IModelOriented;
import com.nextep.datadesigner.model.IObservable;
import com.nextep.datadesigner.sqlgen.impl.SQLScript;
import com.nextep.datadesigner.sqlgen.model.IPackage;
import com.nextep.datadesigner.sqlgen.model.ISQLScript;
import com.nextep.designer.core.CorePlugin;
import com.nextep.designer.core.factories.ControllerFactory;
import com.nextep.designer.dbgm.sql.TextPosition;
import com.nextep.designer.sqlgen.ui.SQLEditorInput;
import com.nextep.designer.sqlgen.ui.SQLMessages;
import com.nextep.designer.sqlgen.ui.commands.MarkOccurrencesJob;
import com.nextep.designer.sqlgen.ui.editors.sql.SQLContentOutlinePage;
import com.nextep.designer.sqlgen.ui.model.IConnectable;
import com.nextep.designer.sqlgen.ui.services.IBreakpoint;
import com.nextep.designer.sqlgen.ui.services.SQLEditorUIServices;

/**
 * @author Christophe Fondacci
 */
public class SQLEditor extends AbstractDecoratedTextEditor implements IEventListener {

    private static final Log log = LogFactory.getLog(SQLEditor.class);
    private SQLContentOutlinePage outlinePage;
    public final static String EDITOR_ID = "com.neXtep.designer.sqlgen.ui.SQLEditor"; //$NON-NLS-1$
    private boolean isSaving = false;
    /** The projection support */
    private ProjectionSupport fProjectionSupport;
    private Collection<ProjectionAnnotation> annotations = new ArrayList<ProjectionAnnotation>();

    public SQLEditor() {
        super();
        setKeyBindingScopes(new String[] { "org.eclipse.ui.textEditorScope" }); //$NON-NLS-1$
        // configureInsertMode(SMART_INSERT, false);
        setDocumentProvider(new SQLDocumentProvider());
    }

    /**
     * @see org.eclipse.ui.editors.text.TextEditor#initializeEditor()
     */
    @Override
    protected void initializeEditor() {
        super.initializeEditor();
        setSourceViewerConfiguration(new SQLSourceViewerConfiguration(this));

    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#init(org.eclipse.ui.IEditorSite,
     *      org.eclipse.ui.IEditorInput)
     */
    @SuppressWarnings("unchecked")
    @Override
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        super.init(site, input);
        outlinePage = new SQLContentOutlinePage(getDocumentProvider(), this);
        if (input instanceof ISQLEditorInput) {
            // We register the editor as a listener to our model to handle
            // Model changes (checkout / checkin)
            final ISQLEditorInput<IObservable> sqlInput = (ISQLEditorInput<IObservable>) input;
            Designer.getListenerService().registerListener(this, (IObservable) sqlInput.getModel(),
                    new IEventListener() {

                        @Override
                        public void handleEvent(final ChangeEvent event, final IObservable source,
                                final Object data) {
                            getSite().getShell().getDisplay().syncExec(new Runnable() {

                                @Override
                                public void run() {
                                    if (source != null) {
                                        switch (event) {
                                        case UPDATES_LOCKED:
                                        case UPDATES_UNLOCKED:
                                            log.debug("Switching SQL editor model"); //$NON-NLS-1$
                                            sqlInput.setModel(source);
                                            updateDocumentFromInput(sqlInput);
                                            resetDocument();
                                            break;
                                        case SOURCE_CHANGED:
                                        case MODEL_CHANGED:
                                            // We only refresh editor contents if we're not in a
                                            // save operation, else multi-sql content (like package)
                                            // might erase other part's modifications
                                            if (!isDirty()) {
                                                updateDocumentFromInput(sqlInput);
                                                resetDocument();
                                            }
                                            break;
                                        default:
                                            updateStateAndActions(sqlInput);
                                            break;
                                        }
                                    } else {
                                        updateStateAndActions(sqlInput);
                                    }
                                    addFolding();
                                    refreshAnnotations();
                                }
                            });
                        }
                    });
            setInput(input);
        } else if (input instanceof FileStoreEditorInput) {
            // Converting an external file input to a standard SQLScript / ISQLEditorInput
            FileStoreEditorInput i = (FileStoreEditorInput) input;
            ISQLScript s = new SQLScript(i.getURI().getPath());
            setInput(new SQLEditorInput(s));
        }
    }

    /**
     * Updates the underlying editor document from the current content of the editor input.
     * 
     * @param sqlInput {@link ISQLEditorInput} to retrieve content from
     */
    private void updateDocumentFromInput(ISQLEditorInput<?> sqlInput) {
        if (!isDirty()) {
            // We save current selection which would be reset by the document update
            int selectionOffset = 0;
            int length = 0;
            final ISelection sel = getSelectionProvider().getSelection();
            if (sel instanceof ITextSelection) {
                final ITextSelection textSel = (ITextSelection) sel;
                selectionOffset = textSel.getOffset();
                length = textSel.getLength();
            }
            // Updating document
            getDocumentProvider().getDocument(sqlInput).set(sqlInput.getSql() == null ? "" : sqlInput.getSql()); //$NON-NLS-1$
            setPartName(sqlInput.getName());
            updateStateAndActions(sqlInput);
            getDocumentProvider().changed(sqlInput);
            // Restoring selection
            if (selectionOffset > 0 || length > 0) {
                ITextSelection textSel = new TextSelection(selectionOffset, length);
                try {
                    getSelectionProvider().setSelection(textSel);
                } catch (RuntimeException e) {
                    log.warn("Could not restore previous selection", e);
                }
            }
        }
    }

    private void updateStateAndActions(ISQLEditorInput<?> sqlInput) {
        updateState(sqlInput);
        updateStateDependentActions();
        updateContentDependentActions();
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#dispose()
     */
    @Override
    public void dispose() {
        Designer.getListenerService().unregisterListeners(this);
        IEditorInput input = getEditorInput();
        if (input instanceof IConnectable) {
            final IConnectable connectableInput = (IConnectable) input;
            Connection conn = connectableInput.getSqlConnection();
            if (conn != null) {
                try {
                    final DatabaseMetaData md = conn.getMetaData();
                    log.info("Disconnecting from " + md.getURL());
                    conn.close();
                    connectableInput.setSqlConnection(null);
                } catch (SQLException e) {
                    log.warn("Unable to close connection: " + e.getMessage(), e);
                }
            }
        }
        super.dispose();
    }

    /**
     * @see org.eclipse.ui.editors.text.TextEditor#createActions()
     */
    @Override
    protected void createActions() {
        super.createActions();

        IAction action = new TextOperationAction(SQLMessages.getResourceBundle(), "ContentAssist.", //$NON-NLS-1$
                this, ISourceViewer.CONTENTASSIST_PROPOSALS);
        action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
        setAction("ContentAssist", action); //$NON-NLS-1$
        markAsStateDependentAction("ContentAssist", true); //$NON-NLS-1$
    }

    /*
     * @see org.eclipse.ui.editors.text.TextEditor#getAdapter(java.lang.Class)
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object getAdapter(Class adapter) {
        if (IContentOutlinePage.class == adapter) {
            return outlinePage;
        }
        if (fProjectionSupport != null) {
            Object a = fProjectionSupport.getAdapter(getSourceViewer(), adapter);
            if (a != null)
                return a;
        }
        if (getDocumentProvider() != null && adapter == IAnnotationModel.class) {
            Object a = getDocumentProvider().getAnnotationModel(getEditorInput());
            if (a != null) {
                return a;
            }
        }
        return super.getAdapter(adapter);
    }

    /**
     * @see org.eclipse.ui.editors.text.TextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
     */
    @Override
    protected void editorContextMenuAboutToShow(IMenuManager menu) {
        menu.add(new Separator("sql")); //$NON-NLS-1$
        menu.add(new Separator("profiling")); //$NON-NLS-1$
        menu.add(new Separator());
        super.editorContextMenuAboutToShow(menu);
        addAction(menu, "ContentAssist"); //$NON-NLS-1$
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractTextEditor#doSave(org.eclipse.core.runtime.IProgressMonitor)
     */
    @Override
    public void doSave(IProgressMonitor progressMonitor) {
        // We should never loose editor content, so if we need a failsafe save, we save as on the
        // filesystem
        try {
            isSaving = true;
            if (Designer.getTerminationSignal()) {
                doSaveAs();
            } else {
                super.doSave(progressMonitor);
                final IPackage pkg = getPackage();
                if (pkg != null) {
                    DBGMHelper.parse(pkg);
                    addFolding();
                }
                refreshAnnotations();
                if (outlinePage != null) {
                    outlinePage.update();
                }
            }
            updateDocumentFromInput((ISQLEditorInput<?>) getEditorInput());
            resetDocument();
        } finally {
            isSaving = false;
        }
    }

    @Override
    protected void performSaveAs(IProgressMonitor progressMonitor) {
        final IEditorInput input = getEditorInput();
        FileDialog d = new FileDialog(this.getSite().getShell(), SWT.SAVE);
        d.setFilterExtensions(new String[] { "*.sql" }); //$NON-NLS-1$
        d.setOverwrite(true);
        d.setFileName(input.getName());
        d.setText(SQLMessages.getString("editor.sql.saveAsSqlFilePrompt")); //$NON-NLS-1$
        String fileLocation = d.open();
        if (fileLocation != null) {
            ISQLScript s = null;
            // If our editor is an editor over a ISQLScript already external, we use it to save
            // our contents
            if (input instanceof IModelOriented<?>) {
                final Object model = ((IModelOriented<?>) input).getModel();
                if (model instanceof ISQLScript) {
                    final ISQLScript script = (ISQLScript) model;
                    if (script.isExternal()) {
                        s = script;
                    }
                }
            }
            if (s == null) {
                s = CorePlugin.getTypedObjectFactory().create(ISQLScript.class);
            }
            s.setExternal(true);
            if (fileLocation.toLowerCase().endsWith(".sql")) { ////$NON-NLS-1$
                fileLocation = fileLocation.substring(0, fileLocation.length() - 4);
            }
            File f = new File(fileLocation);
            s.setDirectory(f.getParent());
            s.setName(f.getName());
            s.setSql(getDocumentProvider().getDocument(getEditorInput()).get());
            ControllerFactory.getController(s).save(s);
            setPartName(input.getName());
        }
    }

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

    /**
     * Convenience method that return the underlying package, if we are editing a package
     * 
     * @return
     */
    private IPackage getPackage() {
        if (getEditorInput() instanceof ISQLEditorInput<?>) {
            if (((ISQLEditorInput<?>) getEditorInput()).getModel() instanceof IPackage) {
                return (IPackage) ((ISQLEditorInput<?>) getEditorInput()).getModel();
            }
        }
        return null;
    }

    // private void removeFolding() {
    // IAnnotationModel model= (IAnnotationModel)getAdapter(ProjectionAnnotationModel.class);
    // if (model != null) {
    // for(ProjectionAnnotation a : annotations) {
    // model.removeAnnotation(a);
    // }
    // }
    // }
    private void addFolding() {
        IAnnotationModel model = (IAnnotationModel) getAdapter(ProjectionAnnotationModel.class);
        Map<ProjectionAnnotation, Position> annMap = new HashMap<ProjectionAnnotation, Position>();
        final IPackage pkg = getPackage();
        if (model != null && pkg != null) {
            IParseData data = pkg.getParseData();
            for (IProcedure proc : pkg.getProcedures()) {
                TextPosition p = data.getPosition(proc);
                if (p != null) {
                    Position pos = new Position(p.getOffset(), p.getLength());
                    ProjectionAnnotation a = new ProjectionAnnotation();
                    annMap.put(a, pos);
                } else {
                    log.debug("WARN: Empty position for procedure: " + proc.getName()); //$NON-NLS-1$
                }
            }
            ((IAnnotationModelExtension) model).replaceAnnotations(annotations.isEmpty() ? null
                    : annotations.toArray(new ProjectionAnnotation[annotations.size()]), annMap);
            annotations = annMap.keySet();
        }
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createSourceViewer(org.eclipse.swt.widgets.Composite,
     *      org.eclipse.jface.text.source.IVerticalRuler, int)
     */
    @Override
    protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
        fAnnotationAccess = getAnnotationAccess();
        fOverviewRuler = createOverviewRuler(getSharedColors());

        ISourceViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(),
                styles);
        // ensure decoration support has been created and configured.
        getSourceViewerDecorationSupport(viewer);

        return viewer;
    }

    /**
     * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);
        ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
        fProjectionSupport = new ProjectionSupport(viewer, getAnnotationAccess(), getSharedColors());
        fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$
        fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$
        fProjectionSupport.install();
        viewer.doOperation(ProjectionViewer.TOGGLE);
        final IPackage pkg = getPackage();
        if (pkg != null) {
            DBGMHelper.parse(pkg);
        }
        addFolding();
        installSelectionListeners();
        // Setting title
        setPartName(getEditorInput().getName());
        SQLEditorUIServices.getInstance().addListener(this);
        getVerticalRuler().getControl().addMouseListener(new RulerMouseListener(getVerticalRuler(), this));
        refreshAnnotations();
    }

    /**
     * @see com.nextep.datadesigner.model.IEventListener#handleEvent(com.nextep.datadesigner.model.ChangeEvent,
     *      com.nextep.datadesigner.model.IObservable, java.lang.Object)
     */
    @Override
    public void handleEvent(ChangeEvent event, IObservable source, Object data) {
        if (getDocumentProvider() == null)
            return;
        IAnnotationModel ann = getDocumentProvider().getAnnotationModel(getEditorInput());
        if (ann == null)
            return;

        try {
            switch (event) {
            case BREAKPOINT_ADDED:
                IBreakpoint bp = (IBreakpoint) data;
                Annotation a = new Annotation("org.eclipse.ui.workbench.texteditor.info", false, //$NON-NLS-1$
                        SQLMessages.getString("editor.sql.breakpointText") + bp.getLine()); //$NON-NLS-1$
                ann.addAnnotation(a, new Position(
                        getDocumentProvider().getDocument(getEditorInput()).getLineOffset(bp.getLine())));
                bp.setAnnotation(a);
                break;
            case BREAKPOINT_REMOVED:
                IBreakpoint b = (IBreakpoint) data;
                if (b.getAnnotation() != null) {
                    ann.removeAnnotation(b.getAnnotation());
                }
                break;
            }
        } catch (BadLocationException e) {
            log.debug("BadLocation while adding breakpoint", e); //$NON-NLS-1$
        }

    }

    @Override
    public void setFocus() {
        super.setFocus();
        refreshAnnotations();
    }

    /**
     * Refreshes markers annotations
     */
    private void refreshAnnotations() {
        SQLEditorUIServices.getInstance().annotateInput(getDocumentProvider(), getEditorInput());
    }

    /**
     * Installs the selection listeners on the current document / editor so that proper listeners
     * will catch the selection events coming from the editor. Typically, this is where we add the
     * listener which marks occurrences of the selected text.
     */
    protected void installSelectionListeners() {
        ((IPostSelectionProvider) this.getEditorSite().getSelectionProvider())
                .addPostSelectionChangedListener(new ISelectionChangedListener() {

                    @Override
                    public void selectionChanged(SelectionChangedEvent event) {
                        // Retriveing selected text
                        ISelection s = event.getSelection();
                        if (s instanceof ITextSelection) {
                            final String selectedText = ((ITextSelection) s).getText();
                            if (selectedText != null && !selectedText.trim().equals("")) { //$NON-NLS-1$
                                MarkOccurrencesJob markerJob = new MarkOccurrencesJob(SQLEditor.this, selectedText);
                                // Running our job
                                markerJob.schedule();
                            }

                        }
                    }
                });
    }

    private void resetDocument() {
        try {
            getDocumentProvider().resetDocument(getEditorInput());
        } catch (CoreException e) {
            log.error(SQLMessages.getString("editor.sql.modelSwitchError") //$NON-NLS-1$
                    + e.getMessage(), e);
        }
    }

    public boolean isSaving() {
        return isSaving;
    }
}