package org.jmeld.model; import org.jmeld.diff.JMChunk; import org.jmeld.diff.JMDelta; import org.jmeld.diff.JMRevision; import org.jmeld.ui.BufferDiffPanel; import org.jmeld.ui.FilePanel; import org.jmeld.ui.text.BufferDocumentIF; import org.jmeld.ui.util.Colors; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.MatteBorder; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import java.awt.*; import java.util.*; import java.util.List; /** * Modelo de levenstein * User: alberto * Date: 7/12/12 * Time: 16:40 */ public class LevenshteinTableModel extends DefaultTableModel { String origin; String destiny; private JMRevision currentRevision; private FilePanel[] filePanels; private HashMap<Point,Color> routeDiff; private HashMap<Point, MatteBorder> borderChunks; private HashMap<Point, MatteBorder> borderSelections; private boolean showSelectionPath; public void buildModel() { if (!isAllDataAvaliable()) { return; } borderSelections = new HashMap<>(); setDataVector(newVector(origin.length() + 2), newVector(destiny.length() + 2)); buildSolutionPath(); int rowCount = getRowCount(); int columnCount = getColumnCount(); for (int columna = 0; columna < columnCount; columna++) { if (columna < 2) { setValueAt("", 0, columna); } else { setValueAt(destiny.charAt(columna - 2), 0, columna); } } for (int fila = 0; fila < rowCount; fila++) { if (fila < 2) { setValueAt("", fila, 0); } else { setValueAt(origin.charAt(fila - 2), fila, 0); } } int minLength = rowCount > columnCount ? columnCount : rowCount; for (int diagonal = 1; diagonal < minLength; diagonal++) { if (diagonal == 1) { setValueAt(0, diagonal, diagonal); } else { Object o = getValueAt(diagonal - 1, diagonal - 1); int incremento; if (origin.charAt(diagonal - 2) == destiny.charAt(diagonal - 2)) { incremento = 0; } else { incremento = 1; } setValueAt((Integer) o + incremento, diagonal, diagonal); } for (int columna = diagonal + 1; columna < columnCount; columna++) { setValueAt((Integer) getValueAt(diagonal, columna - 1) + 1, diagonal, columna); } for (int fila = diagonal + 1; fila < rowCount; fila++) { setValueAt((Integer) getValueAt(fila - 1, diagonal) + 1, fila, diagonal); } } } private boolean isAllDataAvaliable() { return getFilePanels() != null && getCurrentRevision() != null && origin != null && destiny != null; } class ColorPoint extends Point { Color color; public ColorPoint(int x, int y, Color color) { super(x, y); this.color = color; } } private void buildSolutionPath() { BufferDocumentIF originalBufferDocument = filePanels[BufferDiffPanel.LEFT].getBufferDocument(); BufferDocumentIF revisedBufferDocument = filePanels[BufferDiffPanel.RIGHT].getBufferDocument(); java.util.List<JMDelta> deltas = currentRevision.getDeltas(); routeDiff = new HashMap<>(); borderChunks = new HashMap<>(); int xOffset = 0; int yOffset = 0; for (Iterator<JMDelta> iterator = deltas.iterator(); iterator.hasNext(); ) { JMDelta next = iterator.next(); JMChunk original = next.getOriginal(); JMChunk revised = next.getRevised(); int originalLines = originalBufferDocument.getNumberOfLines(); int revisedLines = revisedBufferDocument.getNumberOfLines(); int offsetForLineOriginal; if (original.getAnchor() == originalLines) { offsetForLineOriginal = originalBufferDocument.getOffsetForLine(original.getAnchor() - 1) + 1; } else { offsetForLineOriginal = originalBufferDocument.getOffsetForLine(original.getAnchor()); } int offsetForLineRevised; if (revised.getAnchor() == revisedLines) { offsetForLineRevised = revisedBufferDocument.getOffsetForLine(revised.getAnchor() - 1) + 1; } else { offsetForLineRevised = revisedBufferDocument.getOffsetForLine(revised.getAnchor()); } for (int i = 0; i < offsetForLineOriginal - xOffset; i++) { routeDiff.put(new Point(i + xOffset, i + yOffset), Color.GRAY); } if (next.isAdd()) { int lineNumber = revised.getAnchor() + revised.getSize() - 1; int yOffsetForLineEnd = revisedBufferDocument.getOffsetForLine(lineNumber) + + revisedBufferDocument.getLineText(lineNumber).length(); for (int i = 0; i < yOffsetForLineEnd - offsetForLineRevised; i++) { MatteBorder border; if (i == 0) { border = BorderFactory.createMatteBorder(2, 2, 2, 0, Color.BLACK); } else if (i == yOffsetForLineEnd - offsetForLineRevised - 1) { border = BorderFactory.createMatteBorder(2, 0, 2, 2, Color.BLACK); } else { border = BorderFactory.createMatteBorder(2, 0, 2, 0, Color.BLACK); } Point point = new Point(offsetForLineOriginal - 1, i + offsetForLineRevised); addBorderChunks(point, border); routeDiff.put(point, Colors.ADDED); } xOffset = offsetForLineOriginal; yOffset = yOffsetForLineEnd; } else if (next.isDelete()) { int lineNumber = original.getAnchor() + original.getSize() - 1; int xOffsetForLine = originalBufferDocument.getOffsetForLine(lineNumber) + originalBufferDocument.getLineText(lineNumber).length(); for (int i = 0; i < xOffsetForLine - offsetForLineOriginal; i++) { Point point = new Point(i + offsetForLineOriginal, offsetForLineRevised - 1); routeDiff.put(point, Colors.DELETED); MatteBorder border; if (i == 0) { border = BorderFactory.createMatteBorder(2, 2, 0, 2, Color.BLACK); } else if (i == xOffsetForLine - offsetForLineOriginal - 1) { border = BorderFactory.createMatteBorder(0, 2, 2, 2, Color.BLACK); } else { border = BorderFactory.createMatteBorder(0, 2, 0, 2, Color.BLACK); } addBorderChunks(point, border); } xOffset = xOffsetForLine; yOffset = offsetForLineRevised; } else if (next.isChange()) { List<JMDelta> changedDeltas = next.getChangeRevision().getDeltas(); xOffset = offsetForLineOriginal; yOffset = offsetForLineRevised; for (Iterator<JMDelta> jmDeltaIterator = changedDeltas.iterator(); jmDeltaIterator.hasNext(); ) { JMDelta changeDelta = jmDeltaIterator.next(); JMChunk originalChange = changeDelta.getOriginal(); JMChunk revisedChange = changeDelta.getRevised(); int sameChars = originalChange.getAnchor() - xOffset; for (int i = 0; i < sameChars; i++) { routeDiff.put(new Point(i + xOffset, i + yOffset), Color.GRAY); } xOffset += sameChars; yOffset += sameChars; int removeFromOrigin = originalChange.getSize(); int addFromRevised = revisedChange.getSize(); for (int i = 0; i < removeFromOrigin; i++) { Point point = new Point(i + xOffset, yOffset - 1); routeDiff.put(point, Colors.DELETED); MatteBorder border; if (i == 0) { for (int j = 0; j < addFromRevised; j++) { if (j == addFromRevised - 1) { border = BorderFactory.createMatteBorder(2, 0, 0, 2, Color.BLACK); } else { border = BorderFactory.createMatteBorder(2, 0, 0, 0, Color.BLACK); } addBorderChunks(new Point(i + xOffset, yOffset + j), border); } border = BorderFactory.createMatteBorder(2, 2, 0, 0, Color.BLACK); addBorderChunks(point, border); } if (i == removeFromOrigin - 1) { border = BorderFactory.createMatteBorder(0, 2, 2, 0, Color.BLACK); } else { border = BorderFactory.createMatteBorder(0, 0, 0, 2, Color.BLACK); addBorderChunks(new Point(i + xOffset, yOffset - 1 + addFromRevised), border); border = BorderFactory.createMatteBorder(0, 2, 0, 0, Color.BLACK); } addBorderChunks(point, border); } xOffset += removeFromOrigin; if (addFromRevised == 0) { Point point = new Point(xOffset - 1, yOffset - 1); MatteBorder border = BorderFactory.createMatteBorder(0, 0, 0, 2, Color.BLACK); addBorderChunks(point, border); } for (int i = 0; i < addFromRevised; i++) { Point point = new Point(xOffset - 1, i + yOffset); routeDiff.put(point, Colors.ADDED); MatteBorder border; if (i == 0 && removeFromOrigin == 0) { for (int j = 0; j < addFromRevised; j++) { if (j == addFromRevised - 1) { border = BorderFactory.createMatteBorder(2, 0, 0, 2, Color.BLACK); } else { border = BorderFactory.createMatteBorder(2, 0, 0, 0, Color.BLACK); } addBorderChunks(new Point(i-1 + xOffset, yOffset + j), border); } border = BorderFactory.createMatteBorder(2, 2, 0, 0, Color.BLACK); addBorderChunks(point, border); } if (i == addFromRevised - 1) { border = BorderFactory.createMatteBorder(0, 0, 2, 2, Color.BLACK); } else { border = BorderFactory.createMatteBorder(0, 0, 2, 0, Color.BLACK); } addBorderChunks(point, border); } yOffset+=addFromRevised; } } } int xSize = originalBufferDocument.getDocument().getLength(); for (int i = 0; i < xSize - xOffset; i++) { routeDiff.put(new Point(i + xOffset, i + yOffset), Color.GRAY); } } private void addBorderChunks(Point point, MatteBorder border) { MatteBorder oldborder = borderChunks.get(point); if (oldborder !=null) { Insets oldborderInsets = oldborder.getBorderInsets(); Insets borderInsets = border.getBorderInsets(); if (oldborderInsets.top == 0 && borderInsets.top > 0) { oldborderInsets.top = borderInsets.top; } if (oldborderInsets.left == 0 && borderInsets.left > 0) { oldborderInsets.left = borderInsets.left; } if (oldborderInsets.right == 0 && borderInsets.right > 0) { oldborderInsets.right = borderInsets.right; } if (oldborderInsets.bottom == 0 && borderInsets.bottom > 0) { oldborderInsets.bottom = borderInsets.bottom; } border = BorderFactory.createMatteBorder( oldborderInsets.top, oldborderInsets.left, oldborderInsets.bottom, oldborderInsets.right, border.getMatteColor()); } borderChunks.put(point, border); } private Vector newVector(int size) { Vector vector = new Vector(size); vector.setSize(size); return vector; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; buildModel(); } public String getDestiny() { return destiny; } public void setDestiny(String destiny) { this.destiny = destiny; buildModel(); } public DefaultTableCellRenderer getCellRenderer() { return new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if ("\n".equals(value.toString())) { value = "\\n"; } Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); Color background = Color.WHITE; if (row < 2 && column < 2) { if (row == column) { background = Color.GREEN; } else { background = Color.BLUE; } } else { if (row > 1) { if (column > 1) { if (origin.charAt(row - 2) == destiny.charAt(column - 2)) { background = Color.ORANGE; } } } if (row == 0 || column == 0) { background = new Color(0x550099FF, true); } } if (isSelected || table.isColumnSelected(column)) { Color newColor = Color.CYAN; Color mixColor = Color.WHITE; background = new Color(background.getRed() * newColor.getRed()/mixColor.getRed() ,background.getGreen() * newColor.getGreen()/mixColor.getGreen() ,background.getBlue() * newColor.getBlue()/mixColor.getBlue()); } Point point = new Point(row - 2, column - 2); if (showSelectionPath) { Color color = routeDiff.get(point); if (color != null) { background = color; } } Border border = borderChunks.get(point); Border borderSelection = borderSelections.get(point); if (border == null) { if (borderSelection != null) { border = borderSelection; } } else { if (borderSelection != null) { border = new CompoundBorder(border, borderSelection); } } if (border != null) { ((JLabel)tableCellRendererComponent).setBorder(border); } tableCellRendererComponent.setBackground(background); ((JLabel) tableCellRendererComponent).setHorizontalAlignment(SwingConstants.CENTER); return tableCellRendererComponent; } }; } public void setCurrentRevision(JMRevision currentRevision) { this.currentRevision = currentRevision; buildModel(); } public JMRevision getCurrentRevision() { return currentRevision; } public void setFilePanels(FilePanel[] filePanels) { this.filePanels = filePanels; buildModel(); } public FilePanel[] getFilePanels() { return filePanels; } public boolean isShowSelectionPath() { return showSelectionPath; } public void setShowSelectionPath(boolean showSelectionPath) { this.showSelectionPath = showSelectionPath; } public HashMap<Point, MatteBorder> getBorderSelections() { return borderSelections; } public void setBorderSelections(HashMap<Point, MatteBorder> borderSelection) { this.borderSelections = borderSelection; } }