hu.pagavcs.client.gui.ResolveConflictGui.java Source code

Java tutorial

Introduction

Here is the source code for hu.pagavcs.client.gui.ResolveConflictGui.java

Source

package hu.pagavcs.client.gui;

import hu.pagavcs.client.bl.Manager;
import hu.pagavcs.client.bl.OnSwing;
import hu.pagavcs.client.bl.ThreadAction;
import hu.pagavcs.client.gui.platform.Frame;
import hu.pagavcs.client.gui.platform.GuiHelper;
import hu.pagavcs.client.gui.platform.Label;
import hu.pagavcs.client.gui.platform.MessagePane;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.ToolTipManager;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNConflictChoice;
import org.tmatesoft.svn.core.wc.SVNWCClient;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * PagaVCS 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 2 of the License, or (at your option) any later
 * version.<br>
 * <br>
 * PagaVCS 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.<br>
 * <br>
 * You should have received a copy of the GNU General Public License along with
 * PagaVCS; If not, see http://www.gnu.org/licenses/.
 */
public class ResolveConflictGui {

    private static final String ATTRIBUTE_TYPE_KEY = "PAGAVCS-CONFLICT";
    private static final String ATTRIBUTE_BLOCK_ID = "PAGAVCS-BLOCKID";
    private static final String ATTRIBUTE_NORMAL = "normal";
    private static final String ATTRIBUTE_ORIGINAL = "original";
    private static final String ATTRIBUTE_WORKING = "working";
    private static final String ATTRIBUTE_THEIRS = "theirs";
    private static final String ATTRIBUTE_MIXED = "mixed";

    private TextPane tpConflict;
    private final File mixedFile;
    private final File oldFile;
    private final File newFile;
    private final File wrkFile;
    private SimpleAttributeSet setNormalText;
    private SimpleAttributeSet setConflictWorking;
    private SimpleAttributeSet setConflictTheirs;
    private SimpleAttributeSet setMixedText;
    private SimpleAttributeSet setOriginal;
    private JButton btnReload;
    private JButton btnNextConflict;
    private JButton btnSaveResolved;
    private Frame frame;
    private final Refreshable parentRefreshable;
    private final boolean applyPatchConlict;

    public ResolveConflictGui(Refreshable parentRefreshable, File mixedFile, File oldFile, File newFile,
            File wrkFile, boolean applyPatchConlict) {
        this.parentRefreshable = parentRefreshable;
        this.mixedFile = mixedFile;
        this.oldFile = oldFile;
        this.newFile = newFile;
        this.wrkFile = wrkFile;
        this.applyPatchConlict = applyPatchConlict;
    }

    public void display() throws Exception {

        new OnSwing() {

            protected void process() throws Exception {
                if (!applyPatchConlict && wrkFile == null) {
                    MessagePane.showError(null, "No conflict",
                            "Unable to resolve conflict on a non-conflicted file.");
                    return;
                }

                FormLayout layout = new FormLayout("p,1dlu:g,p,2dlu,p", "p,2dlu,fill:200dlu:g,2dlu,p");
                JPanel pnlMain = new JPanel(layout);
                CellConstraints cc = new CellConstraints();

                tpConflict = new TextPane();
                tpConflict.setBackground(Color.WHITE);
                tpConflict.setAutoscrolls(true);
                tpConflict.setPreferredSize(new Dimension(200, 200));
                GuiHelper.addUndoRedo(tpConflict);
                JScrollPane spConflict = new JScrollPane(tpConflict);
                btnReload = new JButton(new ReloadAction());
                btnNextConflict = new JButton(new GotoNextConflictAction());
                btnSaveResolved = new JButton(new SaveResolvedAction());

                pnlMain.add(new Label(mixedFile.getPath()),
                        cc.xywh(1, 1, 5, 1, CellConstraints.LEFT, CellConstraints.DEFAULT));
                pnlMain.add(spConflict, cc.xywh(1, 3, 5, 1, CellConstraints.FILL, CellConstraints.FILL));
                pnlMain.add(btnReload, cc.xy(1, 5));
                pnlMain.add(btnNextConflict, cc.xy(3, 5));
                pnlMain.add(btnSaveResolved, cc.xy(5, 5));

                setNormalText = new SimpleAttributeSet();
                setNormalText.addAttribute(ATTRIBUTE_TYPE_KEY, ATTRIBUTE_NORMAL);

                setMixedText = new SimpleAttributeSet();
                StyleConstants.setBackground(setMixedText, Color.LIGHT_GRAY);
                StyleConstants.setBold(setMixedText, true);
                setMixedText.addAttribute(ATTRIBUTE_TYPE_KEY, ATTRIBUTE_MIXED);

                setConflictWorking = new SimpleAttributeSet();
                StyleConstants.setBackground(setConflictWorking, Color.YELLOW);
                StyleConstants.setBold(setConflictWorking, true);
                setConflictWorking.addAttribute(ATTRIBUTE_TYPE_KEY, ATTRIBUTE_WORKING);

                setConflictTheirs = new SimpleAttributeSet();
                StyleConstants.setBackground(setConflictTheirs, Color.ORANGE);
                StyleConstants.setBold(setConflictTheirs, true);
                setConflictTheirs.addAttribute(ATTRIBUTE_TYPE_KEY, ATTRIBUTE_THEIRS);

                setOriginal = new SimpleAttributeSet();
                StyleConstants.setBackground(setOriginal, Color.CYAN);
                StyleConstants.setBold(setOriginal, true);
                setOriginal.addAttribute(ATTRIBUTE_TYPE_KEY, ATTRIBUTE_ORIGINAL);

                reload();

                frame = GuiHelper.createAndShowFrame(pnlMain, "Resolve Conflict");
                frame.setTitlePrefix(mixedFile.getPath());
            }

        }.run();

    }

