org.groom.review.ui.flows.ReviewFileDiffFlowlet.java Source code

Java tutorial

Introduction

Here is the source code for org.groom.review.ui.flows.ReviewFileDiffFlowlet.java

Source

/**
 * Copyright 2013 Tommi S.E. Laukkanen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.groom.review.ui.flows;

import com.vaadin.event.ShortcutAction;
import com.vaadin.ui.*;
import org.bubblecloud.ilves.component.flow.AbstractFlowlet;
import org.bubblecloud.ilves.model.Company;
import org.bubblecloud.ilves.util.EmailUtil;
import org.groom.shell.BlameReader;
import org.groom.review.dao.ReviewDao;
import org.groom.review.ui.flows.reviewer.ReviewFlowlet;
import org.groom.review.model.*;
import org.vaadin.aceeditor.AceEditor;
import org.vaadin.aceeditor.AceMode;
import org.vaadin.aceeditor.client.AceAnnotation;
import org.vaadin.aceeditor.client.AceMarker;
import org.vaadin.aceeditor.client.AceRange;

import javax.persistence.EntityManager;
import java.util.Date;
import java.util.List;

/**
 * Review list Flowlet.
 *
 * @author Tommi S.E. Laukkanen
 */
public final class ReviewFileDiffFlowlet extends AbstractFlowlet {

    /** Serial version UID. */
    private static final long serialVersionUID = 1L;
    /** The grid. */
    private Grid grid;

    private String path;

    private AceEditor editor;
    private List<BlameLine> blames;
    private FileDiff fileDiff;
    private EntityManager entityManager;
    private Review review;
    private AceEditor.SelectionChangeListener selectionChangeListener;
    private GridLayout gridLayout;
    private Button groomButton;
    private HorizontalLayout editorLayout;

    @Override
    public String getFlowletKey() {
        return "review-file-diff";
    }

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

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

    private static int findLine(final String value, final int index) {
        int line = 0;
        for (int i = 0; i < Math.min(value.length(), index); i++) {
            if (value.charAt(i) == '\n') {
                line++;
            }
        }
        return line;
    }

