001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.dialogs.history.diff;
029
030import com.alkacon.diff.Diff;
031
032import org.opencms.main.CmsLog;
033import org.opencms.ui.A_CmsUI;
034import org.opencms.ui.CmsVaadinUtils;
035import org.opencms.util.CmsHtml2TextConverter;
036import org.opencms.util.CmsStringUtil;
037import org.opencms.workplace.comparison.CmsDiffViewMode;
038import org.opencms.workplace.comparison.CmsHtmlDifferenceConfiguration;
039
040import java.io.BufferedReader;
041import java.io.IOException;
042import java.io.StringReader;
043
044import org.apache.commons.logging.Log;
045
046import com.vaadin.v7.data.Property.ValueChangeEvent;
047import com.vaadin.v7.data.Property.ValueChangeListener;
048import com.vaadin.v7.shared.ui.label.ContentMode;
049import com.vaadin.v7.ui.Label;
050import com.vaadin.v7.ui.OptionGroup;
051import com.vaadin.v7.ui.VerticalLayout;
052import com.vaadin.ui.themes.ValoTheme;
053
054/**
055 * Widget used to display a colorized diff view for two texts.<p>
056 */
057public class CmsTextDiffPanel extends VerticalLayout {
058
059    /** Logger instance for this class. */
060    private static final Log LOG = CmsLog.getLog(CmsTextDiffPanel.class);
061
062    /** Serial version  id. */
063    private static final long serialVersionUID = 1L;
064
065    /** Label containing the actual diff. */
066    private Label m_diffHtml;
067
068    /** The current diff mode. */
069    private String m_diffMode = "diff";
070
071    /** Selects between different diff modes. */
072    private OptionGroup m_diffModeSelect;
073
074    /** First text used for comparison. */
075    private String m_text1;
076
077    /** Second text used for comparison. */
078    private String m_text2;
079
080    /** The current text mode. */
081    private String m_textMode = "html";
082
083    /** Selects between different text modes. */
084    private OptionGroup m_textOrHtmlSelect;
085
086    /**
087     * Creates a new instance.<p>
088     *
089     * @param text1 the first text
090     * @param text2 the second text
091     *
092     * @param selectTextOrHtml true if the option to select between comparison as text and comparison as HTML should be shown
093     * @param selectDiffMode true if the option to select between showing only the changed lines or all the lines should be displayed
094     */
095    public CmsTextDiffPanel(String text1, String text2, boolean selectTextOrHtml, boolean selectDiffMode) {
096        CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null);
097        m_text1 = text1;
098        m_text2 = text2;
099        m_textOrHtmlSelect.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL);
100        m_textOrHtmlSelect.setValue("html");
101        m_textOrHtmlSelect.setVisible(selectTextOrHtml);
102        m_diffModeSelect.setValue("diff");
103        m_diffModeSelect.addStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL);
104        m_diffModeSelect.setVisible(selectDiffMode);
105
106        m_textOrHtmlSelect.addValueChangeListener(new ValueChangeListener() {
107
108            private static final long serialVersionUID = 1L;
109
110            @SuppressWarnings("synthetic-access")
111            public void valueChange(ValueChangeEvent event) {
112
113                m_textMode = event.getProperty().getValue().toString();
114                update();
115            }
116        });
117
118        m_diffModeSelect.addValueChangeListener(new ValueChangeListener() {
119
120            private static final long serialVersionUID = 1L;
121
122            @SuppressWarnings("synthetic-access")
123            public void valueChange(ValueChangeEvent event) {
124
125                m_diffMode = event.getProperty().getValue().toString();
126                update();
127            }
128        });
129
130        update();
131    }
132
133    /**
134     * Gets the diff HTML based on the selected display options.<p>
135     *
136     * @return the diff HTML to display
137     *
138     * @throws Exception if something goes wrong
139     */
140    public String getDiffHtml() throws Exception {
141
142        CmsDiffViewMode mode = "all".equals(m_diffMode) ? CmsDiffViewMode.ALL : CmsDiffViewMode.DIFF_ONLY;
143        String text1 = m_text1;
144        String text2 = m_text2;
145        if ("text".equals(m_textMode)) {
146            text1 = CmsHtml2TextConverter.html2text(text1, "UTF-8");
147            text2 = CmsHtml2TextConverter.html2text(text2, "UTF-8");
148        }
149        CmsHtmlDifferenceConfiguration conf = new CmsHtmlDifferenceConfiguration(
150            mode == CmsDiffViewMode.ALL ? -1 : 2,
151            A_CmsUI.get().getLocale());
152        String diff = Diff.diffAsHtml(text1, text2, conf);
153        String html = null;
154        if (CmsStringUtil.isNotEmpty(diff)) {
155            html = diff;
156        } else {
157            html = wrapLinesWithUnchangedStyle(
158                CmsStringUtil.substitute(CmsStringUtil.escapeHtml(m_text1), "<br/>", ""));
159        }
160        return html;
161
162    }
163
164    /**
165     * Updates the displayed diff based on the selected display options.<p>
166     */
167    public void update() {
168
169        try {
170            String diffHtml = "<pre>" + getDiffHtml() + "</pre>";
171            m_diffHtml.setContentMode(ContentMode.HTML);
172            m_diffHtml.setValue(diffHtml);
173        } catch (Exception e) {
174            LOG.error(e.getLocalizedMessage(), e);
175        }
176
177    }
178
179    /**
180    *
181    * Returns a diff text wrapped with formatting style.<p>
182    *
183    * @param diff the text to wrap with CSS formatting
184    * @return the text with formatting styles wrapped
185    * @throws IOException if something goes wrong
186    */
187    protected String wrapLinesWithUnchangedStyle(String diff) throws IOException {
188
189        String line;
190        StringBuffer result = new StringBuffer();
191        BufferedReader br = new BufferedReader(new StringReader(diff));
192        while ((line = br.readLine()) != null) {
193            if ("".equals(line.trim())) {
194                line = "&nbsp;";
195            }
196            result.append("<div class=\"df-unc\"><span class=\"df-unc\">").append(line).append("</span></div>\n");
197        }
198        return result.toString();
199    }
200
201}