    private void reload() throws Exception {
        btnNextConflict.setEnabled(true);
        String strMixed = Manager.loadFileToString(mixedFile);
        String[] lines = strMixed.split("\n");

        Document doc = tpConflict.getStyledDocument();
        doc.remove(0, doc.getLength());

        Integer blockId = 0;
        SimpleAttributeSet setActual = setNormalText;

        for (String line : lines) {

            if (line.contains("<<<<<<<")) {
                doc.insertString(doc.getLength(), line.substring(0, line.indexOf("<<<<<<<")), setActual);
                blockId++;
                setActual = (SimpleAttributeSet) setConflictWorking.clone();
                setActual.addAttribute(ATTRIBUTE_BLOCK_ID, blockId);
                continue;
            } else if (line.contains("|||||||")) {
                doc.insertString(doc.getLength(), line.substring(0, line.indexOf("|||||||")), setActual);
                setActual = (SimpleAttributeSet) setOriginal.clone();
                setActual.addAttribute(ATTRIBUTE_BLOCK_ID, blockId);
                continue;
            } else if (line.contains("=======")) {
                doc.insertString(doc.getLength(), line.substring(0, line.indexOf("=======")), setActual);
                setActual = (SimpleAttributeSet) setConflictTheirs.clone();
                setActual.addAttribute(ATTRIBUTE_BLOCK_ID, blockId);
                continue;
            } else if (line.contains(">>>>>>>")) {
                doc.insertString(doc.getLength(), line.substring(0, line.indexOf(">>>>>>>")), setActual);
                setActual = setNormalText;
                continue;
            }

            doc.insertString(doc.getLength(), line + "\n", setActual);
        }

        nextConflict(0);
    }

    private void nextConflict(int initialPos) throws Exception {
        Document doc = tpConflict.getStyledDocument();

        int pos = initialPos;
        while (pos < doc.getLength() && !tpConflict.getAttributeType(pos).equals(ATTRIBUTE_NORMAL)) {
            pos++;
        }
        if (pos >= doc.getLength()) {
            pos = 0;
            while (pos < initialPos && !tpConflict.getAttributeType(pos).equals(ATTRIBUTE_NORMAL)) {
                pos++;
            }
        }
        while (pos < doc.getLength() && tpConflict.getAttributeType(pos).equals(ATTRIBUTE_NORMAL)) {
            pos++;
        }
        if (pos >= doc.getLength()) {
            pos = 0;
            while (pos < initialPos && tpConflict.getAttributeType(pos).equals(ATTRIBUTE_NORMAL)) {
                pos++;
            }
        }
        int startPos = pos;

        Object startType = tpConflict.getAttributeType(pos);
        if (startType.equals(ATTRIBUTE_NORMAL) || startType.equals(ATTRIBUTE_MIXED)) {
            btnNextConflict.setEnabled(false);
        }

        while (pos < doc.getLength() && tpConflict.getAttributeType(pos).equals(ATTRIBUTE_WORKING)) {
            pos++;
        }

        while (pos < doc.getLength() && tpConflict.getAttributeType(pos).equals(ATTRIBUTE_ORIGINAL)) {
            pos++;
        }

        while (pos < doc.getLength() && tpConflict.getAttributeType(pos).equals(ATTRIBUTE_THEIRS)) {
            pos++;
        }
        int endPos = pos;

        new SetCaret(endPos);
        new SetCaret(startPos);
    }

