/******************************************************************************* * Copyright Technophobia Ltd 2012 * * This file is part of the Substeps Eclipse Plugin. * * The Substeps Eclipse Plugin is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License v1.0. * * The Substeps Eclipse Plugin 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 * Eclipse Public License for more details. * * You should have received a copy of the Eclipse Public License * along with the Substeps Eclipse Plugin. If not, see <http://www.eclipse.org/legal/epl-v10.html>. ******************************************************************************/ package com.technophobia.substeps.junit.ui.dialog; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareViewerPane; import org.eclipse.compare.IEncodedStreamContentAccessor; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.contentmergeviewer.TextMergeViewer; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.TrayDialog; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextAttribute; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.text.presentation.IPresentationDamager; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.presentation.IPresentationRepairer; import org.eclipse.jface.text.presentation.PresentationReconciler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import com.technophobia.substeps.FeatureRunnerPlugin; import com.technophobia.substeps.junit.ui.SubstepsFeatureMessages; import com.technophobia.substeps.junit.ui.help.SubstepsHelpContextIds; import com.technophobia.substeps.model.structure.FailureTrace; import com.technophobia.substeps.model.structure.SubstepsTestElement; public class CompareResultDialog extends TrayDialog { private static final String PREFIX_SUFFIX_PROPERTY = "org.eclipse.jdt.internal.junit.ui.CompareResultDialog.prefixSuffix"; //$NON-NLS-1$ private static class CompareResultMergeViewer extends TextMergeViewer { private CompareResultMergeViewer(final Composite parent, final int style, final CompareConfiguration configuration) { super(parent, style, configuration); } @Override protected void createControls(final Composite composite) { super.createControls(composite); PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, SubstepsHelpContextIds.RESULT_COMPARE_DIALOG); } @Override protected void configureTextViewer(final TextViewer textViewer) { if (textViewer instanceof SourceViewer) { final int[] prefixSuffixOffsets = (int[]) getCompareConfiguration().getProperty(PREFIX_SUFFIX_PROPERTY); ((SourceViewer) textViewer).configure(new CompareResultViewerConfiguration(prefixSuffixOffsets)); } } } private static class CompareResultViewerConfiguration extends SourceViewerConfiguration { private static class SimpleDamagerRepairer implements IPresentationDamager, IPresentationRepairer { private IDocument document; private final int[] prefixSuffixOffsets2; public SimpleDamagerRepairer(final int[] prefixSuffixOffsets) { this.prefixSuffixOffsets2 = prefixSuffixOffsets; } @Override public void setDocument(final IDocument document) { this.document = document; } @Override public IRegion getDamageRegion(final ITypedRegion partition, final DocumentEvent event, final boolean changed) { return new Region(0, document.getLength()); } @Override public void createPresentation(final TextPresentation presentation, final ITypedRegion damage) { presentation.setDefaultStyleRange(new StyleRange(0, document.getLength(), null, null)); final int prefix = prefixSuffixOffsets2[0]; final int suffix = prefixSuffixOffsets2[1]; final TextAttribute attr = new TextAttribute(Display.getDefault().getSystemColor(SWT.COLOR_RED), null, SWT.BOLD); presentation.addStyleRange(new StyleRange(prefix, document.getLength() - suffix - prefix, attr .getForeground(), attr.getBackground(), attr.getStyle())); } } private final int[] prefixSuffixOffsets; public CompareResultViewerConfiguration(final int[] prefixSuffixOffsets) { this.prefixSuffixOffsets = prefixSuffixOffsets; } @Override public IPresentationReconciler getPresentationReconciler(final ISourceViewer sourceViewer) { final PresentationReconciler reconciler = new PresentationReconciler(); final SimpleDamagerRepairer dr = new SimpleDamagerRepairer(prefixSuffixOffsets); reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); return reconciler; } } private static class CompareElement implements ITypedElement, IEncodedStreamContentAccessor { private final String content; public CompareElement(final String content) { this.content = content; } @Override public String getName() { return "<no name>"; //$NON-NLS-1$ } @Override public Image getImage() { return null; } @Override public String getType() { return "txt"; //$NON-NLS-1$ } @Override public InputStream getContents() { try { return new ByteArrayInputStream(content.getBytes("UTF-8")); //$NON-NLS-1$ } catch (final UnsupportedEncodingException e) { return new ByteArrayInputStream(content.getBytes()); } } @Override public String getCharset() throws CoreException { return "UTF-8"; //$NON-NLS-1$ } } private TextMergeViewer viewer; private String expected; private String actual; private String testName; /** * Lengths of common prefix and suffix. Note: this array is passed to the * DamagerRepairer and the lengths are updated on content change. */ private final int[] prefixSuffix = new int[2]; private CompareViewerPane compareViewerPane; public CompareResultDialog(final Shell parentShell, final SubstepsTestElement element) { super(parentShell); setShellStyle((getShellStyle() & ~SWT.APPLICATION_MODAL) | SWT.TOOL); setFailedTest(element); } @Override protected boolean isResizable() { return true; } private void setFailedTest(final SubstepsTestElement failedTest) { this.testName = failedTest.getTestName(); final FailureTrace failureTrace = failedTest.getFailureTrace(); this.expected = failureTrace.getExpected(); this.actual = failureTrace.getActual(); computePrefixSuffix(); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.Dialog#getDialogBoundsSettings() */ @Override protected IDialogSettings getDialogBoundsSettings() { final IDialogSettings dialogSettings = FeatureRunnerPlugin.instance().getDialogSettings(); IDialogSettings section = dialogSettings.getSection(getClass().getName()); if (section == null) { section = dialogSettings.addNewSection(getClass().getName()); } return section; } private void computePrefixSuffix() { final int end = Math.min(expected.length(), actual.length()); int i = 0; for (; i < end; i++) if (expected.charAt(i) != actual.charAt(i)) break; prefixSuffix[0] = i; int j = expected.length() - 1; int k = actual.length() - 1; int l = 0; for (; k >= i && j >= i; k--, j--) { if (expected.charAt(j) != actual.charAt(k)) break; l++; } prefixSuffix[1] = l; } @Override protected void configureShell(final Shell newShell) { super.configureShell(newShell); newShell.setText(SubstepsFeatureMessages.CompareResultDialog_title); PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell, SubstepsHelpContextIds.RESULT_COMPARE_DIALOG); } @Override protected void createButtonsForButtonBar(final Composite parent) { createButton(parent, IDialogConstants.OK_ID, SubstepsFeatureMessages.CompareResultDialog_labelOK, true); } @Override protected Control createDialogArea(final Composite parent) { final Composite composite = (Composite) super.createDialogArea(parent); final GridLayout layout = new GridLayout(); layout.numColumns = 1; composite.setLayout(layout); compareViewerPane = new CompareViewerPane(composite, SWT.BORDER | SWT.FLAT); final GridData data = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL); data.widthHint = convertWidthInCharsToPixels(120); data.heightHint = convertHeightInCharsToPixels(13); compareViewerPane.setLayoutData(data); final Control previewer = createPreviewer(compareViewerPane); compareViewerPane.setContent(previewer); final GridData gd = new GridData(GridData.FILL_BOTH); previewer.setLayoutData(gd); applyDialogFont(parent); return composite; } private Control createPreviewer(final Composite parent) { final CompareConfiguration compareConfiguration = new CompareConfiguration(); compareConfiguration.setLeftLabel(SubstepsFeatureMessages.CompareResultDialog_expectedLabel); compareConfiguration.setLeftEditable(false); compareConfiguration.setRightLabel(SubstepsFeatureMessages.CompareResultDialog_actualLabel); compareConfiguration.setRightEditable(false); compareConfiguration.setProperty(CompareConfiguration.IGNORE_WHITESPACE, Boolean.FALSE); compareConfiguration.setProperty(PREFIX_SUFFIX_PROPERTY, prefixSuffix); viewer = new CompareResultMergeViewer(parent, SWT.NONE, compareConfiguration); setCompareViewerInput(); final Control control = viewer.getControl(); control.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(final DisposeEvent e) { compareConfiguration.dispose(); } }); return control; } private void setCompareViewerInput() { if (!viewer.getControl().isDisposed()) { viewer.setInput(new DiffNode(new CompareElement(expected), new CompareElement(actual))); compareViewerPane.setText(testName); } } public void setInput(final SubstepsTestElement failedTest) { setFailedTest(failedTest); setCompareViewerInput(); } }