/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.wc;
import de.regnis.q.sequence.line.diff.QDiffGenerator;
import de.regnis.q.sequence.line.diff.QDiffGeneratorFactory;
import de.regnis.q.sequence.line.diff.QDiffManager;
import de.regnis.q.sequence.line.diff.QDiffUniGenerator;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNMergeRangeList;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.internal.wc.ISVNReturnValueCallback;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.util.SVNLogType;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
/**
* <b>DefaultSVNDiffGenerator</b> is a default implementation of
* <b>ISVNDiffGenerator</b>.
* <p>
* By default, if there's no any specified implementation of the diff generator's
* interface, SVNKit uses this default implementation. To set a custom
* diff driver use {@link SVNDiffClient#setDiffGenerator(ISVNDiffGenerator) setDiffGenerator()}.
*
* @version 1.3
* @since 1.2
* @author TMate Software Ltd.
*/
public class DefaultSVNDiffGenerator implements ISVNDiffGenerator {
protected static final String PROPERTIES_SEPARATOR = "___________________________________________________________________";
protected static final String HEADER_SEPARATOR = "===================================================================";
protected static final String WC_REVISION_LABEL = "(working copy)";
protected static final InputStream EMPTY_FILE_IS = SVNFileUtil.DUMMY_IN;
private boolean myIsForcedBinaryDiff;
private String myAnchorPath1;
private String myAnchorPath2;
private ISVNOptions myOptions;
private String myEncoding;
private byte[] myEOL;
private boolean myIsDiffDeleted;
private boolean myIsDiffAdded;
private boolean myIsDiffCopied;
private File myBasePath;
private boolean myIsDiffUnversioned;
private SVNDiffOptions myDiffOptions;
private Collection myRawDiffOptions;
private String myDiffCommand;
private boolean myIsUseAbsolutePaths;
/**
* Constructs a <b>DefaultSVNDiffGenerator</b>.
*
*/
public DefaultSVNDiffGenerator() {
myAnchorPath1 = "";
myAnchorPath2 = "";
setDiffDeleted(true);
setDiffAdded(true);
}
/**
* Initializes this generator with old and new diff anchor paths.
*
* @param anchorPath1 an old path/URL
* @param anchorPath2 a new path/URL
*/
public void init(String anchorPath1, String anchorPath2) {
myAnchorPath1 = anchorPath1.replace(File.separatorChar, '/');
myAnchorPath2 = anchorPath2.replace(File.separatorChar, '/');
}
/**
* Sets diff options containing diff rules.
*
* @param options diff options
*/
public void setDiffOptions(SVNDiffOptions options) {
myDiffOptions = options;
}
/**
* Sets a collection of raw (<code>String</code>) diff options.
*
* @param options raw options
*/
public void setRawDiffOptions(Collection options) {
myRawDiffOptions = options;
}
/**
* Sets global run-time options.
*
* @param options options implementation
*/
public void setOptions(ISVNOptions options){
myOptions = options;
}
/**
* Sets an external diff program for producing the difference between files.
*
* @param command external diff program
*/
public void setExternalDiffCommand(String command) {
myDiffCommand = command;
}
/**
* Sets the base path that must be stripped from the front of the paths of compared files.
* If <code>basePath</code> is not <span class="javakeyword">null</span> but is not a parent path of
* the target, this will lead to an error during diff.
*
* <p/>
* Note: <code>basePath</code> doesn't affect the path index generated by external diff programs.
*
* @param basePath common parent path to strip off the displayed paths
*/
public void setBasePath(File basePath) {
myBasePath = basePath;
}
/**
* Controls whether error is reported on failure to compute relative display path,
* or absolute path is used instead.
*
* @param fallback true to make generator use absolute path when relative path could not
* be computed.
*/
public void setFallbackToAbsolutePath(boolean fallback) {
myIsUseAbsolutePaths = fallback;
}
/**
* Enables or disables diffing deleted files.
*
* @param isDiffDeleted
*/
public void setDiffDeleted(boolean isDiffDeleted) {
myIsDiffDeleted = isDiffDeleted;
}
/**
* Tells whether deleted files must be diffed also.
*
* @return <span class="javakeyword">true</span> if deleted files must be diffed also
*/
public boolean isDiffDeleted() {
return myIsDiffDeleted;
}
/**
* Enables or disables diffing added files.
*
* @param isDiffAdded
*/
public void setDiffAdded(boolean isDiffAdded) {
myIsDiffAdded = isDiffAdded;
}
/**
* Tells whether added files must be diffed also.
*
* @return <span class="javakeyword">true</span> if added files must be diffed also
*/
public boolean isDiffAdded() {
return myIsDiffAdded;
}
/**
* Enables or disables copied files diffing.
*
* @param isDiffCopied
*/
public void setDiffCopied(boolean isDiffCopied) {
myIsDiffCopied = isDiffCopied;
}
/**
* Tells whether deleted files must be diffed also.
*
* @return <span class="javakeyword">true</span> if copied files must be diffed also
*/
public boolean isDiffCopied() {
return myIsDiffCopied;
}
/**
* Gets the diff options that are used by this generator.
* Creates a new one if none was used before.
*
* @return diff options
*/
public SVNDiffOptions getDiffOptions() {
if (myDiffOptions == null) {
myDiffOptions = new SVNDiffOptions();
}
return myDiffOptions;
}
protected String getDisplayPath(String path) throws SVNException {
if (myBasePath == null) {
return path;
}
if (path == null) {
path = "";
}
if (SVNPathUtil.isURL(path)) {
return path;
}
// treat as file path.
String basePath = myBasePath.getAbsolutePath().replace(File.separatorChar, '/');
path = new File(path).getAbsolutePath().replace(File.separatorChar, '/');
if (path.equals(basePath)) {
return ".";
}
String relativePath = SVNPathUtil.getPathAsChild(basePath, path);
if (relativePath == null) {
if (myIsUseAbsolutePaths) {
return path;
}
createBadRelativePathError(path);
}
if (relativePath.startsWith("./")) {
relativePath = relativePath.substring("./".length());
}
return relativePath;
}
/**
* Sets whether binary files diff must be forced or not.
*
* @param forced whether to force binary diff or not
*/
public void setForcedBinaryDiff(boolean forced) {
myIsForcedBinaryDiff = forced;
}
/**
* Tells if this generator forced binary files diff.
*
* @return <span class="javakeyword">true</span> if forces; otherwise <span class="javakeyword">false</span>
*/
public boolean isForcedBinaryDiff() {
return myIsForcedBinaryDiff;
}
/**
* Produces properties difference and writes it to <code>result</code>.
*
* @param path
* @param baseProps
* @param diff
* @param result
* @throws SVNException in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#IO_ERROR} error code - if an I\O error occurred
* </ul>
*/
public void displayPropDiff(String path, SVNProperties baseProps, SVNProperties diff, OutputStream result) throws SVNException {
baseProps = baseProps != null ? baseProps : new SVNProperties();
diff = diff != null ? diff : new SVNProperties();
for (Iterator changedPropNames = diff.nameSet().iterator(); changedPropNames.hasNext();) {
String name = (String) changedPropNames.next();
SVNPropertyValue originalValue = baseProps.getSVNPropertyValue(name);
SVNPropertyValue newValue = diff.getSVNPropertyValue(name);
if ((originalValue != null && originalValue.equals(newValue)) || (originalValue == null && newValue == null)) {
changedPropNames.remove();
}
}
if (diff.isEmpty()) {
return;
}
path = getDisplayPath(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
diff = new SVNProperties(diff);
try {
bos.write(getEOL());
bos.write(("Property changes on: " + (useLocalFileSeparatorChar() ? path.replace('/', File.separatorChar) : path)).getBytes(getEncoding()));
bos.write(getEOL());
bos.write(PROPERTIES_SEPARATOR.getBytes(getEncoding()));
bos.write(getEOL());
for (Iterator changedPropNames = diff.nameSet().iterator(); changedPropNames.hasNext();) {
String name = (String) changedPropNames.next();
SVNPropertyValue originalValue = baseProps != null ? baseProps.getSVNPropertyValue(name) : null;
SVNPropertyValue newValue = diff.getSVNPropertyValue(name);
String headerFormat = null;
if (originalValue == null) {
headerFormat = "Added: ";
} else if (newValue == null) {
headerFormat = "Deleted: ";
} else {
headerFormat = "Modified: ";
}
bos.write((headerFormat + name).getBytes(getEncoding()));
bos.write(getEOL());
if (SVNProperty.MERGE_INFO.equals(name)) {
displayMergeInfoDiff(bos, originalValue == null ? null : originalValue.getString(), newValue == null ? null : newValue.getString());
continue;
}
if (originalValue != null) {
bos.write(" - ".getBytes(getEncoding()));
bos.write(getPropertyAsBytes(originalValue, getEncoding()));
bos.write(getEOL());
}
if (newValue != null) {
bos.write(" + ".getBytes(getEncoding()));
bos.write(getPropertyAsBytes(newValue, getEncoding()));
bos.write(getEOL());
}
}
bos.write(getEOL());
} catch (IOException e) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
} finally {
try {
bos.close();
bos.writeTo(result);
} catch (IOException e) {
}
}
}
private byte[] getPropertyAsBytes(SVNPropertyValue value, String encoding){
if (value == null){
return null;
}
if (value.isString()){
try {
return value.getString().getBytes(encoding);
} catch (UnsupportedEncodingException e) {
return value.getString().getBytes();
}
}
return value.getBytes();
}
protected File getBasePath() {
return myBasePath;
}
/**
* Writes the difference between <code>file1</code> and <code>file2</code> as they are seen in
* <code>rev1</code> and <code>rev2</code> to <code>result</code>.
*
* @param path
* @param file1
* @param file2
* @param rev1
* @param rev2
* @param mimeType1
* @param mimeType2
* @param result
* @throws SVNException in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#EXTERNAL_PROGRAM} error code - if an external diff program
* exited with an error code value different from <code>0</code> and <code>1</code>
* <li/>exception with {@link SVNErrorCode#IO_ERROR} error code - if an I\O error occurred
* </ul>
*/
public void displayFileDiff(String path, File file1, File file2,
String rev1, String rev2, String mimeType1, String mimeType2, OutputStream result) throws SVNException {
path = getDisplayPath(path);
// if anchor1 is the same as anchor2 just use path.
// if anchor1 differs from anchor2 =>
// condence anchors (get common root and remainings).
int i = 0;
for(; i < myAnchorPath1.length() && i < myAnchorPath2.length() &&
myAnchorPath1.charAt(i) == myAnchorPath2.charAt(i); i++) {}
if (i < myAnchorPath1.length() || i < myAnchorPath2.length()) {
if (i == myAnchorPath1.length()) {
i = myAnchorPath1.length() - 1;
}
for(; i > 0 && myAnchorPath1.charAt(i) != '/'; i--) {}
}
String p1 = myAnchorPath1.substring(i) ;
String p2 = myAnchorPath2.substring(i);
if (p1.length() == 0) {
p1 = path;
} else if (p1.charAt(0) == '/') {
p1 = path + "\t(..." + p1 + ")";
} else {
p1 = path + "\t(.../" + p1 + ")";
}
if (p2.length() == 0) {
p2 = path;
} else if (p2.charAt(0) == '/') {
p2 = path + "\t(..." + p2 + ")";
} else {
p2 = path + "\t(.../" + p2 + ")";
}
String label1 = getLabel(p1, rev1);
String label2 = getLabel(p2, rev2);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
if (displayHeader(bos, path, file2 == null)) {
bos.close();
bos.writeTo(result);
return;
}
if (isHeaderForced(file1, file2)) {
bos.writeTo(result);
bos.reset();
}
} catch (IOException e) {
try {
bos.close();
bos.writeTo(result);
} catch (IOException inner) {
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
}
if (!isForcedBinaryDiff() && (SVNProperty.isBinaryMimeType(mimeType1) || SVNProperty.isBinaryMimeType(mimeType2))) {
try {
displayBinary(bos, mimeType1, mimeType2);
} catch (IOException e) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
} finally {
try {
bos.close();
bos.writeTo(result);
} catch (IOException e) {
}
}
return;
}
if (file1 == file2 && file1 == null) {
try {
bos.close();
bos.writeTo(result);
} catch (IOException e) {
}
return;
}
final String diffCommand = getExternalDiffCommand();
if (diffCommand != null) {
try {
bos.close();
bos.writeTo(result);
} catch (IOException e) {
}
Collection args = new LinkedList();
File diffCommandFile = new File(diffCommand);
args.add(diffCommandFile.getAbsolutePath().replace(File.separatorChar, '/'));
if (myRawDiffOptions != null) {
args.addAll(myRawDiffOptions);
} else {
Collection diffOptions = getDiffOptions().toOptionsCollection();
args.addAll(diffOptions);
args.add("-u");
}
if (label1 != null) {
args.add("-L");
args.add(label1);
}
if (label2 != null) {
args.add("-L");
args.add(label2);
}
boolean tmpFile1 = false;
boolean tmpFile2 = false;
if (file1 == null) {
file1 = SVNFileUtil.createTempFile("svn.", ".tmp");
tmpFile1 = true;
}
if (file2 == null) {
file2 = SVNFileUtil.createTempFile("svn.", ".tmp");
tmpFile2 = true;
}
String currentDir = new File("").getAbsolutePath().replace(File.separatorChar, '/');
String file1Path = file1.getAbsolutePath().replace(File.separatorChar, '/');
String file2Path = file2.getAbsolutePath().replace(File.separatorChar, '/');
if (file1Path.startsWith(currentDir)) {
file1Path = file1Path.substring(currentDir.length());
file1Path = file1Path.startsWith("/") ? file1Path.substring(1) : file1Path;
}
if (file2Path.startsWith(currentDir)) {
file2Path = file2Path.substring(currentDir.length());
file2Path = file2Path.startsWith("/") ? file2Path.substring(1) : file2Path;
}
args.add(file1Path);
args.add(file2Path);
try {
final Writer writer = new OutputStreamWriter(result, getEncoding());
SVNFileUtil.execCommand((String[]) args.toArray(new String[args.size()]), true,
new ISVNReturnValueCallback() {
public void handleReturnValue(int returnValue) throws SVNException {
if (returnValue != 0 && returnValue != 1) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.EXTERNAL_PROGRAM,
"''{0}'' returned {1}", new Object[] { diffCommand, String.valueOf(returnValue) });
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
}
public void handleChar(char ch) throws SVNException {
try {
writer.write(ch);
} catch (IOException ioe) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getMessage());
SVNErrorManager.error(err, ioe, SVNLogType.DEFAULT);
}
}
public boolean isHandleProgramOutput() {
return true;
}
});
writer.flush();
} catch (IOException ioe) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getMessage());
SVNErrorManager.error(err, ioe, SVNLogType.DEFAULT);
} finally {
try {
if (tmpFile1) {
SVNFileUtil.deleteFile(file1);
}
if (tmpFile2) {
SVNFileUtil.deleteFile(file2);
}
} catch (SVNException e) {
// skip
}
}
return;
}
// put header fields.
try {
displayHeaderFields(bos, label1, label2);
} catch (IOException e) {
try {
bos.close();
bos.writeTo(result);
} catch (IOException inner) {
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
}
String header;
try {
bos.close();
header = bos.toString(getEncoding());
} catch (IOException inner) {
header = "";
}
RandomAccessFile is1 = null;
RandomAccessFile is2 = null;
try {
is1 = file1 == null ? null : SVNFileUtil.openRAFileForReading(file1);
is2 = file2 == null ? null : SVNFileUtil.openRAFileForReading(file2);
QDiffUniGenerator.setup();
Map properties = new SVNHashMap();
properties.put(QDiffGeneratorFactory.IGNORE_EOL_PROPERTY, Boolean.valueOf(getDiffOptions().isIgnoreEOLStyle()));
if (getDiffOptions().isIgnoreAllWhitespace()) {
properties.put(QDiffGeneratorFactory.IGNORE_SPACE_PROPERTY, QDiffGeneratorFactory.IGNORE_ALL_SPACE);
} else if (getDiffOptions().isIgnoreAmountOfWhitespace()) {
properties.put(QDiffGeneratorFactory.IGNORE_SPACE_PROPERTY, QDiffGeneratorFactory.IGNORE_SPACE_CHANGE);
}
QDiffGenerator generator = new QDiffUniGenerator(properties, header);
Writer writer = new OutputStreamWriter(result, getEncoding());
QDiffManager.generateTextDiff(is1, is2, getEncoding(), writer, generator);
writer.flush();
} catch (IOException e) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage());
SVNErrorManager.error(err, e, SVNLogType.DEFAULT);
} finally {
SVNFileUtil.closeFile(is1);
SVNFileUtil.closeFile(is2);
}
}
/**
* Sets the encoding to use for diff output.
* @param encoding charset name
*/
public void setEncoding(String encoding) {
myEncoding = encoding;
}
/**
* Returns the encoding used for diff output.
*
* @return charset name
*/
public String getEncoding() {
if (hasEncoding()) {
return myEncoding;
}
return getOptions().getNativeCharset();
}
/**
* Says whether this generator is using any special (non-native)
* charset for outputting diffs.
*
* @return <span class="javakeyword">true</span> if yes;
* otherwise <span class="javakeyword">false</span>
*/
public boolean hasEncoding() {
return myEncoding != null;
}
/**
* Says whether this generator is using any special (non-native)
* EOL bytes for outputting diffs.
*
* @return <span class="javakeyword">true</span> if yes;
* otherwise <span class="javakeyword">false</span>
*/
public boolean hasEOL() {
return myEOL != null;
}
/**
* Returns the encoding specified by svnkit.global-charset option
* of the global configuration.
*
* @return global charset name
*/
public String getGlobalEncoding() {
if (getOptions() instanceof DefaultSVNOptions) {
DefaultSVNOptions defaultOptions = (DefaultSVNOptions) getOptions();
return defaultOptions.getGlobalCharset();
}
return null;
}
/**
* Sets the EOL bytes to use in diff output.
*
* @param eol EOL bytes
*/
public void setEOL(byte[] eol){
myEOL = eol;
}
/**
* Returns the EOL marker bytes being in use.
* If no EOL bytes were provided, uses {@link ISVNOptions#getNativeEOL() native EOL} fetched from
* the options.
*
* @return EOL bytes
*/
public byte[] getEOL(){
if (myEOL == null){
myEOL = getOptions().getNativeEOL();
}
return myEOL;
}
/**
* Creates a temporary directory for diff files.
*
* @return returns the temp directory
* @throws SVNException
*/
public File createTempDirectory() throws SVNException {
return SVNFileUtil.createTempDirectory("diff");
}
/**
* Says if unversioned files are also diffed or ignored.
*
* <p>
* By default unversioned files are ignored.
*
* @return <span class="javakeyword">true</span> if diffed,
* <span class="javakeyword">false</span> if ignored
* @see #setDiffUnversioned(boolean)
*
*/
public boolean isDiffUnversioned() {
return myIsDiffUnversioned;
}
/**
* Includes or not unversioned files into diff processing.
*
* <p>
* If a diff operation is invoked on a versioned directory and
* <code>diffUnversioned</code> is <span class="javakeyword">true</span>
* then all unversioned files that may be met in the directory will
* be processed as added. Otherwise if <code>diffUnversioned</code>
* is <span class="javakeyword">false</span> such files are ignored.
*
* <p>
* By default unversioned files are ignored.
*
* @param diffUnversioned controls whether to diff unversioned files
* or not
* @see #isDiffUnversioned()
*/
public void setDiffUnversioned(boolean diffUnversioned) {
myIsDiffUnversioned = diffUnversioned;
}
/**
* Does nothing.
*
* @param path a directory path
* @param rev1 the first diff revision
* @param rev2 the second diff revision
* @throws SVNException
*/
public void displayDeletedDirectory(String path, String rev1, String rev2) throws SVNException {
// not implemented.
}
/**
* Does nothing.
*
* @param path a directory path
* @param rev1 the first diff revision
* @param rev2 the second diff revision
* @throws SVNException
*/
public void displayAddedDirectory(String path, String rev1, String rev2) throws SVNException {
// not implemented.
}
protected String getExternalDiffCommand() {
if (myDiffCommand != null) {
return myDiffCommand;
}
if (myOptions instanceof DefaultSVNOptions) {
return ((DefaultSVNOptions) myOptions).getDiffCommand();
}
return null;
}
protected ISVNOptions getOptions(){
if (myOptions == null){
myOptions = new DefaultSVNOptions();
}
return myOptions;
}
protected void displayBinary(OutputStream os, String mimeType1, String mimeType2) throws IOException {
os.write("Cannot display: file marked as a binary type.".getBytes(getEncoding()));
os.write(getEOL());
if (SVNProperty.isBinaryMimeType(mimeType1)
&& !SVNProperty.isBinaryMimeType(mimeType2)) {
os.write("svn:mime-type = ".getBytes(getEncoding()));
os.write(mimeType1.getBytes(getEncoding()));
os.write(getEOL());
} else if (!SVNProperty.isBinaryMimeType(mimeType1)
&& SVNProperty.isBinaryMimeType(mimeType2)) {
os.write("svn:mime-type = ".getBytes(getEncoding()));
os.write(mimeType2.getBytes(getEncoding()));
os.write(getEOL());
} else if (SVNProperty.isBinaryMimeType(mimeType1)
&& SVNProperty.isBinaryMimeType(mimeType2)) {
if (mimeType1.equals(mimeType2)) {
os.write("svn:mime-type = ".getBytes(getEncoding()));
os.write(mimeType2.getBytes(getEncoding()));
os.write(getEOL());
} else {
os.write("svn:mime-type = (".getBytes(getEncoding()));
os.write(mimeType1.getBytes(getEncoding()));
os.write(", ".getBytes(getEncoding()));
os.write(mimeType2.getBytes(getEncoding()));
os.write(")".getBytes(getEncoding()));
os.write(getEOL());
}
}
}
protected boolean displayHeader(OutputStream os, String path, boolean deleted) throws IOException {
if (deleted && !isDiffDeleted()) {
os.write("Index: ".getBytes(getEncoding()));
os.write(path.getBytes(getEncoding()));
os.write(" (deleted)".getBytes(getEncoding()));
os.write(getEOL());
os.write(HEADER_SEPARATOR.getBytes(getEncoding()));
os.write(getEOL());
return true;
}
os.write("Index: ".getBytes(getEncoding()));
os.write(path.getBytes(getEncoding()));
os.write(getEOL());
os.write(HEADER_SEPARATOR.getBytes(getEncoding()));
os.write(getEOL());
return false;
}
protected void displayHeaderFields(OutputStream os, String label1, String label2) throws IOException {
os.write("--- ".getBytes(getEncoding()));
os.write(label1.getBytes(getEncoding()));
os.write(getEOL());
os.write("+++ ".getBytes(getEncoding()));
os.write(label2.getBytes(getEncoding()));
os.write(getEOL());
}
protected boolean isHeaderForced(File file1, File file2) {
return (file1 == null && file2 != null);
}
protected boolean useLocalFileSeparatorChar() {
return true;
}
protected String getLabel(String path, String revToken) {
revToken = revToken == null ? WC_REVISION_LABEL : revToken;
return path + "\t" + revToken;
}
private void displayMergeInfoDiff(ByteArrayOutputStream baos, String oldValue, String newValue) throws SVNException, IOException {
Map oldMergeInfo = null;
Map newMergeInfo = null;
if (oldValue != null) {
oldMergeInfo = SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(oldValue), null);
}
if (newValue != null) {
newMergeInfo = SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(newValue), null);
}
Map deleted = new TreeMap();
Map added = new TreeMap();
SVNMergeInfoUtil.diffMergeInfo(deleted, added, oldMergeInfo, newMergeInfo, true);
for (Iterator paths = deleted.keySet().iterator(); paths.hasNext();) {
String path = (String) paths.next();
SVNMergeRangeList rangeList = (SVNMergeRangeList) deleted.get(path);
baos.write((" Reverse-merged " + path + ":r").getBytes(getEncoding()));
baos.write(rangeList.toString().getBytes(getEncoding()));
baos.write(getEOL());
}
for (Iterator paths = added.keySet().iterator(); paths.hasNext();) {
String path = (String) paths.next();
SVNMergeRangeList rangeList = (SVNMergeRangeList) added.get(path);
baos.write((" Merged " + path + ":r").getBytes(getEncoding()));
baos.write(rangeList.toString().getBytes(getEncoding()));
baos.write(getEOL());
}
}
private void createBadRelativePathError(String path) throws SVNException {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.BAD_RELATIVE_PATH,
"Path ''{0}'' must be an immediate child of the directory ''{1}''",
new Object[] { path, myBasePath });
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
}