    private class SetCaret {

        public SetCaret(final int pos) throws Exception {
            new OnSwing(true) {

                protected void process() throws Exception {
                    tpConflict.setCaretPosition(pos);
                }
            }.run();
        }
    }

    public void saveResolved() throws Exception {
        StyledDocument doc = (StyledDocument) tpConflict.getDocument();
        boolean unresolved = false;

        for (int offset = 0; offset < doc.getLength(); offset++) {
            Object attrType = tpConflict.getAttributeType(offset);
            if (attrType.equals(ATTRIBUTE_THEIRS) || attrType.equals(ATTRIBUTE_WORKING)) {
                unresolved = true;
                break;
            }
        }
        if (unresolved) {
            MessagePane.showError(frame, "Unresolved conflict(s)",
                    "There are still unresolved conflicts, it cannot be marked as resolved.");
            return;
        }

        // save
        Manager.saveStringToFile(mixedFile, doc.getText(0, doc.getLength()));

        SVNClientManager svnMgr = Manager.getSVNClientManagerForWorkingCopyOnly();
        try {
            SVNWCClient client = svnMgr.getWCClient();
            client.doResolve(mixedFile, SVNDepth.INFINITY, true, true, true, SVNConflictChoice.MERGED);
        } finally {
            svnMgr.dispose();
        }
        Manager.invalidate(mixedFile);

        if (parentRefreshable != null) {
            parentRefreshable.refresh();
        }

        frame.setVisible(false);
        frame.dispose();
    }

    private class ReloadAction extends ThreadAction {

        public ReloadAction() {
            super("Reload");
        }

        public void actionProcess(ActionEvent e) throws Exception {
            new OnSwing() {

                protected void process() throws Exception {
                    reload();
                };
            }.run();

        }

    }

    private class GotoNextConflictAction extends ThreadAction {

        public GotoNextConflictAction() {
            super("Next conflict");
        }

        public void actionProcess(ActionEvent e) throws Exception {
            new OnSwing() {

                protected void process() throws Exception {
                    nextConflict(tpConflict.getCaretPosition());
                };
            }.run();

        }

    }

    private class SaveResolvedAction extends ThreadAction {

        public SaveResolvedAction() {
            super("Save resolved");
        }

        public void actionProcess(ActionEvent e) throws Exception {
            new OnSwing() {

                protected void process() throws Exception {
                    saveResolved();
                };
            }.run();

        }

    }

    private abstract class AbstractBlockAction extends ThreadAction {

        protected final TextPane tp;
        protected final Element element;
        protected int startMineOffset;
        protected int endMineOffset;
        protected int startOriginalOffset;
        protected int endOriginalOffset;
        protected int startTheirsOffset;
        protected int endTheirsOffset;

        public AbstractBlockAction(TextPane tp, Element element, String label) {
            super(label);
            this.tp = tp;
            this.element = element;
        }

