package com.redhat.ceylon.test.eclipse.plugin.ui; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestMessages.compareValuesDlgActual; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestMessages.compareValuesDlgExpected; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestMessages.compareValuesDlgOk; import static com.redhat.ceylon.test.eclipse.plugin.CeylonTestMessages.compareValuesDlgTitle; 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.layout.GridDataFactory; 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 com.redhat.ceylon.test.eclipse.plugin.CeylonTestPlugin; import com.redhat.ceylon.test.eclipse.plugin.model.TestElement; public class CompareValuesDialog extends TrayDialog { public static final String PREFIX_SUFFIX_PROPERTY = "com.redhat.ceylon.test.eclipse.plugin.testview.compare.CompareResultDialog.prefixSuffix"; // 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; private TextMergeViewer viewer; private String testName; private String actualValue; private String expectedValue; public CompareValuesDialog(Shell parentShell) { super(parentShell); setShellStyle((getShellStyle() & ~SWT.APPLICATION_MODAL) | SWT.TOOL); setBlockOnOpen(false); setHelpAvailable(false); } public void setTestElement(TestElement testElement) { testName = testElement.getQualifiedName(); actualValue = testElement.getActualValue(); expectedValue = testElement.getExpectedValue(); updateView(); } @Override protected boolean isResizable() { return true; } @Override protected IDialogSettings getDialogBoundsSettings() { String name = getClass().getName(); IDialogSettings dialogSettings = CeylonTestPlugin.getDefault().getDialogSettings(); IDialogSettings dialogSettingsSection = dialogSettings.getSection(name); if (dialogSettingsSection == null) { dialogSettingsSection = dialogSettings.addNewSection(name); } return dialogSettingsSection; } @Override protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText(compareValuesDlgTitle); } @Override protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); composite.setLayout(new GridLayout(1, false)); createCompareViewerPane(composite); createPreviewer(); applyDialogFont(parent); return composite; } @Override protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.OK_ID, compareValuesDlgOk, true); } private void createCompareViewerPane(Composite composite) { compareViewerPane = new CompareViewerPane(composite, SWT.BORDER | SWT.FLAT); compareViewerPane.setLayoutData(GridDataFactory.swtDefaults() .align(SWT.FILL, SWT.FILL).grab(true, true) .hint(convertWidthInCharsToPixels(120), convertHeightInCharsToPixels(13)).create()); } private void createPreviewer() { final CompareConfiguration compareConfiguration = new CompareConfiguration(); compareConfiguration.setLeftLabel(compareValuesDlgActual); compareConfiguration.setLeftEditable(false); compareConfiguration.setRightLabel(compareValuesDlgExpected); compareConfiguration.setRightEditable(false); compareConfiguration.setProperty(CompareConfiguration.IGNORE_WHITESPACE, Boolean.FALSE); compareConfiguration.setProperty(PREFIX_SUFFIX_PROPERTY, prefixSuffix); viewer = new CompareValuesMergeViewer(compareViewerPane, SWT.NONE, compareConfiguration); Control control = viewer.getControl(); control.setLayoutData(new GridData(GridData.FILL_BOTH)); control.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { compareConfiguration.dispose(); } }); compareViewerPane.setContent(control); } private void updateView() { updatePrefixSuffix(); if (!viewer.getControl().isDisposed()) { viewer.setInput(new DiffNode(new CompareValueElement(actualValue), new CompareValueElement(expectedValue))); compareViewerPane.setText(testName); } } private void updatePrefixSuffix() { int end = Math.min(actualValue.length(), expectedValue.length()); int i = 0; for (; i < end; i++) if (actualValue.charAt(i) != expectedValue.charAt(i)) break; prefixSuffix[0] = i; int j = actualValue.length() - 1; int k = expectedValue.length() - 1; int l = 0; for (; k >= i && j >= i; k--, j--) { if (actualValue.charAt(j) != expectedValue.charAt(k)) break; l++; } prefixSuffix[1] = l; } public class CompareValueElement implements ITypedElement, IEncodedStreamContentAccessor { private final String content; public CompareValueElement(String content) { this.content = content; } @Override public String getName() { return "<no name>"; } @Override public Image getImage() { return null; } @Override public String getType() { return "txt"; } @Override public String getCharset() throws CoreException { return "UTF-8"; } @Override public InputStream getContents() { try { return new ByteArrayInputStream(content.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { return new ByteArrayInputStream(content.getBytes()); } } } private static class CompareValuesMergeViewer extends TextMergeViewer { public CompareValuesMergeViewer(Composite parent, int style, CompareConfiguration configuration) { super(parent, style, configuration); } @Override protected void configureTextViewer(TextViewer textViewer) { if (textViewer instanceof SourceViewer) { int[] prefixSuffixOffsets = (int[]) getCompareConfiguration().getProperty(CompareValuesDialog.PREFIX_SUFFIX_PROPERTY); ((SourceViewer)textViewer).configure(new CompareValuesViewerConfiguration(prefixSuffixOffsets)); } } } private static class CompareValuesDamagerRepairer implements IPresentationDamager, IPresentationRepairer { private IDocument document; private final int[] prefixSuffixOffsets2; public CompareValuesDamagerRepairer(int[] prefixSuffixOffsets) { this.prefixSuffixOffsets2 = prefixSuffixOffsets; } @Override public void setDocument(IDocument document) { this.document = document; } @Override public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, boolean changed) { return new Region(0, document.getLength()); } @Override public void createPresentation(TextPresentation presentation, ITypedRegion damage) { presentation.setDefaultStyleRange(new StyleRange(0, document.getLength(), null, null)); int prefix = prefixSuffixOffsets2[0]; int suffix = prefixSuffixOffsets2[1]; 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 static class CompareValuesViewerConfiguration extends SourceViewerConfiguration { private final int[] prefixSuffixOffsets; public CompareValuesViewerConfiguration(int[] prefixSuffixOffsets) { this.prefixSuffixOffsets = prefixSuffixOffsets; } @Override public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { PresentationReconciler reconciler = new PresentationReconciler(); CompareValuesDamagerRepairer dr = new CompareValuesDamagerRepairer(prefixSuffixOffsets); reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); return reconciler; } } }