    @Override
    public void initialize() {
        entityManager = getSite().getSiteContext().getObject(EntityManager.class);

        gridLayout = new GridLayout(1, 2);
        gridLayout.setSpacing(true);
        gridLayout.setSizeFull();
        gridLayout.setColumnExpandRatio(0, 1f);
        gridLayout.setRowExpandRatio(1, 1f);
        setViewContent(gridLayout);

        selectionChangeListener = new AceEditor.SelectionChangeListener() {
            @Override
            public void selectionChanged(AceEditor.SelectionChangeEvent e) {

            }
        };

        //gridLayout.addComponent(editor, 0, 0);

        if (getFlow().getFlowlet(ReviewFlowlet.class) != null) {
            final HorizontalLayout buttonLayout = new HorizontalLayout();
            buttonLayout.setSpacing(true);
            gridLayout.addComponent(buttonLayout, 0, 0);

            final Button previousButton = new Button(getSite().localize("button-previous-diff"));
            previousButton.setClickShortcut(ShortcutAction.KeyCode.ARROW_LEFT);
            previousButton.setHtmlContentAllowed(true);
            buttonLayout.addComponent(previousButton);
            previousButton.addClickListener(new Button.ClickListener() {
                @Override
                public void buttonClick(Button.ClickEvent clickEvent) {
                    final ReviewFlowlet view = getFlow().getFlowlet(ReviewFlowlet.class);
                    view.previous(path);
                }
            });

            final Button nextButton = new Button(getSite().localize("button-next-diff"));
            nextButton.setClickShortcut(ShortcutAction.KeyCode.ARROW_RIGHT);
            nextButton.setHtmlContentAllowed(true);
            buttonLayout.addComponent(nextButton);
            nextButton.addClickListener(new Button.ClickListener() {
                @Override
                public void buttonClick(Button.ClickEvent clickEvent) {
                    final ReviewFlowlet view = getFlow().getFlowlet(ReviewFlowlet.class);
                    view.next(path);
                }
            });

            final Button scrollToPreviousChangeButton = new Button(
                    getSite().localize("button-scroll-to-previous-change"));
            scrollToPreviousChangeButton.setClickShortcut(ShortcutAction.KeyCode.ARROW_UP);
            scrollToPreviousChangeButton.setHtmlContentAllowed(true);
            buttonLayout.addComponent(scrollToPreviousChangeButton);
            scrollToPreviousChangeButton.addClickListener(new Button.ClickListener() {
                @Override
                public void buttonClick(Button.ClickEvent clickEvent) {
                    int cursorLine = getScrolledTowRow();

                    for (int i = cursorLine; i >= 0; i--) {
                        cursorLine = i;
                        if (blames.get(i).getType() == LineChangeType.NONE) {
                            break;
                        }
                    }
                    for (int i = cursorLine; i >= 0; i--) {
                        cursorLine = i;
                        if (i == 0 | blames.get(i).getType() != LineChangeType.NONE) {
                            break;
                        }
                    }
                    for (int i = cursorLine; i >= 0; i--) {
                        if (i == 0) {
                            final ReviewFlowlet view = getFlow().getFlowlet(ReviewFlowlet.class);
                            //view.previous(path);
                            scrollToRow(0);
                            break;
                        }
                        if (blames.get(i).getType() == LineChangeType.NONE) {
                            scrollToRow(i + 1);
                            break;
                        }
                    }
                }
            });

            final Button scrollToNextChangeButton = new Button(getSite().localize("button-scroll-to-next-change"));
            scrollToNextChangeButton.setClickShortcut(ShortcutAction.KeyCode.ARROW_DOWN);
            scrollToNextChangeButton.setHtmlContentAllowed(true);
            buttonLayout.addComponent(scrollToNextChangeButton);
            scrollToNextChangeButton.addClickListener(new Button.ClickListener() {
                @Override
                public void buttonClick(Button.ClickEvent clickEvent) {
                    int cursorLine = getScrolledTowRow();

                    for (int i = cursorLine; i < blames.size(); i++) {
                        cursorLine = i;
                        if (blames.get(i).getType() == LineChangeType.NONE) {
                            break;
                        }
                    }
                    for (int i = cursorLine; i < blames.size(); i++) {
                        if (i == blames.size() - 1) {
                            final ReviewFlowlet view = getFlow().getFlowlet(ReviewFlowlet.class);
                            //view.next(path);
                            scrollToRow(i);
                            break;
                        }
                        if (blames.get(i).getType() != LineChangeType.NONE) {
                            scrollToRow(i);
                            break;
                        }
                    }
                }
            });

            final Button scrollToCursorButton = new Button("Scroll to Cursor");
            scrollToCursorButton.setHtmlContentAllowed(true);
            buttonLayout.addComponent(scrollToCursorButton);
            scrollToCursorButton.addClickListener(new Button.ClickListener() {
                @Override
                public void buttonClick(Button.ClickEvent clickEvent) {
                    if (editor.getSelection() == null || editor.getCursorPosition() < 0) {
                        return;
                    }
                    int cursor = editor.getCursorPosition();
                    if (fileDiff.getReviewStatus() != null) {
                        final int cursorLine = findLine(editor.getValue(), cursor);
                        scrollToRow(cursorLine);
                    }
                }
            });

            groomButton = getSite().getButton("groom");
            groomButton.setClickShortcut(ShortcutAction.KeyCode.ENTER);
            groomButton.setHtmlContentAllowed(true);
            buttonLayout.addComponent(groomButton);
            groomButton.addClickListener(new Button.ClickListener() {
                @Override
                public void buttonClick(Button.ClickEvent clickEvent) {
                    if (editor.getSelection() == null || editor.getCursorPosition() < 0) {
                        return;
                    }
                    int cursor = editor.getCursorPosition();
                    if (fileDiff.getReviewStatus() != null) {
                        final int cursorLine = findLine(editor.getValue(), cursor);
                        final BlameLine blame = blames.get(cursorLine);

                        final CommentDialog commentDialog = new CommentDialog(new CommentDialog.DialogListener() {
                            @Override
                            public void onOk(final String message, final int severity) {
                                final ReviewStatus reviewStatus = fileDiff.getReviewStatus();
                                final Review review = reviewStatus.getReview();
                                final Date date = new Date();
                                if (message.trim().length() > 0) {
                                    final Comment comment = new Comment(review, reviewStatus.getReviewer(),
                                            blame.getHash(), fileDiff.getPath(), blame.getFinalLine(), cursorLine,
                                            severity, message, blame.getAuthorName(), blame.getCommitterName(),
                                            date, date);
                                    ReviewDao.saveComment(entityManager, comment);
                                    addComment(comment);
                                    final Company company = getSite().getSiteContext().getObject(Company.class);
                                    final Thread emailThread = new Thread(new Runnable() {
                                        @Override
                                        public void run() {
                                            String severity;
                                            switch (comment.getSeverity()) {
                                            case 1:
                                                severity = "Kudo";
                                                break;
                                            case -1:
                                                severity = "Warning";
                                                break;
                                            case -2:
                                                severity = "Red Flag";
                                                break;
                                            default:
                                                severity = Integer.toString(comment.getSeverity());
                                                if (!severity.startsWith("-")) {
                                                    severity = "+" + severity;
                                                }
                                            }
                                            EmailUtil.send(blame.getAuthorEmail(),
                                                    reviewStatus.getReviewer().getEmailAddress(),
                                                    severity + " from review '" + review.getTitle() + "'",
                                                    "Reviewer: " + reviewStatus.getReviewer().getFirstName() + " "
                                                            + reviewStatus.getReviewer().getLastName() + "\n"
                                                            + "Commit: " + blame.getHash() + "\n" + "File: "
                                                            + fileDiff.getPath() + "\n" + "Original Line: "
                                                            + blame.getOriginalLine() + "\n" + "Diff line: "
                                                            + cursorLine + "\n" + blame.getType() + ":"
                                                            + blame.getLine() + "\n" + "Message: " + message);
                                        }
                                    });
                                    emailThread.start();
                                }
                            }

                            public void onCancel() {
                                //To change body of implemented methods use File | Settings | File Templates.
                            }
                        });
                        int cursorPosition = editor.getCursorPosition();
                        commentDialog.setCaption("Please enter groom text for " + blame.getAuthorName()
                                + " at line: " + (cursorLine + 1));
                        UI.getCurrent().addWindow(commentDialog);
                        commentDialog.getTextArea().focus();

                    }
                }
            });

        }
    }

