package org.tmatesoft.svn.test;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.*;
public class CommitBuilder {
private final SVNURL url;
private String commitMessage;
private final Map<String, byte[]> filesToAdd;
private final Map<String, byte[]> filesToChange;
private final Map<String, SVNProperties> filesToProperties;
private final Map<String, SVNProperties> directoriesToProperties;
private final Map<String, String> filesToCopyFromPath;
private final Map<String, Long> filesToCopyFromRevision;
private final Map<String, String> directoriesToCopyFromPath;
private final Map<String, Long> directoriesToCopyFromRevision;
private final Set<String> directoriesToAdd;
private final Set<String> entriesToDelete;
private ISVNAuthenticationManager authenticationManager;
public CommitBuilder(SVNURL url) {
this.filesToAdd = new HashMap<String, byte[]>();
this.filesToChange = new HashMap<String, byte[]>();
this.filesToCopyFromPath = new HashMap<String, String>();
this.filesToCopyFromRevision = new HashMap<String, Long>();
this.directoriesToCopyFromPath = new HashMap<String, String>();
this.directoriesToCopyFromRevision = new HashMap<String, Long>();
this.directoriesToAdd = new HashSet<String>();
this.entriesToDelete = new HashSet<String>();
this.filesToProperties = new HashMap<String, SVNProperties>();
this.directoriesToProperties = new HashMap<String, SVNProperties>();
this.url = url;
setCommitMessage("");
}
public void setFileProperty(String path, String propertyName, SVNPropertyValue propertyValue) {
SVNProperties properties;
if (!filesToProperties.containsKey(path)) {
properties = new SVNProperties();
filesToProperties.put(path, properties);
} else {
properties = filesToProperties.get(path);
}
properties.put(propertyName, propertyValue);
}
public void setDirectoryProperty(String path, String propertyName, SVNPropertyValue propertyValue) {
SVNProperties properties;
if (!directoriesToProperties.containsKey(path)) {
properties = new SVNProperties();
directoriesToProperties.put(path, properties);
} else {
properties = directoriesToProperties.get(path);
}
properties.put(propertyName, propertyValue);
}
public void setCommitMessage(String commitMessage) {
this.commitMessage = commitMessage;
}
public void addDirectory(String directory) {
directoriesToAdd.add(directory);
}
public CommitBuilder addFile(String path) {
return addFile(path, new byte[0]);
}
public CommitBuilder addFile(String path, byte[] contents) {
filesToAdd.put(path, contents);
return this;
}
public CommitBuilder changeFile(String path, byte[] contents) {
filesToChange.put(path, contents);
return this;
}
public CommitBuilder addDirectoryByCopying(String path, String copyFromPath) {
return addDirectoryByCopying(path, copyFromPath, -1); //the latest revision is used in this case
}
public void addFileByCopying(String path, String copyFromPath) {
addFileByCopying(path, copyFromPath, -1);
}
public void addFileByCopying(String path, String copyFromPath, long copyFromRevision) {
filesToCopyFromPath.put(path, copyFromPath);
filesToCopyFromRevision.put(path, copyFromRevision);
}
public CommitBuilder addDirectoryByCopying(String path, String copyFromPath, long copyFromRevision) {
directoriesToCopyFromPath.put(path, copyFromPath);
directoriesToCopyFromRevision.put(path, copyFromRevision);
return this;
}
public void replaceFileByCopying(String path, String copyFromPath) {
delete(path);
addFileByCopying(path, copyFromPath);
}
public void replaceDirectoryByCopying(String path, String copyFromPath) {
delete(path);
addDirectoryByCopying(path, copyFromPath);
}
public void replaceFileByCopying(String path, String copyFromPath, long copyFromRevision) {
delete(path);
addFileByCopying(path, copyFromPath, copyFromRevision);
}
public void replaceDirectoryByCopying(String path, String copyFromPath, long copyFromRevision) {
delete(path);
addDirectoryByCopying(path, copyFromPath, copyFromRevision);
}
public SVNCommitInfo commit() throws SVNException {
final SortedSet<String> directoriesToVisit = getDirectoriesToVisit();
final SVNRepository svnRepository = createSvnRepository();
final long latestRevision = svnRepository.getLatestRevision();
final ISVNEditor commitEditor = svnRepository.getCommitEditor(commitMessage, null);
commitEditor.openRoot(-1);
String currentDirectory = "";
for (String directory : directoriesToVisit) {
if (directory.length() == 0) {
continue;
}
closeUntilCommonAncestor(commitEditor, currentDirectory, directory);
openOrAddDir(commitEditor, directory, latestRevision);
setDirProperties(commitEditor, directory);
currentDirectory = directory;
addChildrensFiles(commitEditor, directory, latestRevision);
deleteEntries(commitEditor, directory);
}
closeUntilCommonAncestor(commitEditor, currentDirectory, "");
currentDirectory = "";
setDirProperties(commitEditor, "");
addChildrensFiles(commitEditor, "", latestRevision);
deleteEntries(commitEditor, "");
commitEditor.closeDir();
return commitEditor.closeEdit();
}
private void setDirProperties(ISVNEditor commitEditor, String directory) throws SVNException {
SVNProperties properties = directoriesToProperties.get(directory);
if (properties == null) {
return;
}
for (String propertyName : properties.nameSet()) {
final SVNPropertyValue propertyValue = properties.getSVNPropertyValue(propertyName);
commitEditor.changeDirProperty(propertyName, propertyValue);
}
}
private void setFileProperties(ISVNEditor commitEditor, String file) throws SVNException {
SVNProperties properties = filesToProperties.get(file);
if (properties == null) {
return;
}
for (String propertyName : properties.nameSet()) {
final SVNPropertyValue propertyValue = properties.getSVNPropertyValue(propertyName);
commitEditor.changeFileProperty(file, propertyName, propertyValue);
}
}
private void addChildrensFiles(ISVNEditor commitEditor, String directory, long latestRevision) throws SVNException {
for (String file : filesToAdd.keySet()) {
String parent = getParent(file);
if (parent == null) {
parent = "";
}
if (directory.equals(parent)) {
maybeDelete(commitEditor, file);
addFile(commitEditor, file, filesToAdd.get(file), latestRevision);
}
}
for (String file : filesToCopyFromPath.keySet()) {
String parent = getParent(file);
if (parent == null) {
parent = "";
}
if (directory.equals(parent)) {
maybeDelete(commitEditor, file);
addFileByCopying(commitEditor, file, filesToCopyFromPath.get(file), filesToCopyFromRevision.get(file), latestRevision);
}
}
for (String file : filesToChange.keySet()) {
String parent = getParent(file);
if (parent == null) {
parent = "";
}
if (directory.equals(parent)) {
changeFile(commitEditor, file, filesToChange.get(file));
}
}
for (String file : filesToProperties.keySet()) {
String parent = getParent(file);
if (parent == null) {
parent = "";
}
if (directory.equals(parent)) {
commitEditor.openFile(file, -1);
setFileProperties(commitEditor, file);
commitEditor.closeFile(file, null);
}
}
}
private void deleteEntries(ISVNEditor commitEditor, String directory) throws SVNException {
for (String path : entriesToDelete) {
String parent = getParent(path);
if (parent == null) {
parent = "";
}
if (directory.equals(parent)) {
if (!filesToAdd.containsKey(path) && !filesToCopyFromPath.containsKey(path) && !directoriesToAdd.contains(path) && !directoriesToCopyFromPath.containsKey(path)) {
commitEditor.deleteEntry(path, -1);
}
}
}
}
private void maybeDelete(ISVNEditor commitEditor, String file) throws SVNException {
for (Iterator<String> iterator = entriesToDelete.iterator(); iterator.hasNext(); ) {
String path = iterator.next();
if (file.equals(path)) {
commitEditor.deleteEntry(file, -1);
iterator.remove();
break;
}
}
}
private void addFileByCopying(ISVNEditor commitEditor, String file, String copySource, Long copyRevisionLong, long latestRevision) throws SVNException {
final long copyRevisionSpecified = copyRevisionLong == null ? -1 : copyRevisionLong;
final long copyRevision = copyRevisionSpecified == -1 ? latestRevision : copyRevisionSpecified;
commitEditor.addFile(file, copySource, copyRevision);
final byte[] originalContents = getOriginalContents(copySource, copyRevision);
final String checksum = TestUtil.md5(originalContents);
commitEditor.closeFile(file, checksum);
}
private void addFile(ISVNEditor commitEditor, String file, byte[] contents, long latestRevision) throws SVNException {
final SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
commitEditor.addFile(file, null, -1);
setFileProperties(commitEditor, file);
commitEditor.applyTextDelta(file, null);
final String checksum = deltaGenerator.sendDelta(file, new ByteArrayInputStream(contents), commitEditor, true);
commitEditor.closeFile(file, checksum);
}
private void changeFile(ISVNEditor commitEditor, String file, byte[] newContents) throws SVNException {
final SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
final byte[] originalContents = getOriginalContents(file, -1);
if (newContents == null) {
newContents = originalContents;
}
commitEditor.openFile(file, -1);
setFileProperties(commitEditor, file);
commitEditor.applyTextDelta(file, TestUtil.md5(originalContents));
final String checksum = deltaGenerator.sendDelta(file,
new ByteArrayInputStream(originalContents), 0, new ByteArrayInputStream(newContents),
commitEditor, true);
commitEditor.closeFile(file, checksum);
}
private byte[] getOriginalContents(String file, long revision) throws SVNException {
final SVNRepository svnRepository = createSvnRepository();
try {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
svnRepository.getFile(file, revision, null, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} finally {
svnRepository.closeSession();
}
}
private void closeUntilCommonAncestor(ISVNEditor commitEditor, String currentDirectory, String directory) throws SVNException {
final String commonPathAncestor = getCommonPathAncestor(currentDirectory, directory);
while (currentDirectory != null && !currentDirectory.equals(commonPathAncestor)) {
commitEditor.closeDir();
currentDirectory = getParent(currentDirectory);
}
}
private String getCommonPathAncestor(String directory1, String directory2) {
if (directory1 == null || directory1.length() == 0) {
return "";
}
if (directory2 == null || directory2.length() == 0) {
return "";
}
return SVNPathUtil.getCommonPathAncestor(directory1, directory2);
}
private void openOrAddDir(ISVNEditor commitEditor, String directory, long latestRevision) throws SVNException {
boolean exists = existsDirectory(directory);
//process replacement
for (Iterator<String> iterator = entriesToDelete.iterator(); iterator.hasNext(); ) {
final String path = iterator.next();
if (directory.equals(path)) {
commitEditor.deleteEntry(path, -1);
iterator.remove();
exists = false;
break;
}
}
if (exists) {
if (!directoriesToAdd.contains(directory)) {
commitEditor.openDir(directory, -1);
} else {
commitEditor.addDir(directory, null, -1);
}
} else {
final String copySource = directoriesToCopyFromPath.get(directory);
final Long copyRevisionLong = directoriesToCopyFromRevision.get(directory);
final long copyRevisionSpecified = copyRevisionLong == null ? -1 : copyRevisionLong;
final long copyRevision = copyRevisionSpecified == -1 ? latestRevision : copyRevisionSpecified;
if (copySource == null) {
commitEditor.addDir(directory, null, -1);
} else {
commitEditor.addDir(directory, copySource, copyRevision);
}
}
}
private boolean existsDirectory(String directory) throws SVNException {
final SVNRepository svnRepository = createSvnRepository();
try {
final SVNNodeKind nodeKind = svnRepository.checkPath(directory, SVNRepository.INVALID_REVISION);
return nodeKind == SVNNodeKind.DIR;
} finally {
svnRepository.closeSession();
}
}
private SortedSet<String> getDirectoriesToVisit() {
final SortedSet<String> directoriesToVisit = new TreeSet<String>();
for (String directory : directoriesToAdd) {
addDirectoryToVisit(directory, directoriesToVisit);
}
for (String path: entriesToDelete) {
String directory = getParent(path);
if (directory != null) {
addDirectoryToVisit(directory, directoriesToVisit);
}
}
for (String directory : directoriesToProperties.keySet()) {
addDirectoryToVisit(directory, directoriesToVisit);
}
for (String directoryToAdd : directoriesToAdd) {
addDirectoryToVisit(directoryToAdd, directoriesToVisit);
}
addFilesParents(directoriesToVisit, filesToAdd.keySet());
addFilesParents(directoriesToVisit, filesToChange.keySet());
addFilesParents(directoriesToVisit, filesToProperties.keySet());
addFilesParents(directoriesToVisit, filesToCopyFromPath.keySet());
for (String directoryToCopy : directoriesToCopyFromPath.keySet()) {
addDirectoryToVisit(directoryToCopy, directoriesToVisit);
}
return directoriesToVisit;
}
private void addFilesParents(SortedSet<String> directoriesToVisit, Set<String> files) {
for (String file : files) {
final String directory = getParent(file);
if (directory != null) {
addDirectoryToVisit(directory, directoriesToVisit);
}
}
}
private void addDirectoryToVisit(String directory, SortedSet<String> directoriesToVisit) {
do {
directoriesToVisit.add(directory);
directory = getParent(directory);
} while (directory != null);
}
private String getParent(String file) {
if ("".equals(file)) {
return null;
}
String parent = SVNPathUtil.removeTail(file);
if ("".equals(parent)) {
return null;
}
return parent;
}
private SVNRepository createSvnRepository() throws SVNException {
final SVNRepository svnRepository = SVNRepositoryFactory.create(url);
svnRepository.setAuthenticationManager(authenticationManager);
return svnRepository;
}
public void delete(String path) {
entriesToDelete.add(path);
}
public void setAuthenticationManager(ISVNAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
}