        public void actionProcess(ActionEvent e) throws Exception {
            StyledDocument doc = (StyledDocument) tp.getDocument();

            startMineOffset = element.getStartOffset();
            Object blockId = tp.getBlockId(element);
            int length = doc.getLength();

            while (startMineOffset > 1 && blockId.equals(tp.getBlockId(startMineOffset - 1))) {
                startMineOffset--;
            }

            endMineOffset = startMineOffset;
            while (endMineOffset < length && tp.getAttributeType(endMineOffset).equals(ATTRIBUTE_WORKING)
                    && blockId.equals(tp.getBlockId(endMineOffset))) {
                endMineOffset++;
            }

            startOriginalOffset = endMineOffset;
            endOriginalOffset = startOriginalOffset;
            while (endOriginalOffset < length && tp.getAttributeType(endOriginalOffset).equals(ATTRIBUTE_ORIGINAL)
                    && blockId.equals(tp.getBlockId(endOriginalOffset))) {
                endOriginalOffset++;
            }

            startTheirsOffset = endOriginalOffset;
            endTheirsOffset = startTheirsOffset;
            while (endTheirsOffset < length && tp.getAttributeType(endTheirsOffset).equals(ATTRIBUTE_THEIRS)
                    && blockId.equals(tp.getBlockId(endTheirsOffset))) {
                endTheirsOffset++;
            }

            doBlockEdit(doc);
        }

        public abstract void doBlockEdit(StyledDocument doc) throws Exception;

    }

    private class UseMineTextBlockAction extends AbstractBlockAction {

        public UseMineTextBlockAction(TextPane tp, Element element) {
            super(tp, element, "Use mine text block");
        }

        public void doBlockEdit(StyledDocument doc) throws Exception {
            doc.setCharacterAttributes(startMineOffset, endMineOffset - startMineOffset, setMixedText, true);
            doc.remove(startTheirsOffset, endTheirsOffset - startTheirsOffset);
            doc.remove(startOriginalOffset, endOriginalOffset - startOriginalOffset);
            nextConflict(tp.getCaretPosition());
        }
    }

    private class UseMineBeforTheirsTextBlockAction extends AbstractBlockAction {

        public UseMineBeforTheirsTextBlockAction(TextPane tp, Element element) {
            super(tp, element, "Use mine text block before theirs");
        }

        public void doBlockEdit(StyledDocument doc) throws Exception {
            doc.setCharacterAttributes(startMineOffset, endMineOffset - startMineOffset, setMixedText, true);
            doc.setCharacterAttributes(startTheirsOffset, endTheirsOffset - startTheirsOffset, setMixedText, true);
            doc.remove(startOriginalOffset, endOriginalOffset - startOriginalOffset);
            nextConflict(tp.getCaretPosition());
        }
    }

    private class UseTheirsTextBlockAction extends AbstractBlockAction {

        public UseTheirsTextBlockAction(TextPane tp, Element element) {
            super(tp, element, "Use theirs text block");
        }

        public void doBlockEdit(StyledDocument doc) throws Exception {
            doc.setCharacterAttributes(startTheirsOffset, endTheirsOffset - startTheirsOffset, setMixedText, true);
            doc.remove(startOriginalOffset, endOriginalOffset - startOriginalOffset);
            doc.remove(startMineOffset, endMineOffset - startMineOffset);
            nextConflict(tp.getCaretPosition());
        }
    }

    private class UseTheirsBeforeMineTextBlockAction extends AbstractBlockAction {

        public UseTheirsBeforeMineTextBlockAction(TextPane tp, Element element) {
            super(tp, element, "Use theirs text block before mine");
        }

        public void doBlockEdit(StyledDocument doc) throws Exception {
            doc.setCharacterAttributes(startTheirsOffset, endTheirsOffset - startTheirsOffset, setMixedText, true);
            doc.insertString(endTheirsOffset, doc.getText(startMineOffset, endMineOffset - startMineOffset),
                    setMixedText);
            doc.remove(startOriginalOffset, endOriginalOffset - startOriginalOffset);
            doc.remove(startMineOffset, endMineOffset - startMineOffset);
            nextConflict(tp.getCaretPosition());
        }

    }

    private class UseNoneTextBlockAction extends AbstractBlockAction {

        public UseNoneTextBlockAction(TextPane tp, Element element) {
            super(tp, element, "Use none text block");
        }

        public void doBlockEdit(StyledDocument doc) throws Exception {
            doc.remove(startTheirsOffset, endTheirsOffset - startTheirsOffset);
            doc.remove(startOriginalOffset, endOriginalOffset - startOriginalOffset);
            doc.remove(startMineOffset, endMineOffset - startMineOffset);
            nextConflict(tp.getCaretPosition());
        }
    }

    private class UseOriginalTextBlockAction extends AbstractBlockAction {

        public UseOriginalTextBlockAction(TextPane tp, Element element) {
            super(tp, element, "Use base text block");
        }