    public int getScrolledTowRow() {
        return scrolledTowRow;
    }

    private int scrolledTowRow = 0;

    private void scrollToRow(int i) {
        int scrollToRow = i - 3;
        if (scrollToRow < 0) {
            scrollToRow = 0;
        }
        scrolledTowRow = i;
        editor.scrollToRow(scrollToRow);
        editor.setCursorRowCol(i, 0);
    }

    private void addComment(Comment comment) {
        final AceAnnotation.Type type;
        switch (comment.getSeverity()) {
        case -1:
            type = AceAnnotation.Type.warning;
            break;
        case -2:
            type = AceAnnotation.Type.error;
            break;
        default:
            type = AceAnnotation.Type.info;
        }
        editor.addRowAnnotation(
                new AceAnnotation("Groomed by reviewer: " + comment.getReviewer().getFirstName() + " - "
                        + comment.getReviewer().getLastName() + " Message: " + comment.getMessage(), type),
                comment.getDiffLine());
    }

    @Override
    public void enter() {
        groomButton.focus();
    }

    public void setFileDiff(final Review review, final FileDiff fileDiff, final int toLine) {

        if (editorLayout != null) {
            gridLayout.removeComponent(editorLayout);
        }

        editor = new AceEditor();
        editorLayout = new HorizontalLayout();
        editorLayout.setWidth(100, Unit.PERCENTAGE);
        editorLayout.setHeight(UI.getCurrent().getPage().getBrowserWindowHeight() - 350, Unit.PIXELS);
        editorLayout.addComponent(editor);

        final Company company = getSite().getSiteContext().getObject(Company.class);
        editor.setThemePath(company.getUrl() + "/../static/ace");
        editor.setModePath(company.getUrl() + "/../static/ace");
        editor.setWorkerPath(company.getUrl() + "/../static/ace");
        editor.setSizeFull();
        editor.setImmediate(true);
        editor.addSelectionChangeListener(selectionChangeListener);

        this.review = review;
        this.fileDiff = fileDiff;
        this.path = fileDiff.getPath();

        final char status = fileDiff.getStatus();
        final String sinceHash = review.getSinceHash();
        final String untilHash = review.getUntilHash();

        blames = BlameReader.readBlameLines(review.getRepository().getPath(), path, status, sinceHash, untilHash);

        if (path.endsWith(".java")) {
            editor.setMode(AceMode.java);
        } else if (path.endsWith(".xml")) {
            editor.setMode(AceMode.xml);
        } else if (path.endsWith(".js")) {
            editor.setMode(AceMode.javascript);
        } else if (path.endsWith(".css")) {
            editor.setMode(AceMode.css);
        } else if (path.endsWith(".jsp")) {
            editor.setMode(AceMode.jsp);
        } else {
            editor.setMode(AceMode.asciidoc);
        }

        final StringBuilder builder = new StringBuilder();
        for (int i = 0; i < blames.size(); i++) {
            final BlameLine line = blames.get(i);
            builder.append(line.getLine());
            if (i < blames.size() - 1) {
                builder.append('\n');
            }
            /*switch (line.getType()) {
            case ADDED:
                builder.append("[+]");
                break;
            case DELETED:
                builder.append("[-]");
                break;
            default:
                break;
            }*/
        }

        editor.setCaption(
                "Progress: " + fileDiff.getReviewStatus().getProgress() + "% - " + "Path : " + fileDiff.getPath());
        editor.setValue(builder.toString());
        editor.setReadOnly(true);
        BlameLine lastLine = null;
        int lastIndex = -1;
        for (int i = 0; i < blames.size(); i++) {
            final BlameLine blameLine = blames.get(i);
            if (lastLine != null && (!blameLine.getHash().equals(lastLine.getHash())
                    || !blameLine.getType().equals(lastLine.getType()))) {

                if (lastLine.getType() == LineChangeType.ADDED) {
                    editor.addMarker(new AceRange(lastIndex, 0, i, 0), "marker-line-added", AceMarker.Type.line,
                            false, AceMarker.OnTextChange.REMOVE);
                } else if (lastLine.getType() == LineChangeType.DELETED) {
                    editor.addMarker(new AceRange(lastIndex, 0, i, 0), "marker-line-deleted", AceMarker.Type.line,
                            false, AceMarker.OnTextChange.REMOVE);
                }

                lastLine = null;
                lastIndex = -1;
            }
            if (lastLine == null && (blameLine.getType() == LineChangeType.ADDED
                    || blameLine.getType() == LineChangeType.DELETED)) {
                lastLine = blameLine;
                lastIndex = i;

                if (blameLine.getType() == LineChangeType.ADDED) {
                    editor.addRowAnnotation(
                            new AceAnnotation(
                                    "Added by author: " + blameLine.getAuthorName() + " Commit: "
                                            + blameLine.getHash() + " Summary: " + blameLine.getSummary(),
                                    AceAnnotation.Type.info),
                            i);
                } else if (blameLine.getType() == LineChangeType.DELETED) {
                    editor.addRowAnnotation(
                            new AceAnnotation(
                                    "Deleted by author: " + blameLine.getAuthorName() + " Commit: "
                                            + blameLine.getHash() + " Summary: " + blameLine.getSummary(),
                                    AceAnnotation.Type.info),
                            i);
                }
            }
        }

        if (lastLine != null) {

            if (lastLine.getType() == LineChangeType.ADDED) {
                editor.addMarker(new AceRange(lastIndex, 0, blames.size(), 0), "marker-line-added",
                        AceMarker.Type.line, false, AceMarker.OnTextChange.REMOVE);
            } else if (lastLine.getType() == LineChangeType.DELETED) {
                editor.addMarker(new AceRange(lastIndex, 0, blames.size(), 0), "marker-line-deleted",
                        AceMarker.Type.line, false, AceMarker.OnTextChange.REMOVE);
            }

        }

        final List<Comment> comments = ReviewDao.getComments(entityManager, review);
        for (final Comment comment : comments) {
            if (comment.getPath().equals(fileDiff.getPath())) {
                addComment(comment);
            }
        }

        /*if (toLine != 0) {
        editor.setSelectionRowCol(toLine, 0, toLine + 1, 0);
        }*/
        gridLayout.addComponent(editorLayout, 0, 1);
        scrollToRow(toLine);

    }

}