org.talend.designer.core.ui.viewer.ReconcilerViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.talend.designer.core.ui.viewer.ReconcilerViewer.java

Source

// ============================================================================
//
// Copyright (C) 2006-2015 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.designer.core.ui.viewer;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.internal.ui.text.java.hover.SourceViewerInformationControl;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISharedTextColors;
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.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.talend.commons.ui.runtime.exception.ExceptionHandler;
import org.talend.commons.ui.runtime.image.ImageProvider;
import org.talend.commons.utils.threading.ExecutionLimiter;
import org.talend.core.model.process.INode;

/**
 * DOC nrousseau class global comment. Detailled comment <br/>
 * 
 */
public abstract class ReconcilerViewer extends ProjectionViewer {

    protected IFile file = null;

    // private SourceFile source;

    private SourceViewerDecorationSupport fSourceViewerDecorationSupport;

    protected IOverviewRuler fOverviewRuler;

    private MarkerAnnotationPreferences fAnnotationPreferences;

    protected IAnnotationAccess annotationAccess;

    protected ISharedTextColors sharedColors;

    protected boolean checkCode;

    // used to calculate the full document displayed (in the visible part, no
    // matter if there is annotations or not)
    private IRegion viewerStartRegion, viewerEndRegion;

    private int oldDocLength;

    private INode hostNode = null;

    /**
     * Preference key for highlighting current line.
     */
    private static final String CURRENT_LINE = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE;

    /**
     * Preference key for highlight color of current line.
     */
    private static final String CURRENT_LINE_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR;

    /**
     * Preference key for showing print margin ruler.
     */
    private static final String PRINT_MARGIN = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN;

    /**
     * Preference key for print margin ruler color.
     */
    private static final String PRINT_MARGIN_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLOR;

    /**
     * Preference key for print margin ruler column.
     */
    private static final String PRINT_MARGIN_COLUMN = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN;