        public void doBlockEdit(StyledDocument doc) throws Exception {
            doc.setCharacterAttributes(startOriginalOffset, endOriginalOffset - startOriginalOffset, setMixedText,
                    true);
            doc.remove(startTheirsOffset, endTheirsOffset - startTheirsOffset);
            doc.remove(startMineOffset, endMineOffset - startMineOffset);
            nextConflict(tp.getCaretPosition());
        }
    }

    private class UseAllTextBlockAction extends AbstractBlockAction {

        public UseAllTextBlockAction(TextPane tp, Element element) {
            super(tp, element, "Use all text block");
        }

        public void doBlockEdit(StyledDocument doc) throws Exception {
            doc.setCharacterAttributes(startMineOffset, endMineOffset - startMineOffset, setMixedText, true);
            doc.setCharacterAttributes(startOriginalOffset, endOriginalOffset - startOriginalOffset, setMixedText,
                    true);
            doc.setCharacterAttributes(startTheirsOffset, endTheirsOffset - startTheirsOffset, setMixedText, true);
            nextConflict(tp.getCaretPosition());
        }
    }

    private class TextPane extends JTextPane {

        public TextPane() {
            ToolTipManager.sharedInstance().registerComponent(this);
            addMouseListener(new MouseAdapter() {

                private void showPopup(MouseEvent e) {
                    JPopupMenu ppVisible = new JPopupMenu();

                    Element element = getElementAt(e.getX(), e.getY());
                    Object type = getAttributeType(element);

                    if (type.equals(ATTRIBUTE_WORKING)) {
                        ppVisible.add(new UseMineTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseMineBeforTheirsTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseNoneTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseAllTextBlockAction(TextPane.this, element));
                    } else if (type.equals(ATTRIBUTE_THEIRS)) {
                        ppVisible.add(new UseTheirsTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseTheirsBeforeMineTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseNoneTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseAllTextBlockAction(TextPane.this, element));
                    } else if (type.equals(ATTRIBUTE_ORIGINAL)) {
                        ppVisible.add(new UseOriginalTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseNoneTextBlockAction(TextPane.this, element));
                        ppVisible.add(new UseAllTextBlockAction(TextPane.this, element));
                    }

                    if (ppVisible.getComponentCount() > 0) {
                        ppVisible.setInvoker(TextPane.this);
                        ppVisible.setLocation(e.getXOnScreen(), e.getYOnScreen());
                        ppVisible.setVisible(true);
                        e.consume();
                    }
                }

                public void mousePressed(MouseEvent e) {
                    if (e.isPopupTrigger()) {
                        showPopup(e);
                    }
                }

                public void mouseReleased(MouseEvent e) {
                    if (e.isPopupTrigger()) {
                        showPopup(e);
                    }
                }

            });
        }

        private Element getElementAt(int x, int y) {
            int charPos = viewToModel(new Point(x, y));
            StyledDocument doc = (StyledDocument) getDocument();
            return doc.getCharacterElement(charPos);
        }

        public Object getAttributeType(Element element) {
            AttributeSet charAttributes = element.getAttributes();
            return charAttributes.getAttribute(ATTRIBUTE_TYPE_KEY);
        }

        public Object getAttributeType(int pos) {
            StyledDocument doc = (StyledDocument) getDocument();
            return getAttributeType(doc.getCharacterElement(pos));
        }

        public Object getBlockId(Element element) {
            AttributeSet charAttributes = element.getAttributes();
            return charAttributes.getAttribute(ATTRIBUTE_BLOCK_ID);
        }

        public Object getBlockId(int pos) {
            StyledDocument doc = (StyledDocument) getDocument();
            return getBlockId(doc.getCharacterElement(pos));
        }

        public String getToolTipText(MouseEvent event) {

            Element charElement = getElementAt(event.getX(), event.getY());
            Object type = getAttributeType(charElement);

            if (ATTRIBUTE_WORKING.equals(type)) {
                return "Working copy";
            } else if (ATTRIBUTE_ORIGINAL.equals(type)) {
                return "Base";
            } else if (ATTRIBUTE_THEIRS.equals(type)) {
                return "Theirs";
            } else if (ATTRIBUTE_MIXED.equals(type)) {
                return "Mixed";
            }
            return null;
        }
    }

}