package org.tmatesoft.svn.core.internal.wc2.ng;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.tmatesoft.svn.core.ISVNCanceller;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
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.SVNPropertyValue;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.ISVNUpdateEditor;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb;
import org.tmatesoft.svn.core.internal.wc17.db.SVNWCDb;
import org.tmatesoft.svn.core.internal.wc17.db.Structure;
import org.tmatesoft.svn.core.internal.wc17.db.StructureFields;
import org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbShared;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.core.wc2.SvnChecksum;
import org.tmatesoft.svn.util.SVNLogType;
public class SvnDiffEditor implements ISVNEditor, ISVNUpdateEditor {
//immutable
private SVNDepth depth;
private SVNWCContext context;
private ISVNWCDb db;
private File anchorAbspath;
private String target;
private boolean useTextBase;
private boolean showCopiesAsAdds;
private ISvnDiffCallback callback;
private Collection<String> changelists;
private boolean ignoreAncestry;
private boolean useGitDiffFormat;
private ISVNCanceller canceller;
private boolean reverseOrder; //actually, has the opposite meaning
//mutable
private long revision;
private boolean rootOpened;
private Entry rootEntry;
private Entry currentEntry;
private Collection<File> tempFiles;
//once initialized
private final SVNDeltaProcessor deltaProcessor;
public SvnDiffEditor(File anchorAbspath, String target, ISvnDiffCallback callback, SVNDepth depth, SVNWCContext context, boolean reverseOrder, boolean useTextBase, boolean showCopiesAsAdds, boolean ignoreAncestry, Collection<String> changelists, boolean useGitDiffFormat, ISVNCanceller canceller) {
this.depth = depth;
this.context = context;
this.db = context.getDb();
this.anchorAbspath = anchorAbspath;
this.target = target;
this.useTextBase = useTextBase;
this.showCopiesAsAdds = showCopiesAsAdds;
this.callback = callback;
this.changelists = changelists;
this.ignoreAncestry = ignoreAncestry;
this.useGitDiffFormat = useGitDiffFormat;
this.canceller = canceller;
this.reverseOrder = reverseOrder;
this.deltaProcessor = new SVNDeltaProcessor();
this.tempFiles = new ArrayList<File>();
}
public SvnDiffEditor() {
this.deltaProcessor = new SVNDeltaProcessor();
}
public void targetRevision(long revision) throws SVNException {
this.revision = revision;
}
public void openRoot(long revision) throws SVNException {
this.rootOpened = true;
rootEntry = new Entry(false, "", null, false, depth, anchorAbspath);
currentEntry = rootEntry;
}
public void deleteEntry(String path, long revision) throws SVNException {
File localAbspath = getLocalAbspath(path);
addToCompared(currentEntry, path);
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(localAbspath, StructureFields.NodeInfo.status, StructureFields.NodeInfo.kind);
ISVNWCDb.SVNWCDbKind kind = nodeInfoStructure.get(StructureFields.NodeInfo.kind);
ISVNWCDb.SVNWCDbStatus status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
if (!useTextBase && status == ISVNWCDb.SVNWCDbStatus.Deleted) {
return;
}
switch (kind) {
case File:
case Symlink:
if (reverseOrder) {
File textBase = getPristineFile(localAbspath, useTextBase);
SVNProperties baseProps = context.getPristineProps(localAbspath);
String baseMimeType = getPropMimeType(baseProps);
callback.fileDeleted(null, localAbspath, textBase, null, baseMimeType, null, baseProps);
} else {
reportFileAdded(localAbspath, path);
}
break;
case Dir:
reportDirectoryAdded(localAbspath, path, SVNDepth.INFINITY);
break;
default:
break;
}
}
public void absentDir(String path) throws SVNException {
}
public void absentFile(String path) throws SVNException {
}
public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException {
SVNDepth subdirDepth = (depth == SVNDepth.IMMEDIATES) ? SVNDepth.EMPTY : depth;
addToCompared(currentEntry, path);
currentEntry = new Entry(false, path, currentEntry, true, subdirDepth, getLocalAbspath(path));
}
public void openDir(String path, long revision) throws SVNException {
SVNDepth subdirDepth = (depth == SVNDepth.IMMEDIATES) ? SVNDepth.EMPTY : depth;
addToCompared(currentEntry, path);
currentEntry = new Entry(false, path, currentEntry, false, subdirDepth, getLocalAbspath(path));
}
public void closeDir() throws SVNException {
if (!currentEntry.propChanges.isEmpty()) {
SVNProperties originalProps;
if (currentEntry.added) {
originalProps = new SVNProperties();
} else {
if (useTextBase) {
originalProps = context.getPristineProps(currentEntry.localAbspath);
} else {
originalProps = context.getActualProps(currentEntry.localAbspath);
SVNProperties baseProps = context.getPristineProps(currentEntry.localAbspath);
SVNProperties reposProps = applyPropsChanges(baseProps, currentEntry.propChanges);
currentEntry.propChanges = computePropDiff(originalProps, reposProps);
}
}
if (!reverseOrder) {
reversePropChanges(originalProps, currentEntry.propChanges);
}
callback.dirPropsChanged(null, currentEntry.localAbspath, currentEntry.added, currentEntry.propChanges, originalProps);
addToCompared(currentEntry, currentEntry.path);
}
if (!currentEntry.added) {
walkLocalNodesDiff(currentEntry.localAbspath, currentEntry.path, currentEntry.depth, currentEntry.compared);
}
callback.dirClosed(null, currentEntry.localAbspath, currentEntry.added);
currentEntry = currentEntry.parent;
}
public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException {
addToCompared(currentEntry, path);
currentEntry = new Entry(true, path, currentEntry, true, SVNDepth.UNKNOWN, getLocalAbspath(path));
}
public void openFile(String path, long revision) throws SVNException {
addToCompared(currentEntry, path);
currentEntry = new Entry(true, path, currentEntry, false, SVNDepth.UNKNOWN, getLocalAbspath(path));
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(currentEntry.localAbspath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.checksum);
currentEntry.baseChecksum = baseInfo.checksum;
callback.fileOpened(null, currentEntry.localAbspath, revision);
}
public void applyTextDelta(String path, String baseChecksum) throws SVNException {
InputStream sourceStream;
if (currentEntry.baseChecksum != null) {
sourceStream = db.readPristine(currentEntry.localAbspath, currentEntry.baseChecksum);
} else {
sourceStream = SVNFileUtil.DUMMY_IN;
}
currentEntry.file = createTempFile(db.getWCRootTempDir(currentEntry.localAbspath));
deltaProcessor.applyTextDelta(sourceStream, currentEntry.file, true);
}
public void closeFile(String path, String textChecksum) throws SVNException {
try {
if (textChecksum != null) {
SvnChecksum reposChecksum = currentEntry.resultChecksum;
if (reposChecksum == null) {
reposChecksum = currentEntry.baseChecksum;
}
if (reposChecksum.getKind() != SvnChecksum.Kind.md5) {
reposChecksum = db.getPristineMD5(currentEntry.localAbspath, reposChecksum);
}
if (!reposChecksum.getDigest().equals(textChecksum)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH, "Checksum mismatch for ''{0}''", currentEntry.localAbspath);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
}
ISVNWCDb.SVNWCDbStatus status;
SvnChecksum pristineChecksum;
boolean hadProps;
boolean propsMod;
try {
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(currentEntry.localAbspath,
StructureFields.NodeInfo.status,
StructureFields.NodeInfo.checksum,
StructureFields.NodeInfo.hadProps,
StructureFields.NodeInfo.propsMod);
status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
pristineChecksum = nodeInfoStructure.get(StructureFields.NodeInfo.checksum);
hadProps = nodeInfoStructure.is(StructureFields.NodeInfo.hadProps);
propsMod = nodeInfoStructure.is(StructureFields.NodeInfo.propsMod);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) {
status = ISVNWCDb.SVNWCDbStatus.NotPresent;
pristineChecksum = null;
hadProps = false;
propsMod = false;
} else {
throw e;
}
}
SVNProperties pristineProps;
File pristineFile;
if (currentEntry.added) {
pristineProps = new SVNProperties();
pristineFile = null;
} else {
if (status != ISVNWCDb.SVNWCDbStatus.Normal) {
Structure<StructureFields.NodeInfo> nodeInfoStructure = SvnWcDbShared.getBaseInfo((SVNWCDb) db, currentEntry.localAbspath,
StructureFields.NodeInfo.checksum,
StructureFields.NodeInfo.hadProps);
pristineChecksum = nodeInfoStructure.get(StructureFields.NodeInfo.checksum);
hadProps = nodeInfoStructure.is(StructureFields.NodeInfo.hadProps);
}
pristineFile = db.getPristinePath(currentEntry.localAbspath, pristineChecksum);
if (hadProps) {
pristineProps = db.getBaseProps(currentEntry.localAbspath);
} else {
pristineProps = new SVNProperties();
}
}
if (status == ISVNWCDb.SVNWCDbStatus.Added) {
ISVNWCDb.WCDbAdditionInfo wcDbAdditionInfo = db.scanAddition(currentEntry.localAbspath, ISVNWCDb.WCDbAdditionInfo.AdditionInfoField.status);
status = wcDbAdditionInfo.status;
}
SVNProperties reposProps = applyPropsChanges(pristineProps, currentEntry.propChanges);
String reposMimeType = getPropMimeType(reposProps);
File reposFile = currentEntry.file != null ? currentEntry.file : pristineFile;
if (currentEntry.added || (!useTextBase && status == ISVNWCDb.SVNWCDbStatus.Deleted)) {
if (reverseOrder) {
callback.fileAdded(null, currentEntry.localAbspath, null, reposFile, 0, revision, null, reposMimeType, null, -1, currentEntry.propChanges, new SVNProperties());
} else {
callback.fileDeleted(null, currentEntry.localAbspath, reposFile, null, reposMimeType, null, reposProps);
}
return;
}
if ((status == ISVNWCDb.SVNWCDbStatus.Copied || status == ISVNWCDb.SVNWCDbStatus.MovedHere) && showCopiesAsAdds) {
callback.fileAdded(null, currentEntry.localAbspath, null, currentEntry.localAbspath, 0, revision, null, reposMimeType, null, -1, currentEntry.propChanges, new SVNProperties());
return;
}
boolean modified = currentEntry.file != null;
if (!modified && !useTextBase) {
modified = context.isTextModified(currentEntry.localAbspath, false);
}
File localFile;
if (modified) {
if (useTextBase) {
localFile = getPristineFile(currentEntry.localAbspath, false);
} else {
localFile = context.getTranslatedFile(currentEntry.localAbspath, currentEntry.localAbspath, true, false, false, false, false);
//TODO: cancellation?
}
} else {
reposFile = null;
localFile = null;
}
SVNProperties originalProps;
if (useTextBase) {
originalProps = pristineProps;
} else {
originalProps = context.getActualProps(currentEntry.localAbspath);
currentEntry.propChanges = computePropDiff(originalProps, reposProps);
}
if (localFile != null || !currentEntry.propChanges.isEmpty()) {
String originalMimeType = getPropMimeType(originalProps);
if (!currentEntry.propChanges.isEmpty() && !reverseOrder) {
reversePropChanges(originalProps, currentEntry.propChanges);
}
callback.fileChanged(null, currentEntry.localAbspath,
reverseOrder ? localFile : reposFile,
reverseOrder ? reposFile : localFile,
reverseOrder ? -1 : revision,
reverseOrder ? revision : -1,
reverseOrder ? originalMimeType : reposMimeType,
reverseOrder ? reposMimeType : originalMimeType,
currentEntry.propChanges,
originalProps);
return;
}
} finally {
//there're a lot of returns, but we should switch the entry
currentEntry = currentEntry.parent;
}
}
public void changeDirProperty(String name, SVNPropertyValue value) throws SVNException {
currentEntry.propChanges.put(name, value);
}
public void changeFileProperty(String path, String propertyName, SVNPropertyValue propertyValue) throws SVNException {
currentEntry.propChanges.put(propertyName, propertyValue);
}
public SVNCommitInfo closeEdit() throws SVNException {
if (!rootOpened) {
walkLocalNodesDiff(anchorAbspath, "", depth, null);
}
return null;
}
public void abortEdit() throws SVNException {
}
public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
return deltaProcessor.textDeltaChunk(diffWindow);
}
public void textDeltaEnd(String path) throws SVNException {
final String checksum = deltaProcessor.textDeltaEnd();
currentEntry.resultChecksum = new SvnChecksum(SvnChecksum.Kind.md5, checksum);
}
private File getPristineFile(File localAbspath, boolean useTextBase) throws SVNException {
SvnChecksum checksum;
if (!useTextBase) {
Structure<StructureFields.PristineInfo> pristineInfoStructure = db.readPristineInfo(localAbspath);
checksum = pristineInfoStructure.get(StructureFields.PristineInfo.checksum);
} else {
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbspath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.checksum);
checksum = baseInfo.checksum;
}
if (checksum != null) {
return db.getPristinePath(localAbspath, checksum);
}
return null;
}
private String getPropMimeType(SVNProperties baseProps) {
if (baseProps == null) {
return null;
}
return baseProps.getStringValue(SVNProperty.MIME_TYPE);
}
private void reportFileAdded(File localAbspath, String path) throws SVNException {
if (!context.matchesChangelist(localAbspath, changelists)) {
return;
}
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(localAbspath,
StructureFields.NodeInfo.revision,
StructureFields.NodeInfo.status);
long revision = nodeInfoStructure.lng(StructureFields.NodeInfo.revision);
ISVNWCDb.SVNWCDbStatus status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
if (status == ISVNWCDb.SVNWCDbStatus.Added) {
ISVNWCDb.WCDbAdditionInfo wcDbAdditionInfo = db.scanAddition(localAbspath, ISVNWCDb.WCDbAdditionInfo.AdditionInfoField.status);
status = wcDbAdditionInfo.status;
}
assert status != ISVNWCDb.SVNWCDbStatus.Deleted || useTextBase;
if (status == ISVNWCDb.SVNWCDbStatus.Copied || status == ISVNWCDb.SVNWCDbStatus.MovedHere) {
if (useTextBase) {
return;
}
fileDiff(localAbspath, path);
}
SVNProperties emptyProps = new SVNProperties();
SVNProperties wcProps;
if (useTextBase) {
wcProps = context.getPristineProps(localAbspath);
} else {
wcProps = context.getActualProps(localAbspath);
}
String mimeType = getPropMimeType(wcProps);
SVNProperties propchanges = computePropDiff(emptyProps, wcProps);
File sourceFile;
if (useTextBase) {
sourceFile = getPristineFile(localAbspath, false);
} else {
sourceFile = localAbspath;
}
File translatedFile = context.getTranslatedFile(sourceFile, localAbspath, true, false, false, false, false);//TODO; cancel
callback.fileAdded(null, localAbspath,
null, translatedFile,
0, revision,
null, mimeType,
null, -1,
propchanges, emptyProps);
}
private void reportDirectoryAdded(File localAbspath, String path, SVNDepth depth) throws SVNException {
SVNProperties emptyProps = new SVNProperties();
if (context.matchesChangelist(localAbspath, changelists)) {
SVNProperties wcProps;
if (useTextBase) {
wcProps = context.getPristineProps(localAbspath);
} else {
wcProps = context.getActualProps(localAbspath);
}
SVNProperties propChanges = computePropDiff(emptyProps, wcProps);
if (!propChanges.isEmpty()) {
callback.dirPropsChanged(null, localAbspath, true, propChanges, emptyProps);
}
}
Set<String> children = db.readChildren(localAbspath);
for (String name : children) {
checkCancelled();
final File childAbspath = new File(localAbspath, name);
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(childAbspath, StructureFields.NodeInfo.status, StructureFields.NodeInfo.kind);
ISVNWCDb.SVNWCDbStatus status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
ISVNWCDb.SVNWCDbKind kind = nodeInfoStructure.get(StructureFields.NodeInfo.kind);
if (status == ISVNWCDb.SVNWCDbStatus.NotPresent
|| status == ISVNWCDb.SVNWCDbStatus.Excluded
|| status == ISVNWCDb.SVNWCDbStatus.ServerExcluded) {
continue;
}
if (!useTextBase && status == ISVNWCDb.SVNWCDbStatus.Deleted) {
continue;
}
String childPath = SVNPathUtil.append(path, name);
switch (kind) {
case File:
case Symlink:
reportFileAdded(childAbspath, childPath);
break;
case Dir:
if (depth.compareTo(SVNDepth.FILES) > 0 || depth == SVNDepth.UNKNOWN) {
SVNDepth depthBelowHere = depth;
if (depthBelowHere == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
reportDirectoryAdded(childAbspath, childPath, depthBelowHere);
}
break;
default:
break;
}
}
}
private void fileDiff(File localAbspath, String path) throws SVNException {
boolean useBase = false;
assert !useTextBase;
if (!context.matchesChangelist(localAbspath, changelists)) {
return;
}
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(localAbspath,
StructureFields.NodeInfo.status,
StructureFields.NodeInfo.revision,
StructureFields.NodeInfo.haveBase);
ISVNWCDb.SVNWCDbStatus status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
long revision = nodeInfoStructure.lng(StructureFields.NodeInfo.revision);
boolean haveBase = nodeInfoStructure.is(StructureFields.NodeInfo.haveBase);
ISVNWCDb.SVNWCDbStatus baseStatus = null;
long revertBaseRevision = -1;
if (haveBase) {
ISVNWCDb.WCDbBaseInfo baseInfo = db.getBaseInfo(localAbspath, ISVNWCDb.WCDbBaseInfo.BaseInfoField.status, ISVNWCDb.WCDbBaseInfo.BaseInfoField.revision);
baseStatus = baseInfo.status;
revertBaseRevision = baseInfo.revision;
}
boolean replaced = ((status == ISVNWCDb.SVNWCDbStatus.Added) && haveBase && baseStatus != ISVNWCDb.SVNWCDbStatus.NotPresent);
File originalReposRelpath = null;
if (status == ISVNWCDb.SVNWCDbStatus.Added) {
ISVNWCDb.WCDbAdditionInfo wcDbAdditionInfo = db.scanAddition(localAbspath, ISVNWCDb.WCDbAdditionInfo.AdditionInfoField.status, ISVNWCDb.WCDbAdditionInfo.AdditionInfoField.originalReposRelPath);
status = wcDbAdditionInfo.status;
originalReposRelpath = wcDbAdditionInfo.originalReposRelPath;
}
if (replaced && ! (status == ISVNWCDb.SVNWCDbStatus.Copied || status == ISVNWCDb.SVNWCDbStatus.MovedHere)) {
useBase = true;
revision = revertBaseRevision;
}
File textBase = getPristineFile(localAbspath, useBase);
if ((!replaced && status == ISVNWCDb.SVNWCDbStatus.Deleted) || (replaced && !ignoreAncestry)) {
SVNProperties baseProps = context.getPristineProps(localAbspath);
String baseMimeType;
if (baseProps != null) {
baseMimeType = getPropMimeType(baseProps);
} else {
baseMimeType = null;
}
callback.fileDeleted(null, localAbspath, textBase, null, baseMimeType, null, baseProps);
if (! (replaced && !ignoreAncestry)) {
return;
}
}
File translatedFile = null;
if ((! replaced && status == ISVNWCDb.SVNWCDbStatus.Added) || (replaced && !ignoreAncestry) ||
((status == ISVNWCDb.SVNWCDbStatus.Copied || status == ISVNWCDb.SVNWCDbStatus.MovedHere) &&
(showCopiesAsAdds || useGitDiffFormat))) {
SVNProperties workingProps = context.getActualProps(localAbspath);
String workingMimeType = getPropMimeType(workingProps);
SVNProperties baseProps = new SVNProperties();
SVNProperties propChanges = computePropDiff(baseProps, workingProps);
translatedFile = context.getTranslatedFile(localAbspath, localAbspath, true, false, false, false, false);
callback.fileAdded(null, localAbspath,
(!showCopiesAsAdds && useGitDiffFormat && status != ISVNWCDb.SVNWCDbStatus.Added) ? textBase : null,
translatedFile,
0, revision,
null,
workingMimeType,
originalReposRelpath,
-1,
propChanges, baseProps);
} else {
boolean modified = context.isTextModified(localAbspath, false);
if (modified) {
translatedFile = context.getTranslatedFile(localAbspath, localAbspath, true, false, false, false, false);
}
SVNProperties baseProps;
if (replaced && ignoreAncestry) {
baseProps = db.getBaseProps(localAbspath);
} else {
assert (!replaced ||
status == ISVNWCDb.SVNWCDbStatus.Copied ||
status == ISVNWCDb.SVNWCDbStatus.MovedHere);
baseProps = context.getPristineProps(localAbspath);
if (baseProps == null) {
baseProps = new SVNProperties();
}
}
String baseMimeType = getPropMimeType(baseProps);
SVNProperties workingProps = context.getActualProps(localAbspath);
String workingMimeType = getPropMimeType(workingProps);
SVNProperties propChanges = computePropDiff(baseProps, workingProps);
if (modified || !propChanges.isEmpty()) {
callback.fileChanged(null, localAbspath,
modified ? textBase : null, translatedFile,
revision, -1,
baseMimeType, workingMimeType,
propChanges, baseProps);
}
}
}
private File getLocalAbspath(String path) {
return new File(anchorAbspath, path);
}
private void checkCancelled() throws SVNCancelException {
canceller.checkCancelled();
}
private SVNProperties applyPropsChanges(SVNProperties props, SVNProperties propChanges) {
SVNProperties result = new SVNProperties(props);
if (propChanges != null) {
for(Iterator names = propChanges.nameSet().iterator(); names.hasNext();) {
String name = (String) names.next();
SVNPropertyValue value = propChanges.getSVNPropertyValue(name);
if (value == null) {
result.remove(name);
} else {
result.put(name, value);
}
}
}
return result;
}
private static void reversePropChanges(SVNProperties base, SVNProperties diff) {
Collection<String> namesList = new ArrayList<String>(diff.nameSet());
for (Iterator names = namesList.iterator(); names.hasNext();) {
String name = (String) names.next();
SVNPropertyValue newValue = diff.getSVNPropertyValue(name);
SVNPropertyValue oldValue = base.getSVNPropertyValue(name);
if (oldValue == null && newValue != null) {
base.put(name, newValue);
diff.put(name, (SVNPropertyValue) null);
} else if (oldValue != null && newValue == null) {
base.put(name, (SVNPropertyValue) null);
diff.put(name, oldValue);
} else if (oldValue != null && newValue != null) {
base.put(name, newValue);
diff.put(name, oldValue);
}
}
}
private void walkLocalNodesDiff(File localAbspath, String path, SVNDepth depth, Set<String> compared) throws SVNException {
if (useTextBase) {
return;
}
boolean inAnchorNotTarget = path.length() == 0 && target.length() != 0;
if (context.matchesChangelist(localAbspath, changelists) && !inAnchorNotTarget && (compared == null|| !compared.contains(path))) {
boolean modified = context.isPropsModified(localAbspath);
if (modified) {
SVNWCContext.PropDiffs propDiffs = context.getPropDiffs(localAbspath);
SVNProperties baseProps = propDiffs.originalProps;
SVNProperties propChanges = propDiffs.propChanges;
callback.dirPropsChanged(null, localAbspath, false, propChanges, baseProps);
}
}
if (depth == SVNDepth.EMPTY && !inAnchorNotTarget) {
return;
}
Set<String> children = db.readChildren(localAbspath);
for (String name : children) {
checkCancelled();
if (inAnchorNotTarget && !name.equals(target)) {
continue;
}
File childAbspath = new File(localAbspath, name);
Structure<StructureFields.NodeInfo> nodeInfoStructure = db.readInfo(childAbspath, StructureFields.NodeInfo.status, StructureFields.NodeInfo.kind);
ISVNWCDb.SVNWCDbStatus status = nodeInfoStructure.get(StructureFields.NodeInfo.status);
ISVNWCDb.SVNWCDbKind kind = nodeInfoStructure.get(StructureFields.NodeInfo.kind);
if (status == ISVNWCDb.SVNWCDbStatus.NotPresent || status == ISVNWCDb.SVNWCDbStatus.Excluded || status == ISVNWCDb.SVNWCDbStatus.ServerExcluded) {
continue;
}
String childPath = SVNPathUtil.append(path, name);
if (compared != null && compared.contains(childPath)) {
continue;
}
switch (kind) {
case File:
case Symlink:
fileDiff(childAbspath, childPath);
break;
case Dir:
if (inAnchorNotTarget
|| (depth.compareTo(SVNDepth.FILES) > 0) || depth == SVNDepth.UNKNOWN) {
SVNDepth depthBelowHere = depth;
if (depthBelowHere == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
walkLocalNodesDiff(childAbspath, childPath, depthBelowHere, null);
}
break;
default:
break;
}
}
}
private File createTempFile(File tempDir) throws SVNException {
final File tempFile = SVNFileUtil.createUniqueFile(tempDir, "diff.", ".tmp", true);
tempFiles.add(tempFile);
return tempFile;
}
private void addToCompared(Entry entry, String path) {
if (entry.compared == null) {
currentEntry.compared = new HashSet<String>();
}
currentEntry.compared.add(path);
}
public void cleanup() {
for (File tempFile : tempFiles) {
try {
SVNFileUtil.deleteFile(tempFile);
} catch (SVNException ignore) {
}
}
}
public long getTargetRevision() {
return revision;
}
public static SVNProperties computePropDiff(SVNProperties props1, SVNProperties props2) {
SVNProperties propsDiff = new SVNProperties();
for (Iterator names = props2.nameSet().iterator(); names.hasNext();) {
String newPropName = (String) names.next();
if (props1.containsName(newPropName)) {
// changed.
SVNPropertyValue oldValue = props2.getSVNPropertyValue(newPropName);
SVNPropertyValue value = props1.getSVNPropertyValue(newPropName);
if (oldValue != null && !oldValue.equals(value)) {
propsDiff.put(newPropName, oldValue);
} else if (oldValue == null && value != null) {
propsDiff.put(newPropName, oldValue);
}
} else {
// added.
propsDiff.put(newPropName, props2.getSVNPropertyValue(newPropName));
}
}
for (Iterator names = props1.nameSet().iterator(); names.hasNext();) {
String oldPropName = (String) names.next();
if (!props2.containsName(oldPropName)) {
// deleted
propsDiff.put(oldPropName, (String) null);
}
}
return propsDiff;
}
private static class Entry {
private boolean isFile;
private Entry parent;
private String path;
private boolean added;
private SVNDepth depth;
private SVNProperties propChanges;
private File localAbspath;
private SvnChecksum baseChecksum;
private SvnChecksum resultChecksum;
private File file;
private Set<String> compared;
public Entry(boolean file, String path, Entry parent, boolean added, SVNDepth depth, File localAbspath) {
this.isFile = file;
this.path = path;
this.parent = parent;
this.added = added;
this.depth = depth;
this.localAbspath = localAbspath;
this.propChanges = new SVNProperties();
}
}
}