/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.testng.eclipse.ui;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.Dialog;
import org.eclipse.jface.dialogs.DialogSettings;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
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.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.testng.eclipse.TestNGPlugin;
import org.testng.eclipse.util.ResourceUtil;
/**
* Small modifications to fit TestNG.
*
* @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
*/
public class CompareResultDialog extends Dialog {
/* workaround - to make prefix and suffix accessible to the CompareResultViewerConfiguration */
private static CompareResultDialog fgThis;
// dialog store id constants
private final static String DIALOG_BOUNDS_KEY = "CompareResultDialogBounds"; //$NON-NLS-1$
private static final String X = "x"; //$NON-NLS-1$
private static final String Y = "y"; //$NON-NLS-1$
private static final String WIDTH = "width"; //$NON-NLS-1$
private static final String HEIGHT = "height"; //$NON-NLS-1$
private TextMergeViewer fViewer;
private String fExpected;
private String fActual;
private String fTestName;
private int fPrefix;
private int fSuffix;
private IDialogSettings fSettings;
protected Rectangle fNewBounds;
static List<Pattern> patternList;
static String[] regexes = {
".*expected:<(.*)> but was:<(.*)>.*",
".*expected not same with:<(.*)> but was same:<(.*)>.*",
".*expected same with:<(.*)> but was:<(.*)>.*",
".*expected \\[(.*)\\] but found \\[(.*)\\].*"
};
public CompareResultDialog(Shell parentShell, RunInfo failure) {
super(parentShell);
fgThis = this;
setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
fTestName = failure.getMethodName();
initializeActualExpected(failure.getStackTrace());
computePrefixSuffix();
fSettings = TestNGPlugin.getDefault().getDialogSettings();
}
// TESTNG-21
private void initializeActualExpected(String trace) {
if (trace.indexOf("hamcrest") > 0) parseHamCrestTrace(trace);
else parseTestNGTrace(trace);
}
private void parseHamCrestTrace(String trace) {
String IS = "is ";
String WAS = "was ";
int ind1 = trace.indexOf(IS);
int ind2 = trace.indexOf("\n", ind1);
int ind3 = trace.indexOf(WAS, ind2);
int ind4 = trace.indexOf("\n", ind3);
fExpected = trace.substring(ind1 + IS.length(), ind2);
fActual = trace.substring(ind3 + WAS.length(), ind4);
}
static List<Pattern> getPatterns() {
if (patternList == null) {
patternList = new ArrayList<Pattern>();
for (String rgx : regexes) {
patternList.add(Pattern.compile(rgx, Pattern.DOTALL));
}
}
return patternList;
}
private void parseTestNGTrace(String trace) {
Matcher m;
for (Pattern p : getPatterns()) {
m = p.matcher(trace);
if (m.find()) {
fExpected = m.group(1);
fActual = m.group(2);
return;
}
}
fActual= "N/A";
fExpected= "N/A";
return;
}
protected Point getInitialSize() {
int width = 0;
int height = 0;
final Shell s = getShell();
if(s != null) {
s.addControlListener(new ControlListener() {
public void controlMoved(ControlEvent arg) {
fNewBounds = s.getBounds();
}
public void controlResized(ControlEvent arg) {
fNewBounds = s.getBounds();
}
});
}
IDialogSettings bounds = fSettings.getSection(DIALOG_BOUNDS_KEY);
if(bounds == null) {
return super.getInitialSize();
}
else {
try {
width = bounds.getInt(WIDTH);
}
catch(NumberFormatException e) {
width = 400;
}
try {
height = bounds.getInt(HEIGHT);
}
catch(NumberFormatException e) {
height = 300;
}
}
return new Point(width, height);
}
protected Point getInitialLocation(Point initialSize) {
Point loc = super.getInitialLocation(initialSize);
IDialogSettings bounds = fSettings.getSection(DIALOG_BOUNDS_KEY);
if(bounds != null) {
try {
loc.x = bounds.getInt(X);
}
catch(NumberFormatException e) {
}
try {
loc.y = bounds.getInt(Y);
}
catch(NumberFormatException e) {
}
}
return loc;
}
public boolean close() {
boolean closed = super.close();
if(closed && (fNewBounds != null)) {
saveBounds(fNewBounds);
}
return closed;
}
private void saveBounds(Rectangle bounds) {
IDialogSettings dialogBounds = fSettings.getSection(DIALOG_BOUNDS_KEY);
if(dialogBounds == null) {
dialogBounds = new DialogSettings(DIALOG_BOUNDS_KEY);
fSettings.addSection(dialogBounds);
}
dialogBounds.put(X, bounds.x);
dialogBounds.put(Y, bounds.y);
dialogBounds.put(WIDTH, bounds.width);
dialogBounds.put(HEIGHT, bounds.height);
}
private void computePrefixSuffix() {
int end = Math.min(fExpected.length(), fActual.length());
int i = 0;
for(; i < end; i++) {
if(fExpected.charAt(i) != fActual.charAt(i)) {
break;
}
}
fPrefix = i;
int j = fExpected.length() - 1;
int k = fActual.length() - 1;
int l = 0;
for(; (k >= fPrefix) && (j >= fPrefix); k--, j--) {
if(fExpected.charAt(j) != fActual.charAt(k)) {
break;
}
l++;
}
fSuffix = l;
}
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(ResourceUtil.getString("CompareResultDialog.title")); //$NON-NLS-1$
}
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent,
IDialogConstants.OK_ID,
ResourceUtil.getString("CompareResultDialog.labelOK"),
true); //$NON-NLS-1$
}
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
GridLayout layout = new GridLayout();
layout.numColumns = 1;
composite.setLayout(layout);
CompareViewerPane pane = new CompareViewerPane(composite, SWT.BORDER | SWT.FLAT);
pane.setText(fTestName);
GridData data = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
data.widthHint = convertWidthInCharsToPixels(120);
data.heightHint = convertHeightInCharsToPixels(13);
pane.setLayoutData(data);
Control previewer = createPreviewer(pane);
pane.setContent(previewer);
GridData gd = new GridData(GridData.FILL_BOTH);
previewer.setLayoutData(gd);
applyDialogFont(parent);
return composite;
}
private Control createPreviewer(Composite parent) {
final CompareConfiguration compareConfiguration = new CompareConfiguration();
compareConfiguration.setLeftLabel(ResourceUtil.getString("CompareResultDialog.expectedLabel")); //$NON-NLS-1$
compareConfiguration.setLeftEditable(false);
compareConfiguration.setRightLabel(ResourceUtil.getString("CompareResultDialog.actualLabel")); //$NON-NLS-1$
compareConfiguration.setRightEditable(false);
compareConfiguration.setProperty(CompareConfiguration.IGNORE_WHITESPACE, Boolean.FALSE);
fViewer = new CompareResultMergeViewer(parent, SWT.NONE, compareConfiguration);
fViewer.setInput(new DiffNode(new CompareElement(fExpected), new CompareElement(fActual)));
Control control = fViewer.getControl();
control.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if(compareConfiguration != null) {
compareConfiguration.dispose();
}
}
});
return control;
}
private static class CompareResultMergeViewer extends TextMergeViewer {
private CompareResultMergeViewer(Composite parent,
int style,
CompareConfiguration configuration) {
super(parent, style, configuration);
}
protected void configureTextViewer(TextViewer textViewer) {
if(textViewer instanceof SourceViewer) {
((SourceViewer) textViewer).configure(new CompareResultViewerConfiguration());
}
}
}
public static class CompareResultViewerConfiguration extends SourceViewerConfiguration {
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
PresentationReconciler reconciler = new PresentationReconciler();
SimpleDamagerRepairer dr = new SimpleDamagerRepairer();
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
return reconciler;
}
public static class SimpleDamagerRepairer implements IPresentationDamager,
IPresentationRepairer {
private IDocument fDocument;
public void setDocument(IDocument document) {
fDocument = document;
}
public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, boolean changed) {
return new Region(0, fDocument.getLength());
}
public void createPresentation(TextPresentation presentation, ITypedRegion damage) {
int suffix = CompareResultDialog.fgThis.fSuffix;
int prefix = CompareResultDialog.fgThis.fPrefix;
TextAttribute attr = new TextAttribute(Display.getDefault().getSystemColor(SWT.COLOR_RED),
null,
SWT.BOLD);
presentation.addStyleRange(new StyleRange(prefix,
fDocument.getLength() - suffix - prefix,
attr.getForeground(),
attr.getBackground(),
attr.getStyle()));
}
}
}
private static class CompareElement implements ITypedElement, IEncodedStreamContentAccessor {
private String fContent;
public CompareElement(String content) {
fContent = content;
}
public String getName() {
return "<no name>"; //$NON-NLS-1$
}
public Image getImage() {
return null;
}
public String getType() {
return "txt"; //$NON-NLS-1$
}
public InputStream getContents() {
try {
return new ByteArrayInputStream(fContent.getBytes("UTF-8")); //$NON-NLS-1$
}
catch(UnsupportedEncodingException e) {
return new ByteArrayInputStream(fContent.getBytes());
}
}
public String getCharset() throws CoreException {
return "UTF-8"; //$NON-NLS-1$
}
}
}