fi.jyu.student.jatahama.onlineinquirytool.client.ClaimAnalysisPanel.java Source code

Java tutorial

Introduction

Here is the source code for fi.jyu.student.jatahama.onlineinquirytool.client.ClaimAnalysisPanel.java

Source

/**
 * Copyright (c) 2014, Jari Hmlinen, Carita Kiili and Julie Coiro
 * All rights reserved.
 * 
 * See LICENSE for full license text.
 * 
 * @author Jari Hmlinen
 */
package fi.jyu.student.jatahama.onlineinquirytool.client;

import java.util.ArrayList;
import java.util.HashMap;

import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;

import fi.jyu.student.jatahama.onlineinquirytool.shared.AnalysisPerspective;
import fi.jyu.student.jatahama.onlineinquirytool.shared.Argument;
import fi.jyu.student.jatahama.onlineinquirytool.shared.BiMap;
import fi.jyu.student.jatahama.onlineinquirytool.shared.ClaimAnalysis;
import fi.jyu.student.jatahama.onlineinquirytool.shared.Pool;

public class ClaimAnalysisPanel extends FlowPanel
        implements FocusHandler, BlurHandler, ArgumentEditorEventHandler, ClickHandler {
    /**
     * Helper class to keep track of ArgumentEditors
     *
     */
    private class ArgumentEditorPool {
        private ArrayList<ArgumentEditor> inUse = new ArrayList<ArgumentEditor>();
        private ArrayList<ArgumentEditor> free = new ArrayList<ArgumentEditor>();
        private ClaimAnalysisPanel owner = null;

        public ArgumentEditorPool(ClaimAnalysisPanel owner, int startSize) {
            this.owner = owner;
            for (int i = 0; i < startSize; i++) {
                free.add(createNewEditor());
            }
        }

        private ArgumentEditor createNewEditor() {
            ArgumentEditor ae = new ArgumentEditor();
            ae.addArgumentEditorEventHandler(owner);
            return ae;
        }

        public synchronized ArgumentEditor reserveEditor() {
            ArgumentEditor e = null;
            if (free.size() == 0) {
                e = createNewEditor();
            } else {
                e = free.remove(0);
            }
            inUse.add(e);
            return e;
        }

        public synchronized void freeEditor(ArgumentEditor e) {
            owner.removeArgumentEditor(e);
            e.setArgument(null);
            inUse.remove(e);
            free.add(e);
        }

        public synchronized void freeAllEditors() {
            for (ArgumentEditor e : inUse) {
                owner.removeArgumentEditor(e);
                free.add(e);
                e.setArgument(null);
            }
            inUse.clear();
        }

        public int getFreeCount() {
            return free.size();
        }

        public int getReservedCount() {
            return inUse.size();
        }

        public int getSize() {
            return getFreeCount() + getReservedCount();
        }
    }

    /**
     * Layout default: how many (counter-)arguments side-by-side?
     */
    public static final int LAYOUT_ARGUMENTS_PER_ROW = 1;

    /**
     * Layout: Add source button min height in px
     */
    public static int LAYOUT_ADD_SOURCE_MIN_HEIGHT = 20;

    /**
     * Layout: Add source button max height in px
     */
    public static int LAYOUT_ADD_SOURCE_MAX_HEIGHT = 96;

    /**
     * Layout default: ArgumentEditor margin left.
     */
    public static final int LAYOUT_ARGUMENT_MARGIN_LEFT = 5;

    /**
     * Layout default: ArgumentEditor margin left.
     */
    public static final int LAYOUT_ARGUMENT_MARGIN_RIGHT = 0;

    /**
     * Layout default: Subcontainer margin left.
     */
    public static final int LAYOUT_SUBCONTAINER_MARGIN_LEFT = 10;

    /**
     * Layout default: Subcontainer margin left.
     */
    public static final int LAYOUT_SUBCONTAINER_MARGIN_RIGHT = 15;

    /**
     * Layout default: Horizontal grouping margin left.
     */
    public static final int LAYOUT_HORIZONTAL_GROUPING_MARGIN_LEFT = 5;

    /**
     * Layout default: Horizontal grouping margin left.
     */
    public static final int LAYOUT_HORIZONTAL_GROUPING_MARGIN_RIGHT = 5;

    /**
     * Layout default: Horizontal grouping margin left.
     */
    public static final int LAYOUT_HORIZONTAL_GROUPING_PADDING_LEFT = 0;

    /**
     * Layout default: Horizontal grouping margin left.
     */
    public static final int LAYOUT_HORIZONTAL_GROUPING_PADDING_RIGHT = 5;

    /**
     * Layout default: Horizontal grouping border.
     */
    public static final int LAYOUT_HORIZONTAL_GROUPING_BORDER = 4;

    /**
     * Layout default: Vertical grouping border.
     */
    public static final int LAYOUT_VERTICAL_GROUPING_BORDER = 1;

    /**
     * Layout default: Vertical grouping padding on xy axis.
     */
    public static final int LAYOUT_VERTICAL_GROUPING_PAD_X = 5;

    /**
     * Layout default: Group wrapper y filler.
     */
    public static final int LAYOUT_GROUP_WRAPPER_FILL_Y = 10;

    /**
     * Layout: How many (counter-)arguments side-by-side?
     */
    private int layoutArgumentsPerRow = LAYOUT_ARGUMENTS_PER_ROW;

    /**
     * ArgumentEditorPool 
     */
    ArgumentEditorPool editorPool = new ArgumentEditorPool(this, 32);

    /**
     * Default claim analysis to be used if no claim has been set.
     */
    private ClaimAnalysis defaultClaim = new ClaimAnalysis();

    /**
     * Claim analysis to be used.
     */
    private ClaimAnalysis claim = null;

    /**
     * Claim text area wrapper.
     */
    private final SimplePanel claimTextWrap = new SimplePanel();

    /**
     * Claim text area.
     */
    private final TextArea claimText = new TextArea();

    /**
     * Concluding statement text area wrapper.
     */
    private final SimplePanel concludingStatementTextWrap = new SimplePanel();

    /**
     * Concluding statement text area.
     */
    private final TextArea concludingStatementText = new TextArea();

    /** 
     * Wrapper for perspective and header groupings
     */
    private final FlowPanel groupWrapper = new FlowPanel();

    /** 
     * Fillers used for spacing at top and bottom of group wrapper.
     */
    private final Label[] groupWrapperFill = new Label[] { new Label(), new Label(), };

    /**
     * Header horizontal grouping.
     */
    private final FlowPanel headerGroup = new FlowPanel();

    /**
     * Vertical groupings. Perspectives + arguments + counter-arguments + summaries = 4 groups total. 
     */
    private SimplePanel[] vGroups = new SimplePanel[] { new SimplePanel(), new SimplePanel(), new SimplePanel(),
            new SimplePanel(), };

    /**
     * Map argument editor to owner perspective.
     */
    private HashMap<ArgumentEditor, AnalysisPerspective> argEditPersMap = new HashMap<ArgumentEditor, AnalysisPerspective>();

    /**
     * Perspective <-> Perspective editor map
     */
    private BiMap<AnalysisPerspective, PerspectiveEditor> persEditMap = new BiMap<AnalysisPerspective, PerspectiveEditor>();

    /**
     * Perspective editor pool
     */
    private Pool<PerspectiveEditor> perspectiveEditorPool = new Pool<PerspectiveEditor>();

    /**
     * Add argument source buttons for hGroups.
     */
    private ArrayList<PushButton> addArgSource = new ArrayList<PushButton>();

    /**
     * Perspective <-> Add argument source button map for arguments.
     */
    private BiMap<AnalysisPerspective, PushButton> persArgButtonMap = new BiMap<AnalysisPerspective, PushButton>();

    /**
     * Perspective <-> Add counter argument source button map for arguments.
     */
    private BiMap<AnalysisPerspective, PushButton> persCounterArgButtonMap = new BiMap<AnalysisPerspective, PushButton>();

    /**
     * Perspective -> subcontainers array map.
     */
    private BiMap<AnalysisPerspective, FlowPanel[]> persSubMap = new BiMap<AnalysisPerspective, FlowPanel[]>();

    /**
     * Perspective summary box pool.
     */
    private Pool<PerspectiveSummaryBox> perspectiveSummaryPool = new Pool<PerspectiveSummaryBox>();

    /**
     * Perspective <-> Perspective summary map.
     */
    private BiMap<AnalysisPerspective, PerspectiveSummaryBox> persSummaryMap = new BiMap<AnalysisPerspective, PerspectiveSummaryBox>();

    /**
     * Add argument source button pool
     */
    private Pool<PushButton> addArgSourcePool = new Pool<PushButton>();

    /**
     * Add perspective button.
     */
    private final PushButton addPerspective = new PushButton("+");

    /**
     * Remove perspective buttons.
     */
    private Pool<Button> removePerspectivePool = new Pool<Button>();

    /**
     * Perspective <-> Remove perspective map.
     */
    private BiMap<AnalysisPerspective, Button> persRemoveMap = new BiMap<AnalysisPerspective, Button>();

    /**
     * Perspective <-> Horizontal groupings map.
     */
    private BiMap<AnalysisPerspective, FlowPanel> hGroups = new BiMap<AnalysisPerspective, FlowPanel>();

    /**
     * Headers
     */
    private Label[] capHeader = new Label[] { new Label(), new Label(), new Label(), new Label(), };

    /**
     * Header wrappers
     */
    private SimplePanel[] capHeaderWrap = new SimplePanel[] { new SimplePanel(), new SimplePanel(),
            new SimplePanel(), new SimplePanel(), };

    /**
     * Popup for remove confirm
     */
    private final PopupPanel confirmPopup = new PopupPanel(false, false);

    /**
     * Remove confirm label
     */
    private final Label confirmHeader = new Label();

    /**
     * Remove confirm label
     */
    private final Label confirmLabel = new Label();

    /**
     * Last confirm popup source
     * TODO: Object is kinda scary here
     */
    private Object confirmSource = null;

    /**
     * Dirty yes button
     */
    private final Button btnConfirmYes = new Button(OnlineInquiryTool.constants.tcBtnYes());

    /**
     * Dirty no button
     */
    private final Button btnConfirmNo = new Button(OnlineInquiryTool.constants.tcBtnNo());

    private HTML _debug_info = null; //new HTML();

    public ClaimAnalysisPanel(OnlineInquiryTool parent) {
        super();

        // Add debug info if it exists
        if (_debug_info != null) {
            add(_debug_info);
            //_debug_info.setVisible(false);
        }

        // Group wrapper that wraps all hozontal and vertival grouping
        groupWrapper.setStyleName("cap-group-wrapper");

        // Init Group wrapper fills (used for spacing at top and bottom)
        for (int i = 0; i < groupWrapperFill.length; i++) {
            groupWrapperFill[i].setStyleName("cap-group-wrapper-fill");
        }

        // Init vertical groupings (perspectives, arguments, counter-arguments)
        for (int i = 0; i < vGroups.length; i++) {
            groupWrapper.add(vGroups[i]);
            vGroups[i].setStyleName("cap-vertical-grouping");
        }

        // Claim and concluding statement texts + header group
        final Label claimHeader = new Label();
        claimHeader.setStyleName("cap-claim-header");
        claimHeader.setText(OnlineInquiryTool.constants.tcLblClaim());
        claimTextWrap.setStyleName("cap-claim-text-wrapper");
        claimText.setStyleName("cap-claim-text");
        claimText.addFocusHandler(this);
        claimText.addBlurHandler(this);
        claimTextWrap.add(claimText);
        final Label summaryHeader = new Label();
        summaryHeader.setStyleName("cap-summary-header");
        summaryHeader.setText(OnlineInquiryTool.constants.tcLblClaimSummary());
        concludingStatementTextWrap.setStyleName("cap-summary-text-wrapper");
        concludingStatementText.setStyleName("cap-summary-text");
        concludingStatementText.addFocusHandler(this);
        concludingStatementText.addBlurHandler(this);
        concludingStatementTextWrap.add(concludingStatementText);

        // Header styling
        headerGroup.setStyleName("cap-horizontal-grouping-header");

        // Add perspective button styling and title
        addPerspective.setStyleName("cap-add-perspective");
        addPerspective.setTitle(OnlineInquiryTool.constants.tcBtnAddPerspective());
        addPerspective.setText(OnlineInquiryTool.constants.tcBtnAddPerspective());
        addPerspective.addClickHandler(this);

        // Initial groupWrapper population and layout
        add(claimHeader);
        add(claimTextWrap);
        groupWrapper.add(groupWrapperFill[0]);
        groupWrapper.add(headerGroup);
        groupWrapper.add(addPerspective);
        groupWrapper.add(groupWrapperFill[1]);
        add(groupWrapper);
        add(summaryHeader);
        add(concludingStatementTextWrap);

        // Headers, add them in same kind of subpanel like stuff in other horizontal groups to get things aligned nicely
        for (int i = 0; i < capHeader.length; i++) {
            capHeaderWrap[i].setStyleName(i > 0 ? "cap-group-subcontainer" : "cap-group-subcontainer-first");
            capHeader[i].setStyleName(i > 0 ? "cap-header-arg" : "cap-header");
            capHeader[i].setText(OnlineInquiryTool.messages.tmLblPerspectiveHeaderN(i));
            capHeaderWrap[i].add(capHeader[i]);
            headerGroup.add(capHeaderWrap[i]);
        }
        // Cleaner div to fix the messing up the layout
        Label cleaner = new Label();
        cleaner.setStyleName("clearer");
        headerGroup.add(cleaner);

        // Confim popup
        confirmPopup.setStylePrimaryName("confirm-popup");
        final VerticalPanel sv = new VerticalPanel();
        sv.setStylePrimaryName("confirm-wrap");
        confirmHeader.setStyleName("confirm-header");
        sv.add(confirmHeader);
        confirmLabel.setStylePrimaryName("confirm-label");
        sv.add(confirmLabel);
        final HorizontalPanel sh = new HorizontalPanel();
        sh.setStylePrimaryName("confirm-button-wrap");
        sh.add(btnConfirmNo);
        sh.add(btnConfirmYes);
        sv.add(sh);
        btnConfirmNo.addClickHandler(this);
        btnConfirmYes.addClickHandler(this);
        confirmPopup.setWidget(sv);
        confirmPopup.setGlassEnabled(true);
        confirmPopup.setGlassStyleName("confirm-glass");

        // Prefill component pools
        prefillPools();

        // Set all to defaults
        setClaim(null);

        // Initial layout update
        updateWidgets();
        updateLayout();
    }

    private void prefillPools() {
        // NOTE: editorPool is filled when contructing
        for (int i = 0; i < 8; i++) {
            PerspectiveEditor pe = new PerspectiveEditor();
            perspectiveEditorPool.add(pe);
        }
        for (int i = 0; i < 8; i++) {
            perspectiveSummaryPool.add(new PerspectiveSummaryBox());
        }
        for (int i = 0; i < 16; i++) {
            PushButton b = new PushButton();
            b.addClickHandler(this);
            addArgSourcePool.add(b);
            b.setText("+");
            b.setStyleName("cap-add-source");
        }
        Button removePerspective = null;
        for (int i = 0; i < 8; i++) {
            removePerspective = new Button();
            removePerspective.setStyleName("close-button-round");
            removePerspective.setTitle(OnlineInquiryTool.constants.tcBtnRemovePerspective());
            removePerspective.addClickHandler(this);
            removePerspectivePool.add(removePerspective);
        }
    }

    /**
     * Updates position and dimensions of all widgets. Does not remove or add ArgumentEditors();
     */
    public void updateLayout() {
        // Calculate some needed values first
        int perspectiveColWidth = 200 + (2 * 2); // cap-header.min-width + 2 * cap-header.border
        int argColWidth = ((ArgumentEditor.DEFAULT_EDITOR_WIDTH + (2 * 2)) * layoutArgumentsPerRow) + // (ArgumentEditor width + borders) *  layoutArgumentsPerRow +
                ((LAYOUT_ARGUMENT_MARGIN_LEFT + LAYOUT_ARGUMENT_MARGIN_RIGHT) * (layoutArgumentsPerRow)); // Argument margins
        int argHeaderWidth = ((ArgumentEditor.DEFAULT_EDITOR_WIDTH + (2 * 2)) * layoutArgumentsPerRow) + // (ArgumentEditor width + borders) *  layoutArgumentsPerRow +
                ((LAYOUT_ARGUMENT_MARGIN_LEFT + LAYOUT_ARGUMENT_MARGIN_RIGHT) * (layoutArgumentsPerRow - 1)) + // Argument margins (only in between them)
                (-4); // Own borders (2 * 2)
        int totalWidth = 2 * perspectiveColWidth + 2 * argColWidth + // Title + summary = 2 * perspectiveColWidth 
                4 * (LAYOUT_SUBCONTAINER_MARGIN_LEFT + LAYOUT_SUBCONTAINER_MARGIN_RIGHT) - 10 + // TODO: Mystery -10. It's 4:08AM :-)
                8; // Horizontal grouping borders affect this
        int ownWidth = totalWidth + LAYOUT_HORIZONTAL_GROUPING_MARGIN_LEFT + LAYOUT_HORIZONTAL_GROUPING_MARGIN_RIGHT
                + LAYOUT_HORIZONTAL_GROUPING_PADDING_LEFT + LAYOUT_HORIZONTAL_GROUPING_PADDING_RIGHT
                + (LAYOUT_HORIZONTAL_GROUPING_BORDER * 2);
        int vgWidth[] = new int[4];
        vgWidth[0] = perspectiveColWidth + 2 * LAYOUT_VERTICAL_GROUPING_PAD_X;
        vgWidth[1] = argColWidth + (2 * LAYOUT_VERTICAL_GROUPING_PAD_X) - 5; // TODO: Mystery -5. It's 3:32AM :-)
        vgWidth[2] = argColWidth + (2 * LAYOUT_VERTICAL_GROUPING_PAD_X) - 5; // TODO: Mystery -5. It's 3:32AM :-)
        vgWidth[3] = perspectiveColWidth + (2 * LAYOUT_VERTICAL_GROUPING_PAD_X);

        // Own size
        //getElement().getStyle().setPropertyPx("minWidth", totalWidth);
        setWidth(ownWidth + "px");

        // Set width of claim text, conc. text and header group
        claimTextWrap.setWidth((totalWidth - 26) + "px"); // -26 from group round edges (horiz + vert) + own border TODO: Check
        concludingStatementTextWrap.setWidth((totalWidth - 26) + "px"); // -26 from group round edges (horiz + vert) + own border TODO: Check
        headerGroup.setWidth(totalWidth + "px");
        //capHeader[0].setWidth(perspectiveColWidth+"px");
        capHeader[1].setWidth(argHeaderWidth + "px");
        capHeader[2].setWidth(argHeaderWidth + "px");
        //capHeader[3].setWidth(perspectiveColWidth+"px"); // Summary same as title (1st column)

        // Perspectives
        for (AnalysisPerspective p : claim.getPerspectives()) {
            FlowPanel hGroup = hGroups.get(p);
            FlowPanel sub[] = persSubMap.get(p);
            for (int j = 0; j < sub.length; j++) {
                if (j == 0 || j == 3) {
                    // Perspective title and summary columns have same width
                    sub[j].setWidth(perspectiveColWidth + "px");
                } else {
                    sub[j].setWidth(argColWidth + "px");
                }
            }
            hGroup.setWidth((totalWidth - 8) + "px"); // Remove own borders

            // Button heights
            PushButton b = persArgButtonMap.get(p);
            int argMod = p.getArgumentList().size() % layoutArgumentsPerRow;
            int argRows = p.getArgumentList().size() / layoutArgumentsPerRow;
            int cargMod = p.getCounterArgumentList().size() % layoutArgumentsPerRow;
            int cargRows = p.getCounterArgumentList().size() / layoutArgumentsPerRow;
            if (argMod != 0) {
                b.setHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT + "px");
                b.getElement().getStyle().setLineHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT, Unit.PX);
            } else {
                // We can still use bigger button if counter-args would have it too and has same number or rows
                // Or if we are on the first row
                // Or if we have less rows than cargs
                if ((cargMod != 0 && (argRows == cargRows)) || argRows == 0 || (argRows < cargRows)) {
                    b.setHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT + "px");
                    b.getElement().getStyle().setLineHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT, Unit.PX);

                } else {
                    b.setHeight(LAYOUT_ADD_SOURCE_MIN_HEIGHT + "px");
                    b.getElement().getStyle().setLineHeight(LAYOUT_ADD_SOURCE_MIN_HEIGHT, Unit.PX);
                }
            }
            b = persCounterArgButtonMap.get(p);
            if (cargMod != 0) {
                b.setHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT + "px");
                b.getElement().getStyle().setLineHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT, Unit.PX);
            } else {
                // We can still use bigger button if arguments would have it too and has same number or rows
                // Or if we are on the first row
                // Or if we have less rows than args
                if ((argMod != 0 && (argRows == cargRows)) || cargRows == 0 || (cargRows < argRows)) {
                    b.setHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT + "px");
                    b.getElement().getStyle().setLineHeight(LAYOUT_ADD_SOURCE_MAX_HEIGHT, Unit.PX);

                } else {
                    b.setHeight(LAYOUT_ADD_SOURCE_MIN_HEIGHT + "px");
                    b.getElement().getStyle().setLineHeight(LAYOUT_ADD_SOURCE_MIN_HEIGHT, Unit.PX);
                }
            }

            // Remove button
            Button removePerspective = persRemoveMap.get(p);
            float hWidth = hGroup.getOffsetWidth();
            removePerspective.getElement().getStyle().setLeft(hWidth - 21, Unit.PX);
        }

        // Add perspective button
        addPerspective.setWidth((totalWidth - 2) + "px"); // Remove own borders

        // Vertical groupings
        int left = 23;
        for (int i = 0; i < vGroups.length; i++) {
            int parentHeight = groupWrapper.getOffsetHeight();
            vGroups[i].setHeight(((parentHeight >= LAYOUT_VERTICAL_GROUPING_BORDER)
                    ? (parentHeight - (LAYOUT_VERTICAL_GROUPING_BORDER * 2))
                    : 0) + "px");
            vGroups[i].setWidth(vgWidth[i] + "px");
            vGroups[i].getElement().getStyle().setLeft(left, Unit.PX);
            left += vgWidth[i] + 15;
        }
    }

    /**
     * Updates widget. Removes ArgumentEditors and adds new ones if needed. 
     */
    private void updateWidgets() {
        // Clean-up first
        editorPool.freeAllEditors();
        argEditPersMap.clear();

        for (PerspectiveEditor pe : persEditMap.reverseKeySet()) {
            perspectiveEditorPool.free(pe);
            pe.removeFromParent();
        }
        for (FlowPanel hGroup : hGroups.reverseKeySet()) {
            hGroup.clear();
            hGroup.removeFromParent();
        }
        for (FlowPanel sub[] : persSubMap.reverseKeySet()) {
            for (int j = 0; j < sub.length; j++) {
                sub[j].clear();
                sub[j].removeFromParent();
            }
        }
        for (PushButton p : addArgSource) {
            addArgSourcePool.free(p);
            p.removeFromParent();
        }
        for (PerspectiveSummaryBox p : persSummaryMap.reverseKeySet()) {
            perspectiveSummaryPool.free(p);
            p.removeFromParent();
        }
        for (Button p : persRemoveMap.reverseKeySet()) {
            removePerspectivePool.free(p);
            p.removeFromParent();
        }
        persEditMap.clear();
        hGroups.clear();
        persSubMap.clear();
        addArgSource.clear();
        persArgButtonMap.clear();
        persCounterArgButtonMap.clear();
        persSummaryMap.clear();
        persRemoveMap.clear();

        // Iterate all perspectives in current claim and perspectives' argument sources
        for (AnalysisPerspective p : claim.getPerspectives()) {
            addPerspective(p);
        }

        _updateDebugInfo();
    }

    private void _updateDebugInfo() {
        // Update debug info if it exists
        if (_debug_info != null) {
            String debug = "DEBUG INFO<br>";
            debug += "<table><tr><td>";
            debug += "editorPool (free/reserved/total): " + editorPool.getFreeCount() + "/"
                    + editorPool.getReservedCount() + "/" + editorPool.getSize() + "<br>";
            debug += "perspectiveEditorPool (free/reserved/total): " + perspectiveEditorPool.getFreeCount() + "/"
                    + perspectiveEditorPool.getReservedCount() + "/" + perspectiveEditorPool.getSize() + "<br>";
            debug += "perspectiveSummaryPool (free/reserved/total): " + perspectiveSummaryPool.getFreeCount() + "/"
                    + perspectiveSummaryPool.getReservedCount() + "/" + perspectiveSummaryPool.getSize() + "<br>";
            debug += "addArgSourcePool (free/reserved/total): " + addArgSourcePool.getFreeCount() + "/"
                    + addArgSourcePool.getReservedCount() + "/" + addArgSourcePool.getSize() + "<br>";
            debug += "removePerspectivePool (free/reserved/total): " + removePerspectivePool.getFreeCount() + "/"
                    + removePerspectivePool.getReservedCount() + "/" + removePerspectivePool.getSize() + "<br>";
            debug += "</td>";
            debug += "<td>";
            debug += "argEditPersMap (size): " + argEditPersMap.size() + "<br>";
            debug += "persEditMap (size): " + persEditMap.size() + "<br>";
            debug += "persArgButtonMap (size): " + persArgButtonMap.size() + "<br>";
            debug += "persCounterArgButtonMap (size): " + persCounterArgButtonMap.size() + "<br>";
            debug += "persSubMap (size): " + persSubMap.size() + "<br>";
            debug += "persSummaryMap (size): " + persSummaryMap.size() + "<br>";
            debug += "persRemoveMap (size): " + persRemoveMap.size() + "<br>";
            debug += "hGroups (size): " + hGroups.size() + "<br>";
            debug += "</td>";
            debug += "</tr></table>";
            _debug_info.setHTML(debug);
        }
    }

    /**
     * Update claim title and conclusion texts
     */
    public void updateClaimTitleAndConclusionWidgets() {
        if (claim.getClaim() != null) {
            claimText.setText(claim.getClaim());
        } else {
            claimText.setText(OnlineInquiryTool.constants.tcPromptWriteClaimHere());
        }
        if (claim.getConclusion() != null) {
            concludingStatementText.setText(claim.getConclusion());
        } else {
            concludingStatementText.setText(OnlineInquiryTool.constants.tcPromptWriteConcludingStatementHere());
        }
    }

    /**
     * Helper method to add perspective.
     */
    private void removePerspective(AnalysisPerspective p) {
        // List all argument editors belonging to this perspective
        ArrayList<ArgumentEditor> removeEditors = new ArrayList<ArgumentEditor>();
        for (ArgumentEditor ae : argEditPersMap.keySet()) {
            if (argEditPersMap.get(ae) == p) {
                removeEditors.add(ae);
            }
        }

        // Remove argument editors
        for (ArgumentEditor ae : removeEditors) {
            ae.removeFromParent();
            argEditPersMap.remove(ae);
            editorPool.freeEditor(ae);
        }

        // Remove perspective editor
        PerspectiveEditor pe = persEditMap.get(p);
        pe.removeFromParent();
        persEditMap.remove(p);
        perspectiveEditorPool.free(pe);

        // Remove add (counter-)arg buttons
        PushButton b = persArgButtonMap.get(p);
        b.removeFromParent();
        addArgSourcePool.free(b);
        addArgSource.remove(b);
        persArgButtonMap.remove(p);
        b = persCounterArgButtonMap.get(p);
        b.removeFromParent();
        addArgSourcePool.free(b);
        addArgSource.remove(b);
        persCounterArgButtonMap.remove(p);

        // Remove perspective summary
        PerspectiveSummaryBox psb = persSummaryMap.get(p);
        psb.removeFromParent();
        persSummaryMap.remove(p);
        perspectiveSummaryPool.free(psb);

        // Remove perspective button
        Button btn = persRemoveMap.get(p);
        btn.removeFromParent();
        persRemoveMap.remove(p);
        removePerspectivePool.free(btn);

        // Clean up hGroup
        FlowPanel hGroup = hGroups.get(p);
        hGroup.clear();
        hGroup.removeFromParent();
        hGroups.remove(p);

        // Clean up subs
        FlowPanel sub[] = persSubMap.get(p);
        for (int j = 0; j < sub.length; j++) {
            sub[j].clear();
            sub[j].removeFromParent();
        }
        persSubMap.remove(p);

        // Remove perspective from claim
        claim.removePerspective(p);

        // Adjust perspective colors to keep even and odd coloring working
        int i = 0;
        for (AnalysisPerspective ap : claim.getPerspectives()) {
            hGroup = hGroups.get(ap);
            if (i % 2 == 0) {
                hGroup.setStyleName("cap-horizontal-grouping-even");
            } else {
                hGroup.setStyleName("cap-horizontal-grouping-odd");
            }
            i++;
        }

        _updateDebugInfo();
    }

    /**
     * Helper method to add perspective.
     */
    private void addPerspective(AnalysisPerspective p) {
        if (perspectiveEditorPool.getFreeCount() == 0) {
            PerspectiveEditor pe = new PerspectiveEditor();
            perspectiveEditorPool.add(pe);
        }
        PerspectiveEditor pe = perspectiveEditorPool.reserve();
        pe.setPxWidth(200 - 2);
        persEditMap.put(p, pe);
        pe.setPerspective(p);

        FlowPanel hGroup = new FlowPanel();
        if (hGroups.size() % 2 == 0) {
            hGroup.setStyleName("cap-horizontal-grouping-even");
        } else {
            hGroup.setStyleName("cap-horizontal-grouping-odd");
        }
        hGroups.put(p, hGroup);
        FlowPanel sub[] = new FlowPanel[] { new FlowPanel(), new FlowPanel(), new FlowPanel(), new FlowPanel(), };
        persSubMap.put(p, sub);
        for (int i = 0; i < sub.length; i++) {
            sub[i].setStyleName(i > 0 ? "cap-group-subcontainer" : "cap-group-subcontainer-first");
            hGroup.add(sub[i]);
        }
        // Cleaner div to fix the messing up the layout
        Label cleaner = new Label();
        cleaner.setStyleName("clearer");
        hGroup.add(cleaner);
        int index = groupWrapper.getWidgetIndex(addPerspective);
        groupWrapper.insert(hGroup, index);

        sub[0].add(pe);

        for (Argument a : p.getArgumentList()) {
            ArgumentEditor ae = editorPool.reserveEditor();
            ae.setArgument(a);
            sub[1].add(ae);
            argEditPersMap.put(ae, p);
        }
        if (addArgSourcePool.getFreeCount() == 0) {
            PushButton b = new PushButton();
            b.addClickHandler(this);
            addArgSourcePool.add(b);
            b.setText("+");
            b.setStyleName("cap-add-source");
        }
        PushButton b = addArgSourcePool.reserve();
        b.setTitle(OnlineInquiryTool.constants.tcBtnAddNewArgument());
        b.setText(OnlineInquiryTool.constants.tcBtnAddNewArgument());
        addArgSource.add(b);
        sub[1].add(b);
        persArgButtonMap.put(p, b);

        for (Argument a : p.getCounterArgumentList()) {
            ArgumentEditor ae = editorPool.reserveEditor();
            ae.setArgument(a);
            sub[2].add(ae);
            argEditPersMap.put(ae, p);
        }
        if (addArgSourcePool.getFreeCount() == 0) {
            b = new PushButton();
            b.addClickHandler(this);
            addArgSourcePool.add(b);
            b.setText("+");
            b.setStyleName("cap-add-source");
        }
        b = addArgSourcePool.reserve();
        b.setTitle(OnlineInquiryTool.constants.tcBtnAddNewCounterArgument());
        b.setText(OnlineInquiryTool.constants.tcBtnAddNewCounterArgument());
        addArgSource.add(b);
        sub[2].add(b);
        persCounterArgButtonMap.put(p, b);

        // Perspective summary
        if (perspectiveSummaryPool.getFreeCount() == 0) {
            perspectiveSummaryPool.add(new PerspectiveSummaryBox());
        }
        PerspectiveSummaryBox psb = perspectiveSummaryPool.reserve();
        psb.setPerspective(p);
        persSummaryMap.put(p, psb);
        sub[3].add(psb);

        // Remove button
        Button removePerspective = null;
        if (removePerspectivePool.getFreeCount() == 0) {
            removePerspective = new Button();
            removePerspective.setStyleName("close-button-round");
            removePerspective.setTitle(OnlineInquiryTool.constants.tcBtnRemovePerspective());
            removePerspective.addClickHandler(this);
            removePerspectivePool.add(removePerspective);
        }
        removePerspective = removePerspectivePool.reserve();
        hGroup.add(removePerspective);
        persRemoveMap.put(p, removePerspective);

        _updateDebugInfo();
    }

    /**
     * Remove ArgumentEditor.
     */
    private void removeArgumentEditor(ArgumentEditor e) {
        e.removeFromParent();
        argEditPersMap.remove(e);
    }

    /**
     * Returns current claim. If no claim has been given returns the default claim.
     * 
     * @return Returns current claim. If no claim has been given returns the default claim.
     */
    public ClaimAnalysis getClaim() {
        return claim;
    }

    /**
     * Sets new claim where changes are to be reflected. If set to null default claim will be reset and taken into use.
     * 
     * @param claim New claim where changes are to be reflected.
     */
    public void setClaim(ClaimAnalysis claim) {
        if (claim != null) {
            if (claim != this.claim) {
                this.claim = claim;
                updateWidgets();
            }
        } else {
            defaultClaim.reset();
            defaultClaim.addPerspective(); // One perspective by default
            this.claim = defaultClaim;
            updateWidgets();

            // If set to empty/default claim we're not dirty
            this.claim.setDirty(false);
        }
    }

    @Override
    public void onArgumentEditorEvent(ArgumentEditorEvent e) {
        switch (e.getEventType()) {
        case ARGUMENTS_CHANGED:
            if (claim != null) {
                claim.setDirty(true);
            }
            break;
        case RELIABILITY_CHANGED:
            if (claim != null) {
                claim.setDirty(true);
            }
            break;
        case RATIONALE_CHANGED:
            if (claim != null) {
                claim.setDirty(true);
            }
            break;
        case REMOVE_CLICKED:
            ArgumentEditor ae = e.getSource();
            confirmSource = ae;
            Argument arg = ae.getArgument();
            if (arg.isEmpty()) {
                handleConfirmSource();
            } else {
                confirmHeader.setText(OnlineInquiryTool.messages.tmLblNotEmpty(arg.counterArgument ? 2 : 1));
                confirmLabel.setText(OnlineInquiryTool.messages.tmLblRemove(arg.counterArgument ? 2 : 1));
                confirmPopup.showRelativeTo(e.getSourceObject());
            }
            break;
        case SOURCE_CHANGED:
            if (claim != null) {
                claim.setDirty(true);
            }
            break;
        default:
            throw new RuntimeException("Unhandled ArgumentEditorEvent type: " + e.getEventType().toString());
        }
    }

    @Override
    public void onClick(ClickEvent event) {
        Object src = event.getSource();
        if (src instanceof PushButton) {
            PushButton b = (PushButton) src;
            if (b == addPerspective) {
                AnalysisPerspective p = claim.addPerspective(); // TODO: user perspective
                addPerspective(p);
                if (claim != null) {
                    claim.setDirty(true);
                }
            } else {
                if (persArgButtonMap.containsReverseKey(b)) {
                    AnalysisPerspective p = persArgButtonMap.reverseGet(b);
                    FlowPanel sub[] = persSubMap.get(p);
                    Argument a = new Argument(false);
                    p.addArgument(a);
                    ArgumentEditor ae = editorPool.reserveEditor();
                    ae.setArgument(a);
                    sub[1].insert(ae, sub[1].getWidgetIndex(b));
                    argEditPersMap.put(ae, p);

                    if (claim != null) {
                        claim.setDirty(true);
                    }
                } else {
                    AnalysisPerspective p = persCounterArgButtonMap.reverseGet(b);
                    FlowPanel sub[] = persSubMap.get(p);
                    Argument a = new Argument(true);
                    p.addCounterArgument(a);
                    ArgumentEditor ae = editorPool.reserveEditor();
                    ae.setArgument(a);
                    sub[2].insert(ae, sub[2].getWidgetIndex(b));
                    argEditPersMap.put(ae, p);

                    if (claim != null) {
                        claim.setDirty(true);
                    }
                }
            }
            updateLayout();
            _updateDebugInfo();
        } else if (src instanceof Button) {
            Button b = (Button) src;
            if (b == btnConfirmNo) {
                confirmPopup.hide();
            } else if (b == btnConfirmYes) {
                confirmPopup.hide();
                handleConfirmSource();
            } else if (persRemoveMap.containsReverseKey(b)) {
                AnalysisPerspective p = persRemoveMap.reverseGet(b);
                confirmSource = p;
                if (p.isEmpty()) {
                    handleConfirmSource();
                } else {
                    confirmHeader.setText(OnlineInquiryTool.messages.tmLblNotEmpty(0));
                    confirmLabel.setText(OnlineInquiryTool.messages.tmLblRemove(0));
                    confirmPopup.showRelativeTo(b);
                }
            }
        }
    }

    private void doArgumentRemove(ArgumentEditor ae) {
        Argument arg = ae.getArgument();
        groupWrapper.remove(ae);
        AnalysisPerspective p = argEditPersMap.get(ae);
        if (arg.counterArgument) {
            p.removeCounterArgument(arg);
        } else {
            p.removeArgument(arg);
        }
        editorPool.freeEditor(ae);
        ae.setArgument(null);
        updateLayout();

        if (claim != null) {
            claim.setDirty(true);
        }

        _updateDebugInfo();
    }

    private void doPerspectiveRemove(AnalysisPerspective p) {
        removePerspective(p);
        // Remove from claim
        claim.removePerspective(p);

        updateLayout();

        if (claim != null) {
            claim.setDirty(true);
        }
    }

    private void handleConfirmSource() {
        if (confirmSource != null) {
            if (confirmSource instanceof ArgumentEditor) {
                doArgumentRemove((ArgumentEditor) confirmSource);
            } else if (confirmSource instanceof AnalysisPerspective) {
                doPerspectiveRemove((AnalysisPerspective) confirmSource);
            }
            confirmSource = null;
        }
    }

    @Override
    public void onFocus(FocusEvent event) {
        if (event.getSource() == claimText) {
            // Clear text if we don't have a claim (default text shown when not focused)
            if (claim.getClaim() == null) {
                claimText.setText("");
            }
        } else if (event.getSource() == concludingStatementText) {
            // Clear text if we don't have question (default text shown when not focused)
            if (claim.getConclusion() == null) {
                concludingStatementText.setText("");
            }
        }
    }

    @Override
    public void onBlur(BlurEvent event) {
        if (event.getSource() == claimText) {
            String text = claimText.getText();
            //         boolean change = false;
            if ("".equals(text)) {
                // If no text set claim to null
                if (claim.getClaim() != null) {
                    //               change = true;
                }
                claim.setClaim(null);
                updateClaimTitleAndConclusionWidgets();
            } else {
                if (claim.getClaim() == null || !text.equals(claim.getClaim())) {
                    claim.setClaim(text);
                    //               change = true;
                }
            }
            //         if(change) {
            //            fireEvent(new PerspectiveEditorEvent(this, PerspectiveEditorEvent.EventType.TITLE_CHANGED));
            //         }
        } else if (event.getSource() == concludingStatementText) {
            String text = concludingStatementText.getText();
            //         boolean change = false;
            if ("".equals(text)) {
                // If no text set question to null
                if (claim.getConclusion() != null) {
                    //               change = true;
                }
                claim.setConclusion(null);
                updateClaimTitleAndConclusionWidgets();
            } else {
                if (claim.getConclusion() == null || !text.equals(claim.getConclusion())) {
                    claim.setConclusion(text);
                    //               change = true;
                }
            }
            //         if(change) {
            //            fireEvent(new PerspectiveEditorEvent(this, PerspectiveEditorEvent.EventType.QUESTION_CHANGED));
            //         }
        }
    }
}