/* * ==================================================================== * 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.fs; import java.io.InputStream; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; 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.SVNPropertyValue; import org.tmatesoft.svn.core.internal.delta.SVNDeltaCombiner; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.ISVNCommitPathHandler; 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.SVNDeltaGenerator; import org.tmatesoft.svn.util.SVNLogType; /** * @author TMate Software Ltd. * @version 1.3 */ public class FSReplayPathHandler implements ISVNCommitPathHandler { private FSRoot myRoot; private FSRoot myCompareRoot; private Map myChangedPaths; private String myBasePath; private long myLowRevision; private LinkedList myCopies; private FSFS myOwner; private SVNDeltaGenerator myDeltaGenerator; private SVNDeltaCombiner myDeltaCombiner; public FSReplayPathHandler(FSFS owner, FSRoot root, FSRoot compareRoot, Map changedPaths, String basePath, long lowRevision) { myRoot = root; myCompareRoot = compareRoot; myChangedPaths = changedPaths; myBasePath = basePath; myLowRevision = lowRevision; myCopies = new LinkedList(); myOwner = owner; myDeltaGenerator = new SVNDeltaGenerator(); myDeltaCombiner = new SVNDeltaCombiner(); } public boolean handleCommitPath(String path, ISVNEditor editor) throws SVNException { String absPath = !path.startsWith("/") ? "/" + path : path; while (myCopies.size() > 0) { CopyInfo info = (CopyInfo) myCopies.getLast(); if (SVNPathUtil.isAncestor(info.myPath, path)) { break; } myCopies.removeLast(); } boolean isAdd = false; boolean isDelete = false; FSPathChange change = (FSPathChange) myChangedPaths.get(path); if (change == null) { return false; } else if (change.getChangeKind() == FSPathChangeKind.FS_PATH_CHANGE_ADD) { isAdd = true; } else if (change.getChangeKind() == FSPathChangeKind.FS_PATH_CHANGE_DELETE) { isDelete = true; } else if (change.getChangeKind() == FSPathChangeKind.FS_PATH_CHANGE_REPLACE) { isAdd = true; isDelete = true; } boolean closeDir = false; if (isDelete) { editor.deleteEntry(path, -1); } SVNNodeKind kind = null; if (!isDelete || isAdd) { kind = myRoot.checkNodeKind(absPath); if (kind != SVNNodeKind.DIR && kind != SVNNodeKind.FILE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "Filesystem path ''{0}'' is neither a file nor a directory", path); SVNErrorManager.error(err, SVNLogType.FSFS); } } String copyFromPath = null; String realCopyFromPath = null; FSRoot srcRoot = myCompareRoot; String srcPath = srcRoot != null ? absPath : null; boolean closeFile = false; if (isAdd) { FSRoot copyFromRoot = null; FSRevisionNode copyfromNode = myRoot.getRevisionNode(absPath); copyFromPath = copyfromNode.getCopyFromPath(); long copyFromRevision = copyfromNode.getCopyFromRevision(); if (copyFromPath != null && FSRepository.isValidRevision(copyFromRevision)) { copyFromRoot = myOwner.createRevisionRoot(copyFromRevision); } realCopyFromPath = copyFromPath; if (copyFromPath != null) { String relCopyFromPath = copyFromPath.substring(1); if (!SVNPathUtil.isWithinBasePath(myBasePath, relCopyFromPath) || myLowRevision > copyFromRevision) { copyFromPath = null; copyFromRevision = -1; } } if (kind == SVNNodeKind.DIR) { if (realCopyFromPath != null && copyFromPath == null) { addSubdirectory(copyFromRoot, myRoot, editor, realCopyFromPath, path); } else { editor.addDir(path, copyFromPath, copyFromRevision); } closeDir = true; } else { editor.addFile(path, copyFromPath, copyFromRevision); closeFile = true; } if (copyFromPath != null) { if (kind == SVNNodeKind.DIR) { CopyInfo info = new CopyInfo(path, copyFromPath, copyFromRevision); myCopies.addLast(info); } srcRoot = copyFromRoot; srcPath = copyFromPath; } else { if (kind == SVNNodeKind.DIR && myCopies.size() > 0) { CopyInfo info = new CopyInfo(path, null, -1); myCopies.addLast(info); } srcRoot = null; srcPath = null; } } else if (!isDelete) { if (kind == SVNNodeKind.DIR) { if ("".equals(path)) { editor.openRoot(-1); } else { editor.openDir(path, -1); } closeDir = true; } else { editor.openFile(path, -1); closeFile = true; } if (myCopies.size() > 0) { CopyInfo info = (CopyInfo) myCopies.getLast(); if (info.myCopyFromPath != null) { srcRoot = myOwner.createRevisionRoot(info.myCopyFromRevision); srcPath = SVNPathUtil.append(info.myCopyFromPath, SVNPathUtil.getPathAsChild(info.myPath, path)); } else { srcRoot = null; srcPath = null; } } } if (!isDelete || isAdd) { if (change.arePropertiesModified()) { if (myCompareRoot != null) { SVNProperties oldProps = null; if (srcRoot != null) { FSRevisionNode srcNode = srcRoot.getRevisionNode(srcPath); oldProps = srcNode.getProperties(myOwner); } FSRevisionNode node = myRoot.getRevisionNode(absPath); SVNProperties newProps = node.getProperties(myOwner); SVNProperties propDiff = FSRepositoryUtil.getPropsDiffs(oldProps, newProps); for (Iterator propNames = propDiff.nameSet().iterator(); propNames.hasNext();) { String propName = (String) propNames.next(); SVNPropertyValue propValue = propDiff.getSVNPropertyValue(propName); if (kind == SVNNodeKind.DIR) { editor.changeDirProperty(propName, propValue); } else if (kind == SVNNodeKind.FILE) { editor.changeFileProperty(path, propName, propValue); } } } else { if (kind == SVNNodeKind.DIR) { editor.changeDirProperty("", null); } else if (kind == SVNNodeKind.FILE) { editor.changeFileProperty(path, "", null); } } } if (kind == SVNNodeKind.FILE && (change.isTextModified() || (realCopyFromPath != null && copyFromPath == null))) { String checksum = null; if (myCompareRoot != null && srcRoot != null && srcPath != null) { FSRevisionNode node = srcRoot.getRevisionNode(srcPath); checksum = node.getFileMD5Checksum(); } editor.applyTextDelta(path, checksum); if (myCompareRoot != null) { InputStream sourceStream = null; InputStream targetStream = null; try { if (srcRoot != null && srcPath != null) { sourceStream = srcRoot.getFileStreamForPath(myDeltaCombiner, srcPath); } else { sourceStream = SVNFileUtil.DUMMY_IN; } targetStream = myRoot.getFileStreamForPath(myDeltaCombiner, absPath); myDeltaGenerator.sendDelta(path, sourceStream, 0, targetStream, editor, false); } finally { SVNFileUtil.closeFile(sourceStream); SVNFileUtil.closeFile(targetStream); } } else { editor.textDeltaEnd(path); } } } if (closeFile) { FSRevisionNode node = myRoot.getRevisionNode(absPath); editor.closeFile(path, node.getFileMD5Checksum()); } return closeDir; } private void addSubdirectory(FSRoot srcRoot, FSRoot tgtRoot, ISVNEditor editor, String srcPath, String path) throws SVNException { editor.addDir(path, null, -1); FSRevisionNode node = srcRoot.getRevisionNode(srcPath); SVNProperties props = node.getProperties(myOwner); for (Iterator names = props.nameSet().iterator(); names.hasNext();) { String propName = (String) names.next(); SVNPropertyValue propValue = props.getSVNPropertyValue(propName); editor.changeDirProperty(propName, propValue); } Map entries = node.getDirEntries(myOwner); for (Iterator entryNames = entries.keySet().iterator(); entryNames.hasNext();) { String entryName = (String) entryNames.next(); FSEntry entry = (FSEntry) entries.get(entryName); String newPath = SVNPathUtil.append(path, entry.getName()); if (entry.getType() == SVNNodeKind.DIR) { addSubdirectory(srcRoot, tgtRoot, editor, SVNPathUtil.append(srcPath, entry.getName()), newPath); editor.closeDir(); } else if (entry.getType() == SVNNodeKind.FILE) { editor.addFile(SVNPathUtil.append(path, entry.getName()), null, -1); String newSrcPath = SVNPathUtil.append(srcPath, entry.getName()); FSRevisionNode srcNode = srcRoot.getRevisionNode(newSrcPath); props = srcNode.getProperties(myOwner); for (Iterator names = props.nameSet().iterator(); names.hasNext();) { String propName = (String) names.next(); SVNPropertyValue propValue = props.getSVNPropertyValue(propName); editor.changeFileProperty(newPath, propName, propValue); } editor.applyTextDelta(newPath, null); InputStream targetStream = null; try { targetStream = srcRoot.getFileStreamForPath(myDeltaCombiner, newSrcPath); myDeltaGenerator.sendDelta(newPath, SVNFileUtil.DUMMY_IN, 0, targetStream, editor, false); } finally { SVNFileUtil.closeFile(targetStream); } String checksum = srcNode.getFileMD5Checksum(); editor.closeFile(newPath, checksum); } } } private class CopyInfo { String myCopyFromPath; long myCopyFromRevision; String myPath; public CopyInfo(String path, String copyFromPath, long copyFromRevision) { myPath = path; myCopyFromPath = copyFromPath; myCopyFromRevision = copyFromRevision; } } }