package org.tmatesoft.svn.core.internal.wc2.ng;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.internal.util.SVNCharsetOutputStream;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNPropertiesManager;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.util.SVNLogType;
public class SvnDiffCallback implements ISvnDiffCallback {
private static final long NON_EXSTENT_REVISION = -100;
private ISvnDiffGenerator generator;
private OutputStream outputStream;
private long revision2;
private long revision1;
private boolean noCopyFromOnAdd;
private boolean diffTargetIsCopy;
public SvnDiffCallback(ISvnDiffGenerator generator, long rev1, long rev2, boolean noCopyFromOnAdd, boolean diffTargetIsCopy, OutputStream outputStream) {
this.generator = generator;
this.noCopyFromOnAdd = noCopyFromOnAdd;
this.revision1 = rev1;
this.revision2 = rev2;
this.diffTargetIsCopy = diffTargetIsCopy;
this.outputStream = outputStream;
}
public void fileOpened(SvnDiffCallbackResult result, File path, long revision) throws SVNException {
}
public void fileChanged(SvnDiffCallbackResult result, File path, File leftFile, File rightFile, long rev1, long rev2, String mimeType1, String mimeType2, SVNProperties propChanges, SVNProperties originalProperties) throws SVNException {
if (leftFile != null) {
displayContentChanged(path, leftFile, rightFile, rev1, rev2, mimeType1, mimeType2, propChanges, originalProperties, OperationKind.Modified, null);
}
if (propChanges != null && !propChanges.isEmpty()) {
propertiesChanged(path, rev1, rev2, false, propChanges, originalProperties);
}
}
public void fileAdded(SvnDiffCallbackResult result, File path, File leftFile, File rightFile, long rev1, long rev2, String mimeType1, String mimeType2, File copyFromPath, long copyFromRevision, SVNProperties propChanges, SVNProperties originalProperties) throws SVNException {
generator.setForceEmpty(true);
if (diffTargetIsCopy) {
if (rev1 == SVNRepository.INVALID_REVISION && this.revision1 != SVNRepository.INVALID_REVISION) {
rev1 = this.revision1;
}
if (rev2 == SVNRepository.INVALID_REVISION && this.revision2 != SVNRepository.INVALID_REVISION) {
rev2 = this.revision2;
}
}
if (noCopyFromOnAdd && (copyFromPath != null || SVNRevision.isValidRevisionNumber(copyFromRevision))) {
SVNProperties newChanges = new SVNProperties(originalProperties);
newChanges.putAll(propChanges);
leftFile = null;
propChanges = newChanges;
originalProperties = new SVNProperties();
copyFromRevision = SVNRepository.INVALID_REVISION;
}
//TODO: no diff added?
if (rightFile != null && copyFromPath != null) {
displayContentChanged(path, leftFile, rightFile, NON_EXSTENT_REVISION, rev2, mimeType1, mimeType2, propChanges, originalProperties, OperationKind.Copied, copyFromPath);
} else if (rightFile != null) {
displayContentChanged(path, leftFile, rightFile, NON_EXSTENT_REVISION, rev2, mimeType1, mimeType2, propChanges, originalProperties, OperationKind.Added, null);
}
if (propChanges != null && !propChanges.isEmpty()) {
//we do not rev1 and rev2 here because SVN doesn't
propertiesChanged(path, NON_EXSTENT_REVISION, rev2, false, propChanges, originalProperties);
}
generator.setForceEmpty(false);
}
public void fileDeleted(SvnDiffCallbackResult result, File path, File leftFile, File rightFile, String mimeType1, String mimeType2, SVNProperties originalProperties) throws SVNException {
displayContentChanged(path, leftFile, null, revision1, NON_EXSTENT_REVISION, mimeType1, mimeType2, null, originalProperties, OperationKind.Deleted, null);
}
public void dirDeleted(SvnDiffCallbackResult result, File path) throws SVNException {
generator.displayDeletedDirectory(getTarget(path), getRevisionString(revision1), getRevisionString(NON_EXSTENT_REVISION), outputStream);
}
public void dirOpened(SvnDiffCallbackResult result, File path, long revision) throws SVNException {
}
public void dirAdded(SvnDiffCallbackResult result, File path, long revision, String copyFromPath, long copyFromRevision) throws SVNException {
generator.displayAddedDirectory(getTarget(path), getRevisionString(NON_EXSTENT_REVISION), getRevisionString(revision), outputStream);
}
public void dirPropsChanged(SvnDiffCallbackResult result, File path, boolean dirWasAdded, SVNProperties propChanges, SVNProperties originalProperties) throws SVNException {
originalProperties = originalProperties == null ? new SVNProperties() : originalProperties;
propChanges = propChanges == null ? new SVNProperties() : propChanges;
SVNProperties regularDiff = getRegularProperties(propChanges);
if (regularDiff == null || regularDiff.isEmpty()) {
return;
}
generator.displayPropsChanged(getTarget(path), dirWasAdded ? getRevisionString(NON_EXSTENT_REVISION) : getRevisionString(revision1), getRevisionString(revision2), dirWasAdded, originalProperties, regularDiff, outputStream);
}
public void dirClosed(SvnDiffCallbackResult result, File path, boolean dirWasAdded) throws SVNException {
}
private String getRevisionString(long revision) {
if (revision >= 0) {
return "(revision " + revision + ")";
} else if (revision == NON_EXSTENT_REVISION) {
return "(nonexistent)";
}
return "(working copy)";
}
private static SVNProperties getRegularProperties(SVNProperties propChanges) {
if (propChanges == null) {
return null;
}
final SVNProperties regularPropertiesChanges = new SVNProperties();
SvnNgPropertiesManager.categorizeProperties(propChanges, regularPropertiesChanges, null, null);
return regularPropertiesChanges;
}
public void propertiesChanged(File path, long revision1, long revision2, boolean dirWasAdded, SVNProperties diff, SVNProperties originalProperties) throws SVNException {
originalProperties = originalProperties == null ? new SVNProperties() : originalProperties;
diff = diff == null ? new SVNProperties() : diff;
SVNProperties regularDiff = getRegularProperties(diff);
if (regularDiff != null && !regularDiff.isEmpty()) {
generator.displayPropsChanged(getTarget(path), getRevisionString(revision1), getRevisionString(revision2), dirWasAdded, originalProperties, regularDiff, outputStream);
}
}
private void displayContentChanged(File path, File leftFile, File rightFile, long rev1, long rev2, String mimeType1, String mimeType2, SVNProperties propChanges, SVNProperties originalProperties, OperationKind operation, File copyFromPath) throws SVNException {
boolean resetEncoding = false;
OutputStream result = outputStream;
String encoding = defineEncoding(originalProperties, propChanges);
if (encoding != null) {
generator.setEncoding(encoding);
resetEncoding = true;
} else {
String conversionEncoding = defineConversionEncoding(originalProperties, propChanges);
if (conversionEncoding != null) {
resetEncoding = adjustDiffGenerator("UTF-8");
result = new SVNCharsetOutputStream(result, Charset.forName("UTF-8"), Charset.forName(conversionEncoding), CodingErrorAction.IGNORE, CodingErrorAction.IGNORE);
}
}
try {
generator.displayContentChanged(getTarget(path), leftFile, rightFile, getRevisionString(rev1), getRevisionString(rev2), mimeType1, mimeType2, operation, copyFromPath, originalProperties, propChanges, result);
} finally {
if (resetEncoding) {
generator.setEncoding(null);
generator.setEOL(null);
}
if (result instanceof SVNCharsetOutputStream) {
try {
result.flush();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e, SVNLogType.WC);
}
}
}
}
private String defineEncoding(SVNProperties properties, SVNProperties diff) {
ISvnDiffGenerator defaultGenerator = generator;
if (defaultGenerator.getEncoding() != null) {
return null;
}
String originalEncoding = getCharsetByMimeType(properties, defaultGenerator);
if (originalEncoding != null) {
return originalEncoding;
}
String changedEncoding = getCharsetByMimeType(diff, defaultGenerator);
if (changedEncoding != null) {
return changedEncoding;
}
return null;
}
private String getCharsetByMimeType(SVNProperties properties, ISvnDiffGenerator generator) {
if (properties == null) {
return null;
}
String mimeType = properties.getStringValue(SVNProperty.MIME_TYPE);
String charset = SVNPropertiesManager.determineEncodingByMimeType(mimeType);
return getCharset(charset, generator, false);
}
private String getCharset(SVNProperties properties, ISvnDiffGenerator generator) {
if (properties == null) {
return null;
}
String charset = properties.getStringValue(SVNProperty.CHARSET);
return getCharset(charset, generator, true);
}
private String getCharset(String charset, ISvnDiffGenerator generator, boolean allowNative) {
if (charset == null) {
return null;
}
if (allowNative && SVNProperty.NATIVE.equals(charset)) {
return generator.getEncoding();
}
if (Charset.isSupported(charset)) {
return charset;
}
return null;
}
private String defineConversionEncoding(SVNProperties properties, SVNProperties diff) {
ISvnDiffGenerator defaultGenerator = generator;
if (defaultGenerator.getEncoding() != null) {
return null;
}
String originalCharset = getCharset(properties, defaultGenerator);
if (originalCharset != null) {
return originalCharset;
}
String changedCharset = getCharset(diff, defaultGenerator);
if (changedCharset != null) {
return changedCharset;
}
String globalEncoding = getCharset(defaultGenerator.getGlobalEncoding(), defaultGenerator, false);
if (globalEncoding != null) {
return globalEncoding;
}
return null;
}
private boolean adjustDiffGenerator(String charset) {
boolean encodingAdjusted = false;
if (generator.getEncoding() == null) {
generator.setEncoding(charset);
encodingAdjusted = true;
}
if (generator.getEOL() == null) {
byte[] eol;
String eolString = System.getProperty("line.separator");
try {
eol = eolString.getBytes(charset);
} catch (UnsupportedEncodingException e) {
eol = eolString.getBytes();
}
generator.setEOL(eol);
}
return encodingAdjusted;
}
private SvnTarget getTarget(File path) {
return SvnTarget.fromFile(path);
}
public enum OperationKind {
Unchanged, Added, Deleted, Copied, Moved, Modified;
}
}