/*
* ====================================================================
* 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.wc;
import java.io.File;
import java.io.OutputStream;
import java.util.Map;
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.SVNNodeKind;
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.SVNDate;
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.admin.SVNTranslator;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class SVNExportEditor implements ISVNEditor {
private File myRoot;
private boolean myIsForce;
private String myEOLStyle;
private File myCurrentDirectory;
private File myCurrentFile;
private File myCurrentTmpFile;
private String myCurrentPath;
private Map<String,String> myExternals;
private SVNProperties myFileProperties;
private ISVNEventHandler myEventDispatcher;
private String myURL;
private String myRepositoryRootUrl;
private ISVNOptions myOptions;
private SVNDeltaProcessor myDeltaProcessor;
private boolean myIsExpandKeywords;
public SVNExportEditor(ISVNEventHandler eventDispatcher, String url,
File dstPath, boolean force, String eolStyle, boolean expandKeywords, String repositoryRootUrl, ISVNOptions options) {
myRoot = dstPath;
myIsForce = force;
myEOLStyle = eolStyle;
myExternals = new SVNHashMap();
myEventDispatcher = eventDispatcher;
myURL = url;
myDeltaProcessor = new SVNDeltaProcessor();
myOptions = options;
myIsExpandKeywords = expandKeywords;
myRepositoryRootUrl = repositoryRootUrl;
}
public Map<String,String> getCollectedExternals() {
return myExternals;
}
public void openRoot(long revision) throws SVNException {
// create root if missing or delete (if force).
addDir("", null, -1);
}
public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException {
myCurrentDirectory = new File(myRoot, path);
myCurrentPath = path;
SVNFileType dirType = SVNFileType.getType(myCurrentDirectory);
if (dirType == SVNFileType.FILE || dirType == SVNFileType.SYMLINK) {
// export is obstructed.
if (!myIsForce) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "''{0}'' exists and is not a directory", myCurrentDirectory);
SVNErrorManager.error(err, SVNLogType.WC);
} else {
SVNFileUtil.deleteAll(myCurrentDirectory, myEventDispatcher);
}
} else if (dirType == SVNFileType.DIRECTORY && !myIsForce) {
if (!"".equals(path)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "''{0}'' already exists", myCurrentDirectory);
SVNErrorManager.error(err, SVNLogType.WC);
}
File[] children = SVNFileListUtil.listFiles(myCurrentDirectory);
if (children != null && children.length > 0) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "''{0}'' already exists", myCurrentDirectory);
SVNErrorManager.error(err, SVNLogType.WC);
}
} else if (dirType == SVNFileType.NONE) {
if (!myCurrentDirectory.mkdirs()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "Cannot create directory ''{0}''", myCurrentDirectory);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
myEventDispatcher.handleEvent(SVNEventFactory.createSVNEvent(myCurrentDirectory, SVNNodeKind.DIR, null, SVNRepository.INVALID_REVISION, SVNEventAction.UPDATE_ADD, null, null, null), ISVNEventHandler.UNKNOWN);
}
public void changeDirProperty(String name, SVNPropertyValue value)
throws SVNException {
if (SVNProperty.EXTERNALS.equals(name) && value != null) {
myExternals.put(myCurrentPath, value.getString());
}
}
public void closeDir() throws SVNException {
myCurrentDirectory = myCurrentDirectory.getParentFile();
myCurrentPath = SVNPathUtil.removeTail(myCurrentPath);
}
public void addFile(String path, String copyFromPath, long copyFromRevision)
throws SVNException {
File file = new File(myRoot, path);
if (!myIsForce && file.exists()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "File ''{0}'' already exists", file);
SVNErrorManager.error(err, SVNLogType.WC);
}
myCurrentFile = file;
myFileProperties = new SVNProperties();
myChecksum = null;
}
public void changeFileProperty(String commitPath, String name, SVNPropertyValue value)
throws SVNException {
myFileProperties.put(name, value);
}
public void applyTextDelta(String commitPath, String baseChecksum) throws SVNException {
String name = SVNPathUtil.tail(commitPath);
myCurrentTmpFile = SVNFileUtil.createUniqueFile(myCurrentDirectory, name, ".tmp", false);
myDeltaProcessor.applyTextDelta((File)null, myCurrentTmpFile, true);
}
public OutputStream textDeltaChunk(String commitPath, SVNDiffWindow diffWindow) throws SVNException {
return myDeltaProcessor.textDeltaChunk(diffWindow);
}
private String myChecksum;
public void textDeltaEnd(String commitPath) throws SVNException {
myChecksum = myDeltaProcessor.textDeltaEnd();
}
public void closeFile(String commitPath, String textChecksum) throws SVNException {
if (textChecksum == null) {
textChecksum = myFileProperties.getStringValue(SVNProperty.CHECKSUM);
}
if (myIsForce) {
myCurrentFile.delete();
}
String realChecksum = myChecksum != null ? myChecksum : SVNFileUtil.computeChecksum(myCurrentTmpFile);
myChecksum = null;
if (textChecksum != null && !textChecksum.equals(realChecksum)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH, "Checksum mismatch for ''{0}''; expected: ''{1}'', actual: ''{2}''",
new Object[] {myCurrentFile, textChecksum, realChecksum});
SVNErrorManager.error(err, SVNLogType.WC);
}
// retranslate.
if (!myIsExpandKeywords) {
myFileProperties.put(SVNProperty.MIME_TYPE, "application/octet-stream");
}
try {
String date = myFileProperties.getStringValue(SVNProperty.COMMITTED_DATE);
boolean special = myFileProperties.getStringValue(SVNProperty.SPECIAL) != null;
String mimeType = myFileProperties.getStringValue(SVNProperty.MIME_TYPE);
boolean binary = SVNProperty.isBinaryMimeType(mimeType);
String keywords = myFileProperties.getStringValue(SVNProperty.KEYWORDS);
Map keywordsMap = null;
if (keywords != null) {
String url = SVNPathUtil.append(myURL, SVNEncodingUtil.uriEncode(myCurrentPath));
url = SVNPathUtil.append(url, SVNEncodingUtil.uriEncode(myCurrentFile.getName()));
String author = myFileProperties.getStringValue(SVNProperty.LAST_AUTHOR);
String revStr = myFileProperties.getStringValue(SVNProperty.COMMITTED_REVISION);
keywordsMap = SVNTranslator.computeKeywords(keywords, url, myRepositoryRootUrl, author, date, revStr, myOptions);
}
String charset = SVNTranslator.getCharset(myFileProperties.getStringValue(SVNProperty.CHARSET), mimeType, myCurrentFile.getPath(), myOptions);
byte[] eolBytes = null;
if (SVNProperty.EOL_STYLE_NATIVE.equals(myFileProperties.getStringValue(SVNProperty.EOL_STYLE))) {
eolBytes = SVNTranslator.getEOL(myEOLStyle != null ? myEOLStyle : myFileProperties.getStringValue(SVNProperty.EOL_STYLE), myOptions);
} else if (myFileProperties.containsName(SVNProperty.EOL_STYLE)) {
eolBytes = SVNTranslator.getEOL(myFileProperties.getStringValue(SVNProperty.EOL_STYLE), myOptions);
}
if (binary) {
// no translation unless 'special'.
charset = null;
eolBytes = null;
keywordsMap = null;
}
if (charset != null || eolBytes != null || (keywordsMap != null && !keywordsMap.isEmpty()) || special) {
SVNTranslator.translate(myCurrentTmpFile, myCurrentFile, charset, eolBytes, keywordsMap, special, true);
} else {
SVNFileUtil.rename(myCurrentTmpFile, myCurrentFile);
}
boolean executable = myFileProperties.getStringValue(SVNProperty.EXECUTABLE) != null;
if (executable) {
SVNFileUtil.setExecutable(myCurrentFile, true);
}
if (!special && date != null) {
SVNFileUtil.setLastModified(myCurrentFile, SVNDate.parseDate(date).getTime());
}
myEventDispatcher.handleEvent(SVNEventFactory.createSVNEvent(myCurrentFile, SVNNodeKind.FILE, null, SVNRepository.INVALID_REVISION, SVNEventAction.UPDATE_ADD, null, null, null), ISVNEventHandler.UNKNOWN);
} finally {
myCurrentTmpFile.delete();
}
}
public SVNCommitInfo closeEdit() throws SVNException {
return null;
}
public void targetRevision(long revision) throws SVNException {
}
public void deleteEntry(String path, long revision) throws SVNException {
}
public void absentDir(String path) throws SVNException {
}
public void absentFile(String path) throws SVNException {
}
public void openDir(String path, long revision) throws SVNException {
}
public void openFile(String path, long revision) throws SVNException {
}
public void abortEdit() throws SVNException {
}
}