/**
*
*/
package org.owasp.webscarab.util.swing;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Event;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Iterator;
import java.util.List;
import java.util.prefs.Preferences;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import org.owasp.webscarab.util.Diff;
import org.owasp.webscarab.util.Diff.Edit;
/**
* @author rdawes
*
*/
public class DiffPanel extends JPanel {
private static final long serialVersionUID = 1604132435765855634L;
public static final String SIDE_BY_SIDE = "SIDEBYSIDE";
public static final String COMBINED = "COMBINED";
private static final NoWrapEditorKit noWrapEditorKit = new NoWrapEditorKit();
private String displayLayout = "";
private JTextPane srcTextPane = null;
private Document srcDoc = null;
private JTextPane dstTextPane = null;
private Document dstDoc = null;
private JTextPane combinedTextPane = null;
private Document combinedDoc = null;
private CharSequence src = null, dst = null;
private List<Edit> edits = null;
private Color changedColor, addedColor, deletedColor;
private SimpleAttributeSet unchanged, changed, added, deleted;
private CardLayout layout;
private JPanel combinedPanel;
private JPanel bothPanel;
public DiffPanel() {
this(SIDE_BY_SIDE);
}
public DiffPanel(String displayLayout) {
super();
getPreferences();
createAttributes();
createComponents();
addKeyMappings();
setDisplayLayout(displayLayout);
}
private void getPreferences() {
Preferences prefs = Preferences.userNodeForPackage(getClass());
if (changedColor == null) {
int colorSpec = prefs.getInt("changed", Color.YELLOW.getRGB());
changedColor = new Color(colorSpec);
}
if (addedColor == null) {
int colorSpec = prefs.getInt("added", Color.GREEN.getRGB());
addedColor = new Color(colorSpec);
}
if (deletedColor == null) {
int colorSpec = prefs.getInt("deleted", Color.PINK.getRGB());
deletedColor = new Color(colorSpec);
}
}
private void addKeyMappings() {
getActionMap().put("TOGGLELAYOUT", new AbstractAction() {
private static final long serialVersionUID = 1558804946998494321L;
public void actionPerformed(ActionEvent event) {
layout.next(DiffPanel.this);
DiffPanel.this.requestFocusInWindow();
}
});
InputMap inputMap = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.CTRL_MASK),
"TOGGLELAYOUT");
}
private void createAttributes() {
unchanged = new SimpleAttributeSet();
changed = new SimpleAttributeSet();
changed.addAttribute(StyleConstants.Background, changedColor);
added = new SimpleAttributeSet();
added.addAttribute(StyleConstants.Background, addedColor);
deleted = new SimpleAttributeSet();
deleted.addAttribute(StyleConstants.Background, deletedColor);
}
private void createComponents() {
layout = new CardLayout();
setLayout(layout);
combinedPanel = new JPanel(new BorderLayout());
combinedTextPane = new JTextPane();
combinedTextPane.setEditorKit(noWrapEditorKit);
combinedTextPane.setFont(new java.awt.Font("Monospaced", 0, 12));
combinedTextPane.setEditable(false);
combinedPanel.add(new JScrollPane(combinedTextPane));
srcTextPane = new JTextPane();
srcTextPane.setEditorKit(noWrapEditorKit);
srcTextPane.setFont(new java.awt.Font("Monospaced", 0, 12));
srcTextPane.setEditable(false);
dstTextPane = new JTextPane();
dstTextPane.setEditorKit(noWrapEditorKit);
dstTextPane.setFont(new java.awt.Font("Monospaced", 0, 12));
dstTextPane.setEditable(false);
JScrollPane srcScrollPane = new JScrollPane(srcTextPane);
srcScrollPane
.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
srcScrollPane
.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
JScrollPane dstScrollPane = new JScrollPane(dstTextPane);
dstScrollPane
.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
dstScrollPane
.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
JScrollBar horizontalScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
JScrollBar verticalScrollBar = new JScrollBar(JScrollBar.VERTICAL);
srcScrollPane.getHorizontalScrollBar().setModel(
horizontalScrollBar.getModel());
srcScrollPane.getVerticalScrollBar().setModel(
verticalScrollBar.getModel());
dstScrollPane.getHorizontalScrollBar().setModel(
horizontalScrollBar.getModel());
dstScrollPane.getVerticalScrollBar().setModel(
verticalScrollBar.getModel());
JPanel panel = new JPanel(new GridLayout(1, 2));
panel.add(srcScrollPane);
panel.add(dstScrollPane);
bothPanel = new JPanel(new BorderLayout());
bothPanel.add(panel, BorderLayout.CENTER);
bothPanel.add(horizontalScrollBar, BorderLayout.SOUTH);
bothPanel.add(verticalScrollBar, BorderLayout.EAST);
this.add(COMBINED, combinedPanel);
this.add(SIDE_BY_SIDE, bothPanel);
}
public void setDisplayLayout(String displayLayout) {
if (!displayLayout.equals(this.displayLayout)) {
this.displayLayout = displayLayout;
layout.show(this, displayLayout);
}
}
public String getDisplayLayout() {
return displayLayout;
}
public void clear() {
combinedTextPane.setText("");
srcTextPane.setText("");
dstTextPane.setText("");
}
public void showDifferences(CharSequence src, CharSequence dst,
List<Edit> edits) {
this.src = src;
this.dst = dst;
this.edits = edits;
deleteDocuments();
createDocuments();
}
private void deleteDocuments() {
combinedDoc = srcDoc = dstDoc = null;
if (combinedTextPane != null)
combinedTextPane.setText("");
if (srcTextPane != null)
srcTextPane.setText("");
if (dstTextPane != null)
dstTextPane.setText("");
}
private void createDocuments() {
combinedDoc = new DefaultStyledDocument();
srcDoc = new DefaultStyledDocument();
dstDoc = new DefaultStyledDocument();
Iterator<Edit> it = edits.iterator();
int srcLast = 0;
int dstLast = 0;
try {
while (it.hasNext()) {
Edit edit = it.next();
if (edit.getSrcLocation() > srcLast) {
// catch up common items in between edits
String s = src.subSequence(srcLast, edit.getSrcLocation())
.toString();
combinedDoc.insertString(combinedDoc.getLength(), s,
unchanged);
srcDoc.insertString(srcDoc.getLength(), s, unchanged);
}
if (edit.getDstLocation() > dstLast) {
String d = dst.subSequence(dstLast, edit.getDstLocation())
.toString();
// catch up common items in between edits
dstDoc.insertString(dstDoc.getLength(), d, unchanged);
}
String s = edit.getSrc().toString();
String d = edit.getDst().toString();
if (edit.getSrc().length() > 0 && edit.getDst().length() > 0) {
combinedDoc.insertString(combinedDoc.getLength(), s,
deleted);
combinedDoc.insertString(combinedDoc.getLength(), d, added);
srcDoc.insertString(srcDoc.getLength(), s, changed);
dstDoc.insertString(dstDoc.getLength(), d, changed);
int v = countLines(s) - countLines(d);
if (v > 0) {
String cr = "";
for (int i = 0; i < v; i++)
cr = cr + "\n";
dstDoc.insertString(dstDoc.getLength(), cr, changed);
} else if (v < 0) {
v = -v;
String cr = "";
for (int i = 0; i < v; i++)
cr = cr + "\n";
srcDoc.insertString(srcDoc.getLength(), cr, changed);
}
} else if (edit.getSrc().length() > 0) {
combinedDoc.insertString(combinedDoc.getLength(), s,
deleted);
srcDoc.insertString(srcDoc.getLength(), s, added);
dstDoc.insertString(dstDoc.getLength(), s.replaceAll(
"[^\n]", " "), deleted);
} else if (edit.getDst().length() > 0) {
combinedDoc.insertString(combinedDoc.getLength(), d, added);
srcDoc.insertString(srcDoc.getLength(), d.replaceAll(
"[^\n]", " "), deleted);
dstDoc.insertString(dstDoc.getLength(), d, added);
}
srcLast = edit.getSrcLocation() + s.length();
dstLast = edit.getDstLocation() + d.length();
}
if (srcLast < src.length()) {
String s = src.subSequence(srcLast, src.length()).toString();
combinedDoc.insertString(combinedDoc.getLength(), s, unchanged);
srcDoc.insertString(srcDoc.getLength(), s, unchanged);
}
if (dstLast < dst.length()) {
String d = dst.subSequence(dstLast, dst.length()).toString();
dstDoc.insertString(dstDoc.getLength(), d, unchanged);
}
combinedTextPane.setDocument(combinedDoc);
srcTextPane.setDocument(srcDoc);
dstTextPane.setDocument(dstDoc);
} catch (BadLocationException ble) {
combinedTextPane.setText(ble.toString());
}
}
private int countLines(String string) {
int lines = 0;
int last = -1;
while ((last = string.indexOf("\n", last + 1)) > -1)
lines++;
return lines;
}
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame("Diff");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DiffPanel panel = new DiffPanel(DiffPanel.SIDE_BY_SIDE);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.setBounds(200, 100, 1000, 700);
frame.setVisible(true);
String src = "abc\ndef\nghi\nqrs\nxyz\n";
String dst = "def\nghi\njkl\nmno\nxyz\nlmn\n";
if (args.length == 2) {
BufferedReader reader = new BufferedReader(new FileReader(args[0]));
String line;
StringBuffer buff = new StringBuffer();
while ((line = reader.readLine()) != null)
buff.append(line).append("\n");
reader.close();
src = buff.toString();
reader = new BufferedReader(new FileReader(args[1]));
buff = new StringBuffer();
while ((line = reader.readLine()) != null)
buff.append(line).append("\n");
reader.close();
dst = buff.toString();
}
List<Edit> edits = Diff.getEdits(src, dst, '\n');
System.out.println("Distance: " + Diff.getDistance(edits));
edits = Diff.refine(src, dst, edits);
System.out.println("Distance: " + Diff.getDistance(edits));
panel.showDifferences(src, dst, edits);
}
}