/** Copyright (c) 2011 Delcyon, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.delcyon.capo.util.diff; import com.delcyon.capo.util.diff.Diff.Side; /** * This is in charge out parsing, and writing lines from/for the diff class. * Everything has been put here, so that it is in one place. This is the only place that format should be changed otherwise things won't match up. * In general, it's a bad idea to mess around with this class unless the default diff stream format is causing you problems. * I think this format is esoteric enough to not run into common collisions out there. * <br/>format example: +(12,14)[12]hello world\n<br/> * Where the first char indicates the side ala std diff +/-/= * The two numbers in the parentheses are the tokenized stream positions for the base, and the modified file respectively. * The number in square brackets indicates the length of the data. * and everything afterwards is the data, including that newline char. * @author jeremiah * */ public class DiffEntry { public static final String INPUT_REGEX_FORMAT = "(["+Side.MOD.getDirectionChar()+Side.BASE.getDirectionChar()+Side.BOTH.getDirectionChar()+"])\\((\\d+),(\\d+)\\)\\[(\\d+)\\]"; public static final String INPUT_REGEX_REPLACEMENT = "$1,$2,$3,$4"; public static final String INPUT_REPLACEMENT_SPLIT = ","; public static final String OUTPUT_FORMAT = "%c(%d,%d)[%d]"; public static final char LINE_DESCRIPTOR_TERMINATOR_CHAR = OUTPUT_FORMAT.charAt(OUTPUT_FORMAT.length()-1); private char directionChar; private int expectedTextLength = 0; private long baseStreamPosition; private long otherStreamPosition; private byte[] data = null; /** * Used by the diff class to format an outgoing difference * @param side * @param expectedTextLength * @param baseStreamPosition * @param otherStreamPosition * @param data */ public DiffEntry(Side side, int expectedTextLength, long baseStreamPosition, long otherStreamPosition, byte[] data) { if (side == null) { this.directionChar = Side.BOTH.getDirectionChar(); } else { this.directionChar = side.getDirectionChar(); } this.expectedTextLength = expectedTextLength; this.baseStreamPosition = baseStreamPosition; this.otherStreamPosition = otherStreamPosition; this.data = data; } /** * Creates a DiffEntry from a line of a diff stream. * @param lineData * @return */ public static DiffEntry parseLineData(byte[] lineData) { return new DiffEntry(lineData); } /** * The parsing is done in the constructor. Probably a bad idea. * @param lineData */ private DiffEntry(byte[] lineData) { String text = new String(lineData); int lineDescriptorEndPosition = text.indexOf(LINE_DESCRIPTOR_TERMINATOR_CHAR); String[] descriptors = text.substring(0, lineDescriptorEndPosition+1).replaceAll(INPUT_REGEX_FORMAT, INPUT_REGEX_REPLACEMENT).split(INPUT_REPLACEMENT_SPLIT); this.data = text.substring(lineDescriptorEndPosition+1).getBytes(); directionChar = descriptors[0].charAt(0); baseStreamPosition = Long.parseLong(descriptors[1]); otherStreamPosition = Long.parseLong(descriptors[2]); expectedTextLength = Integer.parseInt(descriptors[3]); } /** * return the char for this line +/-/= */ public char getDirectionChar() { return directionChar; } /** * @return the length of the data for this entry. minus any diff information, and minus any trailing data. this uses the info stored in the diff entry itself. */ public int getExpectedTextLength() { return expectedTextLength; } /** * * @return where this entry is relative to the original stream */ public long getBaseStreamPosition() { return baseStreamPosition; } /** * @return where this entry is relative to the modified stream */ public long getOtherStreamPosition() { return otherStreamPosition; } /** * the data stored in the diff entry, and not the whole entry itself * @return */ public byte[] getData() { return data; } /** * @return this diff entry as a byte array, with diff meta data included */ public byte[] toByteArray() { String position = String.format(OUTPUT_FORMAT, getDirectionChar(),getBaseStreamPosition()+1l,getOtherStreamPosition()+1l,data.length); byte[] bytes = new byte[position.length()+data.length]; System.arraycopy(position.getBytes(), 0, bytes, 0, position.length()); //store the position information System.arraycopy(data, 0, bytes, position.length(), data.length); //store the data return bytes; } /** * returns the results of toByteArray as a String for convenience and debugging. */ @Override public String toString() { return new String(toByteArray()); } }