/*
* ====================================================================
* 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.internal.io.svn;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @author TMate Software Ltd.
* @version 1.3
*/
class SVNCommitEditor implements ISVNEditor {
private SVNConnection myConnection;
private SVNRepositoryImpl myRepository;
private ISVNCommitCallback myCloseCallback;
private Stack myDirsStack;
private int myNextToken;
private Map myFilesToTokens;
public interface ISVNCommitCallback {
public void run(SVNException error);
}
public SVNCommitEditor(SVNRepositoryImpl location, SVNConnection connection, ISVNCommitCallback closeCallback) {
myRepository = location;
myConnection = connection;
myCloseCallback = closeCallback;
myDirsStack = new Stack();
myNextToken = 0;
}
/* do nothing */
public void targetRevision(long revision) throws SVNException {
}
public void absentDir(String path) throws SVNException {
}
public void absentFile(String path) throws SVNException {
}
public void openRoot(long revision) throws SVNException {
DirBaton rootBaton = new DirBaton(myNextToken++);
myConnection.write("(w((n)s))", new Object[]{"open-root",
getRevisionObject(revision), rootBaton.getToken()});
myDirsStack.push(rootBaton);
}
public void deleteEntry(String path, long revision) throws SVNException {
DirBaton parentBaton = (DirBaton) myDirsStack.peek();
myConnection.write("(w(s(n)s))", new Object[]{"delete-entry", path,
getRevisionObject(revision), parentBaton.getToken()});
}
public void addDir(String path, String copyFromPath, long copyFromRevision)
throws SVNException {
DirBaton parentBaton = (DirBaton) myDirsStack.peek();
DirBaton dirBaton = new DirBaton(myNextToken++);
if (copyFromPath != null) {
String rootURL = myRepository.getRepositoryRoot(false).toString();
copyFromPath = SVNPathUtil.append(rootURL, SVNEncodingUtil.uriEncode(myRepository.getRepositoryPath(copyFromPath)));
myConnection.write("(w(sss(sn)))", new Object[]{"add-dir", path,
parentBaton.getToken(), dirBaton.getToken(), copyFromPath,
getRevisionObject(copyFromRevision)});
} else {
myConnection.write("(w(sss()))", new Object[]{"add-dir", path,
parentBaton.getToken(), dirBaton.getToken()});
}
myDirsStack.push(dirBaton);
}
public void openDir(String path, long revision) throws SVNException {
DirBaton parentBaton = (DirBaton) myDirsStack.peek();
DirBaton dirBaton = new DirBaton(myNextToken++);
myConnection.write("(w(sss(n)))", new Object[]{"open-dir", path,
parentBaton.getToken(), dirBaton.getToken(), getRevisionObject(revision)});
myDirsStack.push(dirBaton);
}
public void changeDirProperty(String name, SVNPropertyValue value)
throws SVNException {
DirBaton dirBaton = (DirBaton) myDirsStack.peek();
byte[] bytes = SVNPropertyValue.getPropertyAsBytes(value);
myConnection.write("(w(ss(b)))", new Object[]{"change-dir-prop",
dirBaton.getToken(), name, bytes});
}
public void closeDir() throws SVNException {
DirBaton dirBaton = (DirBaton) myDirsStack.pop();
myConnection.write("(w(s))",
new Object[]{"close-dir", dirBaton.getToken()});
}
public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException {
DirBaton parentBaton = (DirBaton) myDirsStack.peek();
String fileToken = "c" + myNextToken++;
if (copyFromPath != null) {
String host = myRepository.getRepositoryRoot(false).toString();
copyFromPath = SVNPathUtil.append(host, SVNEncodingUtil.uriEncode(myRepository.getRepositoryPath(copyFromPath)));
myConnection.write("(w(sss(sn)))", new Object[]{"add-file", path,
parentBaton.getToken(), fileToken, copyFromPath,
getRevisionObject(copyFromRevision)});
} else {
myConnection.write("(w(sss()))", new Object[]{"add-file", path,
parentBaton.getToken(), fileToken});
}
if (myFilesToTokens == null) {
myFilesToTokens = new SVNHashMap();
}
myFilesToTokens.put(path, fileToken);
}
public void openFile(String path, long revision) throws SVNException {
DirBaton parentBaton = (DirBaton) myDirsStack.peek();
String fileToken = "c" + myNextToken++;
myConnection.write("(w(sss(n)))", new Object[]{"open-file", path,
parentBaton.getToken(), fileToken, getRevisionObject(revision)});
if (myFilesToTokens == null) {
myFilesToTokens = new SVNHashMap();
}
myFilesToTokens.put(path, fileToken);
}
public void applyTextDelta(String path, String baseChecksum) throws SVNException {
String fileToken = (String) myFilesToTokens.get(path);
myDiffWindowCount = 0;
myConnection.write("(w(s(s)))", new Object[]{"apply-textdelta", fileToken, baseChecksum});
}
private int myDiffWindowCount = 0;
private boolean myIsAborted;
public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
String fileToken = (String) myFilesToTokens.get(path);
try {
diffWindow.writeTo(myConnection.getDeltaStream(fileToken), myDiffWindowCount == 0, myConnection.isSVNDiff1());
myDiffWindowCount++;
return SVNFileUtil.DUMMY_OUT;
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_SVN_IO_ERROR, e.getMessage()), e, SVNLogType.NETWORK);
}
return null;
}
public void textDeltaEnd(String path) throws SVNException {
String fileToken = (String) myFilesToTokens.get(path);
myDiffWindowCount = 0;
myConnection.write("(w(s))", new Object[]{"textdelta-end", fileToken});
}
public void changeFileProperty(String path, String name, SVNPropertyValue value) throws SVNException {
String fileToken = (String) myFilesToTokens.get(path);
byte[] bytes = SVNPropertyValue.getPropertyAsBytes(value);
myConnection.write("(w(ss(b)))", new Object[]{"change-file-prop", fileToken, name, bytes});
}
public void closeFile(String path, String textChecksum) throws SVNException {
String fileToken = (String) myFilesToTokens.remove(path);
myDiffWindowCount = 0;
myConnection.write("(w(s(s)))", new Object[]{"close-file", fileToken, textChecksum});
}
public SVNCommitInfo closeEdit() throws SVNException {
SVNException e = null;
try {
myConnection.write("(w())", new Object[]{"close-edit"});
myConnection.read("", null, true);
myRepository.authenticate();
List items = myConnection.readTuple("r(?s)(?s)?(?s)", true);
long revision = SVNReader.getLong(items, 0);
Date date = SVNReader.getDate(items, 1);
String author = SVNReader.getString(items, 2);
String errorMessage = SVNReader.getString(items, 3);
SVNErrorMessage err = errorMessage == null || errorMessage.length() == 0 ? null : SVNErrorMessage.create(SVNErrorCode.REPOS_POST_COMMIT_HOOK_FAILED, errorMessage, SVNErrorMessage.TYPE_WARNING);
return new SVNCommitInfo(revision, author, date, err);
} catch (SVNException exception) {
e = exception;
try {
myConnection.write("(w())", new Object[]{"abort-edit"});
} catch (SVNException e1) {
}
throw exception;
} finally {
myCloseCallback.run(e);
myCloseCallback = null;
}
}
public void abortEdit() throws SVNException {
if (myIsAborted || myCloseCallback == null) {
return;
}
myIsAborted = true;
SVNException error = null;
try {
myConnection.write("(w())", new Object[]{"abort-edit"});
myConnection.read("", null, true);
} catch (SVNException e) {
error = e;
throw e;
} finally {
myCloseCallback.run(error);
myCloseCallback = null;
}
}
private static Long getRevisionObject(long rev) {
return rev >= 0 ? new Long(rev) : null;
}
private final static class DirBaton {
private String myToken;
public DirBaton(int token) {
myToken = "d" + token;
}
public String getToken() {
return myToken;
}
}
}