v7cr.ReviewTab.java Source code

Java tutorial

Introduction

Here is the source code for v7cr.ReviewTab.java

Source

/**
 * Copyright (c) 2011-2012, Thilo Planz. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package v7cr;

import static org.apache.commons.lang.ArrayUtils.EMPTY_STRING_ARRAY;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DurationFormatUtils;
import org.bson.types.ObjectId;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.vaadin.easyuploads.MultiFileUpload;

import v7cr.v7db.BSONBackedObject;
import v7cr.v7db.LocalizedString;
import v7cr.v7db.SchemaDefinition;

import com.vaadin.terminal.ExternalResource;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.Tree;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Window.Notification;

@SuppressWarnings("serial")
public class ReviewTab extends CustomComponent implements ClickListener {

    final ObjectId reviewId;

    private Review r;

    private TextArea newComment;

    private OptionGroup voteOptions;

    private ComponentContainer fileArea;

    ReviewTab(ObjectId id) {
        setIcon(new ThemeResource("../runo/icons/16/document-txt.png"));

        reviewId = id;

        reload();
    }

    private void reload() {
        final V7CR v7 = V7CR.getInstance();
        r = new Review(v7.load("reviews", reviewId));
        Project p = new Project(v7.load("projects", r.getProjectName()));
        SVNLogEntry svn = r.getSVNLogEntry();
        String url;
        if (svn != null) {
            url = r.getProjectName() + "-" + svn.getRevision();
            setCaption(url);
        } else {
            url = reviewId.toString();
            setCaption(StringUtils.abbreviate(r.getTitle(), 20));
        }
        VerticalLayout vl = new VerticalLayout();
        vl.setSizeFull();

        vl.addComponent(getBasicInfo(v7, r, p, url));
        Panel s = getSVNPanel(v7, r.getSchemaDefinition(), svn, p);
        if (s != null)
            vl.addComponent(s);

        final BSONBackedObject[] notes = r.getObjectFieldAsArray("notes");
        if (notes != null) {
            for (BSONBackedObject note : notes) {
                vl.addComponent(getNotesPanel(note));
            }
        }

        final BSONBackedObject[] votes = r.getObjectFieldAsArray("v");
        if (votes != null) {
            for (BSONBackedObject vote : votes) {
                vl.addComponent(new CommentPanel(vote));
            }

        }

        {
            HorizontalLayout commentGrid = new HorizontalLayout();

            newComment = new TextArea();
            newComment.setColumns(50);
            newComment.setRows(10);
            commentGrid.addComponent(newComment);
            voteOptions = new OptionGroup();
            voteOptions.addItem("+");
            voteOptions.addItem("0");
            voteOptions.addItem("-");
            voteOptions.setValue("0");
            commentGrid.addComponent(voteOptions);

            vl.addComponent(commentGrid);
            fileArea = new VerticalLayout();
            vl.addComponent(fileArea);
            MultiFileUpload uploader = new MultiFileUpload() {

                @Override
                protected void handleFile(File file, String fileName, String mimeType, long length) {
                    try {
                        BSONBackedObject gf = v7.storeFile(file, fileName, mimeType);
                        TemporaryFile tf = new TemporaryFile(v7, gf);
                        fileArea.addComponent(tf);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            };

            vl.addComponent(uploader);

            Button submitButton = new Button(v7.getMessage("button.submit"));
            submitButton.addListener(this);
            vl.addComponent(submitButton);
        }

        setCompositionRoot(vl);

        Component parent = getParent();
        if (parent instanceof TabSheet) {
            TabSheet t = (TabSheet) parent;
            Iterator<Component> i = t.getComponentIterator();
            while (i.hasNext()) {
                Component c = i.next();
                if (c instanceof ReviewList && r.getProjectName().equals(c.getCaption())) {
                    ((ReviewList) c).reload();
                    break;
                }
            }
        }
    }

    class CommentPanel extends CustomComponent {

        private final Panel p;

        private final BSONBackedObject data;

        CommentPanel(BSONBackedObject vote) {
            p = new Panel();
            data = vote;
            p.setWidth("600px");
            makeNotEditable();
        }

        private void makeNotEditable() {
            final GridLayout grid = new GridLayout(3, 4);
            grid.setSizeFull();
            p.setContent(grid);
            grid.setSpacing(true);

            Date created = data.getDateField("d");

            p.addComponent(new Label(data.getStringField("by.n")));
            p.addComponent(new Label(data.getStringField("by._id")));
            p.addComponent(new Label(DateFormat.getDateTimeInstance().format(created)));
            grid.addComponent(new Label(data.getStringField("c"), Label.CONTENT_PREFORMATTED), 0, 1, 1, 1);
            Label icon = new Label();
            String v = data.getStringField("v");
            if ("+".equals(v)) {
                icon.setIcon(new ThemeResource("../runo/icons/32/ok.png"));
            }
            if ("-".equals(v)) {
                icon.setIcon(new ThemeResource("../runo/icons/32/attention.png"));
            }
            p.addComponent(icon);
            setCompositionRoot(p);
            V7CR v7 = V7CR.getInstance();

            BSONBackedObject[] files = data.getObjectFieldAsArray("files");
            if (files != null) {
                for (BSONBackedObject f : files) {
                    Link downloadLink = v7.getFile(f);
                    if (downloadLink != null)
                        grid.addComponent(downloadLink, 0, grid.getCursorY(), 2, grid.getCursorY());
                }
            }

            // the last comment can still be edited for some time
            long timeLeft = created.getTime() + 30 * 60 * 1000 - System.currentTimeMillis();
            if (timeLeft > 0) {

                p.addComponent(new Button(v7.getMessage("button.edit"), new Button.ClickListener() {

                    public void buttonClick(ClickEvent event) {
                        makeEditable();
                    }
                }

                ));
                p.addComponent(new Button(v7.getMessage("button.delete"), new Button.ClickListener() {

                    public void buttonClick(ClickEvent event) {
                        V7CR v7cr = V7CR.getInstance();
                        v7cr.update("reviews", r.deleteVote(data));
                        reload();
                    }
                }));
                p.addComponent(
                        new Label(DurationFormatUtils.formatDurationWords(timeLeft, true, true) + " left to edit"));
            }
            ;

        }

        private void makeEditable() {
            final GridLayout grid = new GridLayout(3, 4);
            grid.setSizeFull();
            p.setContent(grid);
            grid.setSpacing(true);
            V7CR v7 = V7CR.getInstance();

            final Date created = data.getDateField("d");

            p.addComponent(new Label(data.getStringField("by.n")));
            p.addComponent(new Label(data.getStringField("by._id")));
            p.addComponent(new Label(DateFormat.getDateTimeInstance().format(created)));
            final TextArea textArea = new TextArea(null, data.getStringField("c"));
            textArea.setSizeFull();
            grid.addComponent(textArea, 0, 1, 1, 1);
            final OptionGroup voteOptions = new OptionGroup();
            voteOptions.addItem("+");
            voteOptions.addItem("0");
            voteOptions.addItem("-");
            voteOptions.setValue("0");
            grid.addComponent(voteOptions);
            p.addComponent(new Button(v7.getMessage("button.submit"), new Button.ClickListener() {

                public void buttonClick(ClickEvent event) {
                    V7CR v7cr = V7CR.getInstance();
                    v7cr.update("reviews",
                            r.updateVote(data, textArea.getValue().toString(), voteOptions.getValue().toString()));
                    reload();

                    //               
                    // V7CR.getInstance().getDBCollection("reviews").update(
                    // new BasicDBObject("_id", reviewId).append("v.d",
                    // created),
                    // new BasicDBObject("$set", new BasicDBObject(
                    // "v.$.c", textArea.getValue()).append(
                    // "v.$.v", voteOptions.getValue())));
                }
            }

            ));
            p.addComponent(new Button(v7.getMessage("button.cancel"), new Button.ClickListener() {

                public void buttonClick(ClickEvent event) {
                    makeNotEditable();
                }
            }));
            p.addComponent(new Button(v7.getMessage("button.delete"), new Button.ClickListener() {

                public void buttonClick(ClickEvent event) {
                    V7CR v7cr = V7CR.getInstance();
                    v7cr.update("reviews", r.deleteVote(data));
                    reload();

                }
            }));

        }
    }

    private Panel getBasicInfo(V7CR v7, Review r, Project proj, String linkUrl) {

        Panel p = new Panel(v7.getMessage("reviewTab.review"));
        p.setWidth("600px");
        GridLayout grid = new GridLayout(3, 4);
        grid.setSizeFull();
        p.setContent(grid);
        grid.setSpacing(true);
        Locale l = v7.getLocale();
        SchemaDefinition sd = r.getSchemaDefinition();
        grid.addComponent(new Label(sd.getFieldCaption("s", l)), 0, 0, 1, 0);
        p.addComponent(new Label("<b>" + LocalizedString
                .get(sd.getFieldDefinition("s").getPossibleValueMetaData(r.getStatus()), "caption", l) + "</b>",
                Label.CONTENT_XHTML));
        p.addComponent(new Label(sd.getFieldCaption("p", l)));
        p.addComponent(new Label("[" + proj.getId() + "]"));
        grid.addComponent(new Label(proj.getName()));
        p.addComponent(new Label(sd.getFieldCaption("reviewee", l)));
        p.addComponent(new Label(r.getReviewee().getId()));
        grid.addComponent(new Label(r.getReviewee().getName()));
        p.addComponent(new Label(sd.getFieldCaption("t", l)));
        grid.addComponent(new Label(r.getTitle()), 1, 3, 2, 3);
        p.addComponent(new Label(v7.getMessage("reviewTab.directLink")));
        Link link = new Link(linkUrl, new ExternalResource(linkUrl));
        link.setTargetName("_blank");
        link.setIcon(new ThemeResource("../runo/icons/16/arrow-right.png"));
        grid.addComponent(link);
        return p;

    }

    private Panel getNotesPanel(BSONBackedObject note) {
        Panel p = new Panel(note.getStringField("t"));
        p.setWidth("600px");
        Label c = new Label(note.getStringField("c"));
        p.addComponent(c);

        String v = note.getStringField("v");
        if ("+".equals(v)) {
            c.setIcon(new ThemeResource("../runo/icons/16/ok.png"));
        }
        if ("-".equals(v)) {
            c.setIcon(new ThemeResource("../runo/icons/16/attention.png"));
        }
        return p;
    }

    private Panel getSVNPanel(V7CR v7, SchemaDefinition sd, SVNLogEntry svn, Project proj) {
        if (svn == null)
            return null;
        Locale l = v7.getLocale();
        Panel p = new Panel(v7.getMessage("reviewTab.subversion"));
        p.setWidth("600px");
        GridLayout grid = new GridLayout(4, 4);
        grid.setSizeFull();
        p.setContent(grid);
        grid.setSpacing(true);
        p.addComponent(new Label(sd.getFieldCaption("svn.rev", l)));
        p.addComponent(new Label("" + svn.getRevision()));
        p.addComponent(new Label(DateFormat.getDateTimeInstance().format(svn.getDate())));
        p.addComponent(new Label(svn.getAuthor()));
        Link link = new Link(v7.getMessage("reviewTab.viewChanges"),
                new ExternalResource(proj.getChangesetViewUrl(svn.getRevision())));
        link.setTargetName("_blank");
        link.setIcon(new ThemeResource("../runo/icons/16/arrow-right.png"));
        p.addComponent(link);
        grid.addComponent(new Label(svn.getMessage()), 1, 1, 3, 1);

        Map<String, SVNLogEntryPath> changed = svn.getChangedPaths();

        if (changed != null) {
            Tree changeTree = new Tree(sd.getFieldCaption("svn.changed", l) + "(" + changed.size() + ")");
            Set<String> paths = changed.keySet();
            for (String s : changed.keySet()) {
                changeTree.addItem(s);
                changeTree.setChildrenAllowed(s, false);
                changeTree.setItemCaption(s, changed.get(s).getType() + " " + s);
            }
            if (paths.size() > 5) {
                compressTree(changeTree, paths);
            }

            grid.addComponent(changeTree, 0, 2, 3, 2);
        }
        return p;
    }

    private String connectPath(Tree tree, String node) {
        tree.addItem(node);
        tree.setChildrenAllowed(node, true);
        String pp = StringUtils.substringBeforeLast(StringUtils.substringBeforeLast(node, "/"), "/") + "/";
        while (true) {
            if (tree.containsId(pp)) {
                tree.setParent(node, pp);
                return pp;
            }
            pp = StringUtils.substringBeforeLast(StringUtils.substringBeforeLast(pp, "/"), "/") + "/";
            if ("/".equals(pp))
                return pp;
        }
    }

    // introduce intermediate nodes (for common prefixes) to make the tree more
    // even
    private void compressTree(Tree tree, Collection<String> nodes) {

        List<String> orgPaths = new ArrayList<String>(nodes);

        int end = orgPaths.size();

        for (int start = 0; start < end; start++) {
            String prefix = StringUtils.getCommonPrefix(orgPaths.subList(start, end).toArray(EMPTY_STRING_ARRAY));
            if (!prefix.endsWith("/")) {
                prefix = StringUtils.substringBeforeLast(prefix, "/") + "/";
            }
            String first = orgPaths.get(start);
            String extendedPrefix = prefix + StringUtils.substringBefore(first.substring(prefix.length()), "/");
            if (!extendedPrefix.equals(first)) {
                extendedPrefix += "/";
                if (tree.containsId(prefix)) {
                    tree.addItem(extendedPrefix);
                    tree.setParent(extendedPrefix, prefix);
                } else {
                    tree.setParent(extendedPrefix, connectPath(tree, extendedPrefix));
                }
                tree.setChildrenAllowed(extendedPrefix, true);
                int last;
                for (last = start; last < end; last++) {
                    String l = orgPaths.get(last);
                    if (l.startsWith(extendedPrefix)) {
                        tree.setParent(l, extendedPrefix);
                    } else {
                        last++;
                        break;
                    }
                }
                tree.setItemCaption(extendedPrefix, extendedPrefix + "(" + (last - 1 - start) + ")");

                if (last - start > 5) {
                    compressTree(tree, orgPaths.subList(start, last - 1));
                }
                start = last - 1;

            } else {
                if (!tree.containsId(prefix)) {
                    prefix = connectPath(tree, prefix);
                }

                if (!prefix.equals(first)) {
                    tree.setParent(first, prefix);
                }

            }
        }

    }

    public void buttonClick(ClickEvent event) {
        String comment = (String) newComment.getValue();
        String vote = (String) voteOptions.getValue();

        if (StringUtils.isBlank(comment)) {
            getWindow().showNotification(null, "Please leave a comment", Notification.TYPE_WARNING_MESSAGE);
            return;
        }
        if (StringUtils.isBlank(vote)) {
            getWindow().showNotification(null, "Please vote", Notification.TYPE_WARNING_MESSAGE);
            return;
        }

        V7CR v7cr = V7CR.getInstance();
        v7cr.update("reviews", r.addVote(v7cr.getSessionUser(), new Date(), comment, vote, fileArea));

        reload();
    }
}