/*
* ====================================================================
* Copyright (c) 2004-2009 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.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.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.io.SVNLocationEntry;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class FSRevisionRoot extends FSRoot {
private long myRevision;
private long myRootOffset;
private long myChangesOffset;
public FSRevisionRoot(FSFS owner, long revision) {
super(owner);
myRevision = revision;
myRootOffset = -1;
myChangesOffset = -1;
}
public long getRevision() {
return myRevision;
}
public Map getChangedPaths() throws SVNException {
FSFile file = getOwner().getPackOrRevisionFSFile(getRevision());
try {
loadOffsets(file);
file.seek(myChangesOffset);
return fetchAllChanges(file, true);
} finally {
file.close();
}
}
public FSCopyInheritance getCopyInheritance(FSParentPath child) throws SVNException{
return null;
}
public FSNodeHistory getNodeHistory(String path) throws SVNException {
SVNNodeKind kind = checkNodeKind(path);
if (kind == SVNNodeKind.NONE) {
SVNErrorManager.error(FSErrors.errorNotFound(this, path), SVNLogType.FSFS);
}
return new FSNodeHistory(new SVNLocationEntry(getRevision(), SVNPathUtil.canonicalizeAbsolutePath(path)),
false, new SVNLocationEntry(SVNRepository.INVALID_REVISION, null), getOwner());
}
public FSClosestCopy getClosestCopy(String path) throws SVNException {
FSParentPath parentPath = openPath(path, true, true);
SVNLocationEntry copyDstEntry = FSNodeHistory.findYoungestCopyroot(getOwner().getRepositoryRoot(),
parentPath);
if (copyDstEntry == null || copyDstEntry.getRevision() == 0) {
return null;
}
FSRevisionRoot copyDstRoot = getOwner().createRevisionRoot(copyDstEntry.getRevision());
if (copyDstRoot.checkNodeKind(path) == SVNNodeKind.NONE) {
return null;
}
FSParentPath copyDstParentPath = copyDstRoot.openPath(path, true, true);
FSRevisionNode copyDstNode = copyDstParentPath.getRevNode();
if (!copyDstNode.getId().isRelated(parentPath.getRevNode().getId())) {
return null;
}
long createdRev = copyDstNode.getCreatedRevision();
if (createdRev == copyDstEntry.getRevision()) {
if (copyDstNode.getPredecessorId() == null) {
return null;
}
}
return new FSClosestCopy(copyDstRoot, copyDstEntry.getPath());
}
public FSRevisionNode getRootRevisionNode() throws SVNException {
if (myRootRevisionNode == null) {
FSFile file = getOwner().getPackOrRevisionFSFile(getRevision());
try {
loadOffsets(file);
file.seek(myRootOffset);
Map headers = file.readHeader();
myRootRevisionNode = FSRevisionNode.fromMap(headers);
} finally {
file.close();
}
}
return myRootRevisionNode;
}
public SVNLocationEntry getPreviousLocation(String path, long[] appearedRevision) throws SVNException {
if (appearedRevision != null && appearedRevision.length > 0) {
appearedRevision[0] = SVNRepository.INVALID_REVISION;
}
FSClosestCopy closestCopy = getClosestCopy(path);
if (closestCopy == null) {
return null;
}
FSRevisionRoot copyTargetRoot = closestCopy.getRevisionRoot();
String copyTargetPath = closestCopy.getPath();
FSRevisionNode copyFromNode = copyTargetRoot.getRevisionNode(copyTargetPath);
String copyFromPath = copyFromNode.getCopyFromPath();
long copyFromRevision = copyFromNode.getCopyFromRevision();
String remainder = "";
if (!path.equals(copyTargetPath)) {
remainder = path.substring(copyTargetPath.length());
if (remainder.startsWith("/")) {
remainder = remainder.substring(1);
}
}
String previousPath = SVNPathUtil.getAbsolutePath(SVNPathUtil.append(copyFromPath, remainder));
if (appearedRevision != null && appearedRevision.length > 0) {
appearedRevision[0] = copyTargetRoot.getRevision();
}
return new SVNLocationEntry(copyFromRevision, previousPath);
}
public long getNodeOriginRevision(String path) throws SVNException {
path = SVNPathUtil.canonicalizeAbsolutePath(path);
FSRevisionNode node = getRevisionNode(path);
String nodeID = node.getId().getNodeID();
FSFS fsfs = getOwner();
if (nodeID.startsWith("_")) {
return SVNRepository.INVALID_REVISION;
}
int dashIndex = nodeID.indexOf('-');
if (dashIndex != -1 && dashIndex != nodeID.length() - 1) {
try {
return Long.parseLong(nodeID.substring(dashIndex + 1));
} catch (NumberFormatException nfe) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.FS_CORRUPT, nfe), SVNLogType.FSFS);
}
}
String cachedOriginID = fsfs.getNodeOrigin(nodeID);
if (cachedOriginID != null) {
FSID id = FSID.fromString(cachedOriginID);
return id.getRevision();
}
long lastRev = SVNRepository.INVALID_REVISION;
String lastPath = path;
FSRevisionRoot curRoot = this;
while (true) {
if (FSRepository.isValidRevision(lastRev)) {
curRoot = fsfs.createRevisionRoot(lastRev);
}
SVNLocationEntry previousLocation = curRoot.getPreviousLocation(lastPath, null);
if (previousLocation == null) {
break;
}
lastPath = previousLocation.getPath();
lastRev = previousLocation.getRevision();
}
node = curRoot.getRevisionNode(lastPath);
FSID predID = node.getId();
while (predID != null) {
node = fsfs.getRevisionNode(predID);
predID = node.getPredecessorId();
}
if (!nodeID.startsWith("_")) {
fsfs.setNodeOrigin(nodeID, node.getId());
}
return node.getCreatedRevision();
}
private void loadOffsets(FSFile file) throws SVNException {
if (myRootOffset >= 0) {
return;
}
long[] rootOffset = { -1 };
long[] changesOffset = { -1 };
FSRepositoryUtil.loadRootChangesOffset(getOwner(), getRevision(), file, rootOffset, changesOffset);
myRootOffset = rootOffset[0];
myChangesOffset = changesOffset[0];
}
}