    public ReconcilerViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler,
            boolean showAnnotationsOverview, int styles, IAnnotationAccess annotationAccess,
            ISharedTextColors sharedColors, boolean checkCode, IDocument document, final INode node) {
        super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles);
        fOverviewRuler = overviewRuler;
        this.annotationAccess = annotationAccess;
        this.sharedColors = sharedColors;
        this.checkCode = checkCode;
        this.hostNode = node;

        initializeViewer(document);
    }

    protected void initializeViewer(IDocument document) {
        fAnnotationPreferences = EditorsPlugin.getDefault().getMarkerAnnotationPreferences();

        setDocument(document);

        installViewerConfiguration();
        setEditable(true);

        Font font = JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT);
        getTextWidget().setFont(font);

        Control control = getControl();
        GridData data = new GridData(GridData.FILL_BOTH);
        control.setLayoutData(data);

        prependVerifyKeyListener(new VerifyKeyListener() {

            public void verifyKey(VerifyEvent event) {
                handleVerifyKeyPressed(event);
            }
        });
        addDocumentListener(document);

        addMenu();
    }

    private void addDocumentListener(final IDocument document) {
        final ExecutionLimiter documentReconcilerLimiter = new ExecutionLimiter(500, true) {

            @Override
            protected void execute(boolean isFinalExecution, Object data) {
                if (isFinalExecution) {
                    if (getControl() != null && !getControl().isDisposed()) {
                        getControl().getDisplay().asyncExec(new Runnable() {

                            public void run() {
                                // System.out.println(System.currentTimeMillis())
                                // ;
                                updateContents();
                                if (document.get().length() != 0) {
                                    calculatePositions();
                                }
                                // (bug 4289)
                                updateVisibleRegion();
                            }
                        });
                    }
                }
            }
        };

        document.addDocumentListener(new IDocumentListener() {

            public void documentAboutToBeChanged(DocumentEvent event) {
                // nothing
            }

            public void documentChanged(DocumentEvent event) {
                documentReconcilerLimiter.resetTimer();
                documentReconcilerLimiter.startIfExecutable(true, null);
            }
        });
    }

    protected Map<ProjectionAnnotation, Position> oldAnnotations = new HashMap<ProjectionAnnotation, Position>();

    protected void calculatePositions() {
        if (hasSnippetsModifications()) {
            final Map<ProjectionAnnotation, Position> annotations = getAllSnippetsAnnotations();

            Display.getDefault().asyncExec(new Runnable() {

                public void run() {
                    if (!annotations.isEmpty() && getProjectionAnnotationModel() == null) {
                        enableProjection();
                    }
                    if (getProjectionAnnotationModel() != null) {
                        Annotation[] oldAnno = oldAnnotations.keySet().toArray(new Annotation[0]);
                        getProjectionAnnotationModel().modifyAnnotations(oldAnno, annotations, null);
                        oldAnnotations.clear();
                        oldAnnotations.putAll(annotations);
                        if (annotations.isEmpty()) {
                            disableProjection();
                        }
                    }
                }

            });
        }
    }

    /**
     * Check if there is any new snippet or if some has been deleted.
     * 
     * @return
     */
    private boolean hasSnippetsModifications() {
        IDocument document = getDocument();
        if (document == null) {
            return false;
        }
        int curNbAnnotations = oldAnnotations.size();
        int actualNbAnnotations = 0;
        int curOffset = 0;
        FindReplaceDocumentAdapter frda = new FindReplaceDocumentAdapter(document);
        try {
            IRegion startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
            while (startRegion != null && startRegion.getOffset() >= curOffset) {
                int startLine = document.getLineOfOffset(startRegion.getOffset());
                int startOffset = document.getLineOffset(startLine);
                curOffset = startOffset + document.getLineLength(startLine);
                IRegion endRegion = frda.find(startRegion.getOffset(), "SNIPPET_END", true, false, false, false); //$NON-NLS-1$
                if (endRegion != null) {
                    actualNbAnnotations++;
                    int endLine = document.getLineOfOffset(endRegion.getOffset());
                    int endOffset = document.getLineOffset(endLine);
                    endOffset += document.getLineLength(endLine);
                    curOffset = endOffset;
                    boolean contains = false;
                    String text = document.get(startOffset, endOffset - startOffset);
                    for (ProjectionAnnotation annotation : oldAnnotations.keySet()) {
                        Position pos = oldAnnotations.get(annotation);
                        if (annotation.getText().equals(text) && (startOffset == pos.getOffset())) {
                            contains = true;
                        }
                    }
                    if (!contains) {
                        return true;
                    }
                }
                if (curOffset < document.getLength()) {
                    startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
                }
            }

        } catch (BadLocationException e) {
            ExceptionHandler.process(e);
        }
        if (curNbAnnotations != actualNbAnnotations) {
            return true;
        }
        return false;
    }

    private Map<ProjectionAnnotation, Position> getAllSnippetsAnnotations() {
        Map<ProjectionAnnotation, Position> annotations = new HashMap<ProjectionAnnotation, Position>();
        IDocument document = getDocument();
        int curOffset = 0;
        FindReplaceDocumentAdapter frda = new FindReplaceDocumentAdapter(document);
        try {
            IRegion startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
            while (startRegion != null && startRegion.getOffset() >= curOffset) {
                int startLine = document.getLineOfOffset(startRegion.getOffset());
                int startOffset = document.getLineOffset(startLine);
                curOffset = startOffset + document.getLineLength(startLine);
                IRegion endRegion = frda.find(startRegion.getOffset(), "SNIPPET_END", true, false, false, false); //$NON-NLS-1$
                if (endRegion != null) {
                    int endLine = document.getLineOfOffset(endRegion.getOffset());
                    int endOffset = document.getLineOffset(endLine);
                    endOffset += document.getLineLength(endLine);
                    curOffset = endOffset;
                    String text = document.get(startOffset, endOffset - startOffset);
                    ProjectionAnnotation annotation = new ProjectionAnnotation(true);
                    annotation.setText(text);
                    annotation.setRangeIndication(true);
                    annotations.put(annotation, new Position(startOffset, endOffset - startOffset));
                }
                if (curOffset < document.getLength()) {
                    startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
                }
            }

        } catch (BadLocationException e) {
            ExceptionHandler.process(e);
        }
        return annotations;
    }

    /**
     * DOC nrousseau Comment method "addMenu".
     */
    private void addMenu() {
        ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();

        Image image = ImageProvider.getImage(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_UNDO));
        Menu popupMenu = new Menu(this.getTextWidget());

        MenuItem undoItem = new MenuItem(popupMenu, SWT.PUSH);
        undoItem.setText("Undo"); //$NON-NLS-1$
        undoItem.setImage(image);
        undoItem.addListener(SWT.Selection, new Listener() {

            public void handleEvent(Event event) {
                doOperation(ITextOperationTarget.UNDO);
                event.doit = false;
            }
        });

        image = ImageProvider.getImage(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
        MenuItem redoItem = new MenuItem(popupMenu, SWT.PUSH);
        redoItem.setText("Redo"); //$NON-NLS-1$
        redoItem.setData(this);
        redoItem.setImage(image);
        redoItem.addListener(SWT.Selection, new Listener() {

            public void handleEvent(Event event) {
                doOperation(ITextOperationTarget.REDO);
                event.doit = false;
            }
        });

        // add separator
        new MenuItem(popupMenu, SWT.SEPARATOR);

        image = ImageProvider.getImage(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
        MenuItem copyItem = new MenuItem(popupMenu, SWT.PUSH);
        copyItem.setText("Copy"); //$NON-NLS-1$
        copyItem.setImage(image);
        copyItem.addListener(SWT.Selection, new Listener() {

            public void handleEvent(Event event) {
                getTextWidget().copy();
            }
        });

        image = ImageProvider.getImage(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE));
        MenuItem pasteItem = new MenuItem(popupMenu, SWT.PUSH);
        pasteItem.setText("Paste"); //$NON-NLS-1$
        pasteItem.setData(this);
        pasteItem.setImage(image);
        pasteItem.addListener(SWT.Selection, new Listener() {

            public void handleEvent(Event event) {
                getTextWidget().paste();
            }
        });

        MenuItem selectAllItem = new MenuItem(popupMenu, SWT.PUSH);
        selectAllItem.setText("Select All"); //$NON-NLS-1$
        selectAllItem.addListener(SWT.Selection, new Listener() {

            public void handleEvent(Event event) {
                getTextWidget().selectAll();
            }
        });
        getTextWidget().setMenu(popupMenu);

        Listener selectAllListener = new Listener() {

            public void handleEvent(Event event) {
                if (event.character == '\u0001') { // CTRL + A
                    getTextWidget().selectAll();
                }
            }
        };

        getTextWidget().addListener(SWT.KeyDown, selectAllListener);

    }

    /**
     * Example: configure(new TalendPerlSourceViewerConfiguration(PerlEditorPlugin .getDefault().getPreferenceStore(),
     * this));.
     */
    protected abstract void installViewerConfiguration();

    protected SourceViewerDecorationSupport getSourceViewerDecorationSupport() {
        if (fSourceViewerDecorationSupport == null) {
            fSourceViewerDecorationSupport = new SourceViewerDecorationSupport(this, fOverviewRuler,
                    annotationAccess, sharedColors);
            configureSourceViewerDecorationSupport(fSourceViewerDecorationSupport);
        }
        return fSourceViewerDecorationSupport;
    }

    protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {

        Iterator e = fAnnotationPreferences.getAnnotationPreferences().iterator();
        while (e.hasNext()) {
            support.setAnnotationPreference((AnnotationPreference) e.next());
        }

        support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR);
        support.setMarginPainterPreferenceKeys(PRINT_MARGIN, PRINT_MARGIN_COLOR, PRINT_MARGIN_COLUMN);
        support.setSymbolicFontName(JFaceResources.TEXT_FONT);
    }

    public abstract void updateContents();

    protected void initializeModel() {
        ProjectionSupport projectionSupport = new ProjectionSupport(this, annotationAccess, sharedColors);
        projectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$
        projectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$
        projectionSupport.setHoverControlCreator(new IInformationControlCreator() {

            public IInformationControl createInformationControl(Shell shell) {
                return new SourceViewerInformationControl(shell, false, SWT.LEFT_TO_RIGHT,
                        EditorsUI.getTooltipAffordanceString());
            }
        });
        projectionSupport.setInformationPresenterControlCreator(new IInformationControlCreator() {

            public IInformationControl createInformationControl(Shell shell) {
                int shellStyle = SWT.RESIZE | SWT.TOOL | SWT.LEFT_TO_RIGHT;
                int style = SWT.V_SCROLL | SWT.H_SCROLL;
                return new SourceViewerInformationControl(shell, true, SWT.LEFT_TO_RIGHT, null);
            }
        });
        projectionSupport.install();
    }

    private void handleVerifyKeyPressed(VerifyEvent event) {
        if (!event.doit) {
            return;
        }

        if (event.stateMask != SWT.CTRL) {
            return;
        }

        switch (event.character) {
        case ' ':
            doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
            event.doit = false;
            break;

        case '.':
            doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
            event.doit = false;
            break;

        // CTRL-Y
        case (int) 'y' - (int) 'a' + 1:
            doOperation(ITextOperationTarget.REDO);
            event.doit = false;
            break;

        // CTRL-Z
        case (int) 'z' - (int) 'a' + 1:
            doOperation(ITextOperationTarget.UNDO);
            event.doit = false;
            break;
        default:
        }
    }

    // /**
    // * Getter for source.
    // *
    // * @return the source
    // */
    // public SourceFile getSource() {
    // return this.source;
    // }

    /**
     * Getter for file.
     * 
     * @return the file
     */
    public IFile getFile() {
        return this.file;
    }

    /**
     * Adds support for dropping items into this viewer via a user drag-and-drop operation.
     * 
     * @param operations a bitwise OR of the supported drag and drop operation types ( <code>DROP_COPY</code>,
     * <code>DROP_LINK</code>, and <code>DROP_MOVE</code>)
     * @param transferTypes the transfer types that are supported by the drop operation
     * @param listener the callback that will be invoked after the drag and drop operation finishes
     * @see org.eclipse.swt.dnd.DND
     */
    public void addDropSupport(int operations, Transfer[] transferTypes, final DropTargetListener listener) {
        Control control = getTextWidget();
        DropTarget dropTarget = new DropTarget(control, operations);
        dropTarget.setTransfer(transferTypes);
        dropTarget.addDropListener(listener);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.text.source.projection.ProjectionViewer#handleDispose()
     */
    @Override
    protected void handleDispose() {
        if (!getControl().isDisposed()) {
            super.handleDispose();
        }
        if (file != null && file.exists()) {
            try {
                file.delete(false, new NullProgressMonitor());
            } catch (CoreException e1) {
                // do nothing as the delete is not important.
            }
        }
    }

    public IRegion getViewerRegion() {
        if (viewerStartRegion == null) {
            return new Region(0, getDocument().getLength());
        }
        return new Region(viewerStartRegion.getLength(),
                getDocument().getLength() - viewerStartRegion.getLength() - viewerEndRegion.getLength());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.text.source.projection.ProjectionViewer#setVisibleRegion (int, int)
     */
    @Override
    public void setVisibleRegion(int start, int length) {
        viewerStartRegion = new Region(0, start);
        if (getDocument() == null) {
            return;
        }
        if (getDocument().getLength() > start) {
            viewerEndRegion = new Region(start + 1 + length, getDocument().getLength() - start - length);
        } else {
            viewerEndRegion = new Region(start, 0);
        }
        super.setVisibleRegion(start, length);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.text.TextViewer#createTextWidget(org.eclipse.swt.widgets .Composite, int)
     */
    @Override
    protected StyledText createTextWidget(Composite parent, int styles) {
        return new ReconcilerStyledText(parent, styles, this);
    }

    /**
     * 
     * ggu Comment method "updateVisibleRegion".
     */
    protected void updateVisibleRegion() {
        if (this.getDocument() == null || this.viewerEndRegion == null || this.getVisibleDocument() == null) {
            return;
        }
        final String docText = this.getDocument().get();
        final int newDocLength = docText.length();
        if (this.oldDocLength != newDocLength) {
            final String visibleText = this.getVisibleDocument().get(); // get
            // visible
            // text

            final int newLength = visibleText.length();
            final int newStart = newDocLength - newLength - this.viewerEndRegion.getLength();

            setVisibleRegion(newStart, newLength);

            this.oldDocLength = newDocLength;
        }
    }

    /**
     * Getter for hostNode.
     * 
     * @return the hostNode
     */
    public INode getHostNode() {
        return this.hostNode;
    }

    /**
     * Sets the hostNode.
     * 
     * @param hostNode the hostNode to set
     */
    public void setHostNode(INode hostNode) {
        this.hostNode = hostNode;
    }

    @Override
    public void setDocument(IDocument document, IAnnotationModel annotationModel, int modelRangeOffset,
            int modelRangeLength) {
        super.setDocument(document, annotationModel, modelRangeOffset, modelRangeLength);
        if (document != null) {
            addDocumentListener(document);
        }
    }
}