/*
* ====================================================================
* 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.admin;
import java.io.File;
import java.util.Iterator;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
import org.tmatesoft.svn.core.internal.wc.SVNFileType;
import org.tmatesoft.svn.core.io.ISVNReporter;
import org.tmatesoft.svn.core.io.ISVNReporterBaton;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.util.ISVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class SVNReporter implements ISVNReporterBaton {
private SVNAdminAreaInfo myInfo;
private SVNDepth myDepth;
private boolean myIsRestore;
private boolean myUseDepthCompatibilityTrick;
private boolean myIsStatus;
private boolean myIsHonorDepthExclude;
private File myTarget;
private ISVNDebugLog myLog;
private boolean myIsLockOnDemand;
private long myTotalFilesCount;
private long myReportedFilesCount;
public SVNReporter(SVNAdminAreaInfo info, File file, boolean restoreFiles,
boolean useDepthCompatibilityTrick, SVNDepth depth, boolean lockOnDemand,
boolean isStatus, boolean isHonorDepthExclude, ISVNDebugLog log) {
myInfo = info;
myDepth = depth;
myIsRestore = restoreFiles;
myIsStatus = isStatus;
myUseDepthCompatibilityTrick = useDepthCompatibilityTrick;
myLog = log;
myTarget = file;
myIsLockOnDemand = lockOnDemand;
myIsHonorDepthExclude = isHonorDepthExclude;
myTotalFilesCount = 0;
myReportedFilesCount = 0;
}
public void report(ISVNReporter reporter) throws SVNException {
try {
SVNAdminArea targetArea = myInfo.getTarget();
SVNWCAccess wcAccess = myInfo.getWCAccess();
SVNEntry targetEntry = wcAccess.getEntry(myTarget, false);
if (targetEntry == null || (targetEntry.isDirectory() && targetEntry.isScheduledForAddition())) {
SVNEntry parentEntry = wcAccess.getVersionedEntry(myTarget.getParentFile(), false);
long revision = parentEntry.getRevision();
if (myDepth == SVNDepth.UNKNOWN) {
myDepth = SVNDepth.INFINITY;
}
reporter.setPath("", null, revision, myDepth, targetEntry == null || targetEntry.isIncomplete());
if (targetEntry == null || targetEntry.isIncomplete()) {
myInfo.addIncompleteEntry("");
}
reporter.deletePath("");
reporter.finishReport();
myReportedFilesCount++;
myTotalFilesCount++;
return;
}
SVNEntry parentEntry = null;
boolean startEmpty = targetEntry.isIncomplete();
if (myUseDepthCompatibilityTrick && targetEntry.getDepth().compareTo(SVNDepth.IMMEDIATES) <= 0 &&
myDepth.compareTo(targetEntry.getDepth()) > 0) {
startEmpty = true;
}
long revision = targetEntry.getRevision();
if (!SVNRevision.isValidRevisionNumber(revision)) {
parentEntry = wcAccess.getVersionedEntry(myTarget.getParentFile(), false);
revision = parentEntry.getRevision();
}
reporter.setPath("", null, revision, targetEntry.getDepth(), startEmpty);
if (startEmpty) {
myInfo.addIncompleteEntry("");
}
boolean missing = false;
if (!targetEntry.isScheduledForDeletion()) {
SVNFileType fileType = SVNFileType.getType(myTarget);
missing = fileType == SVNFileType.NONE;
}
myTotalFilesCount = 1;
myReportedFilesCount = 1;
if (targetEntry.isDirectory()) {
if (!myIsStatus && !missing && !targetEntry.isThisDir()) {
missing = true;
}
if (missing) {
reporter.deletePath("");
} else if (myDepth != SVNDepth.EMPTY) {
reportEntries(reporter, targetArea, "", revision, startEmpty, myDepth);
}
} else if (targetEntry.isFile()) {
if (missing) {
restoreFile(targetArea, targetEntry.getName());
}
// report either linked path or entry path
parentEntry = parentEntry == null ? wcAccess.getEntry(myTarget.getParentFile(), false) : parentEntry;
String url = targetEntry.getURL();
String parentURL = parentEntry.getURL();
String expectedURL = SVNPathUtil.append(parentURL, SVNEncodingUtil.uriEncode(targetEntry.getName()));
if (parentEntry != null && !expectedURL.equals(url)) {
SVNURL svnURL = SVNURL.parseURIEncoded(url);
reporter.linkPath(svnURL, "", targetEntry.getLockToken(), targetEntry.getRevision(), targetEntry.getDepth(), false);
} else if (targetEntry.getRevision() != revision || targetEntry.getLockToken() != null) {
reporter.setPath("", targetEntry.getLockToken(), targetEntry.getRevision(), targetEntry.getDepth(), false);
}
}
reporter.finishReport();
} catch (SVNException e) {
try {
reporter.abortReport();
} catch (SVNException inner) {
myLog.logFine(SVNLogType.WC, inner);
}
throw e;
} catch (Throwable th) {
myLog.logFine(SVNLogType.WC, th);
try {
reporter.abortReport();
} catch (SVNException e) {
myLog.logFine(SVNLogType.WC, e);
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "WC report failed: {0}", th.getMessage());
SVNErrorManager.error(err, th, SVNLogType.WC);
}
}
private void reportEntries(ISVNReporter reporter, SVNAdminArea adminArea, String dirPath, long dirRevision,
boolean reportAll, SVNDepth depth) throws SVNException {
SVNWCAccess wcAccess = myInfo.getWCAccess();
String externalsProperty = adminArea.getProperties(adminArea.getThisDirName()).getStringPropertyValue(SVNProperty.EXTERNALS);
SVNEntry thisEntry = adminArea.getEntry(adminArea.getThisDirName(), true);
if (externalsProperty != null) {
// use owners path as a key.
String areaPath = adminArea.getRelativePath(myInfo.getAnchor());
myInfo.addExternal(areaPath, externalsProperty, externalsProperty);
myInfo.addDepth(areaPath, thisEntry.getDepth());
}
String parentURL = thisEntry.getURL();
for (Iterator e = adminArea.entries(true); e.hasNext();) {
SVNEntry entry = (SVNEntry) e.next();
if (adminArea.getThisDirName().equals(entry.getName())) {
continue;
}
myTotalFilesCount++;
String path = "".equals(dirPath) ? entry.getName() : SVNPathUtil.append(dirPath, entry.getName());
if (entry.isDeleted() || entry.isAbsent()) {
if (!reportAll) {
reporter.deletePath(path);
myReportedFilesCount++;
}
continue;
}
if (entry.getDepth() == SVNDepth.EXCLUDE) {
if (myIsHonorDepthExclude) {
reporter.setPath(path, null, dirRevision, SVNDepth.EXCLUDE, false);
} else {
if (!reportAll) {
reporter.deletePath(path);
}
}
continue;
}
if (entry.isScheduledForAddition()) {
continue;
}
File file = adminArea.getFile(entry.getName());
SVNFileType fileType = SVNFileType.getType(file);
boolean missing = fileType == SVNFileType.NONE;
String expectedURL = SVNPathUtil.append(parentURL, SVNEncodingUtil.uriEncode(entry.getName()));
if (entry.isFile()) {
if (missing && !entry.isScheduledForDeletion() && !entry.isScheduledForReplacement()) {
adminArea = restoreFile(adminArea, entry.getName());
}
String url = entry.getURL();
if (reportAll) {
if (!url.equals(expectedURL)) {
SVNURL svnURL = SVNURL.parseURIEncoded(url);
reporter.linkPath(svnURL, path, entry.getLockToken(), entry.getRevision(), entry.getDepth(), false);
} else {
reporter.setPath(path, entry.getLockToken(), entry.getRevision(), entry.getDepth(), false);
myReportedFilesCount++;
}
} else if (!entry.isScheduledForAddition() && !entry.isScheduledForReplacement() && !url.equals(expectedURL)) {
// link path
SVNURL svnURL = SVNURL.parseURIEncoded(url);
reporter.linkPath(svnURL, path, entry.getLockToken(), entry.getRevision(), entry.getDepth(), false);
} else if (entry.getRevision() != dirRevision ||
entry.getLockToken() != null ||
thisEntry.getDepth() == SVNDepth.EMPTY) {
reporter.setPath(path, entry.getLockToken(), entry.getRevision(),
entry.getDepth(), false);
myReportedFilesCount++;
}
} else if (entry.isDirectory() && (depth.compareTo(SVNDepth.FILES) > 0 || depth == SVNDepth.UNKNOWN)) {
if (missing) {
if (myIsRestore) {
boolean dropDeletedSchedule = myInfo.getAnchor().getWCAccess().getMaxFormatVersion() < SVNAdminArea16.WC_FORMAT;
if (entry.isScheduledForReplacement() || (dropDeletedSchedule && entry.isScheduledForDeletion())) {
// remove dir schedule if it is 'scheduled for deletion' but missing.
entry.setSchedule(null);
adminArea.saveEntries(false);
}
}
if (!reportAll) {
reporter.deletePath(path);
myReportedFilesCount++;
}
continue;
}
if (wcAccess.isMissing(adminArea.getFile(entry.getName()))) {
if (!myIsStatus) {
reporter.deletePath(path);
myReportedFilesCount++;
}
continue;
}
SVNAdminArea childArea = wcAccess.retrieve(adminArea.getFile(entry.getName()));
SVNEntry childEntry = childArea.getEntry(childArea.getThisDirName(), true);
String url = childEntry.getURL();
boolean startEmpty = childEntry.isIncomplete();
if (myUseDepthCompatibilityTrick && childEntry.getDepth().compareTo(SVNDepth.FILES) <= 0 &&
depth.compareTo(childEntry.getDepth()) > 0) {
startEmpty = true;
myInfo.addIncompleteEntry(path);
}
if (reportAll) {
if (!url.equals(expectedURL)) {
SVNURL svnURL = SVNURL.parseURIEncoded(url);
reporter.linkPath(svnURL, path, childEntry.getLockToken(), childEntry.getRevision(),
childEntry.getDepth(), startEmpty);
} else {
reporter.setPath(path, childEntry.getLockToken(), childEntry.getRevision(),
childEntry.getDepth(), startEmpty);
myReportedFilesCount++;
}
} else if (!url.equals(expectedURL)) {
SVNURL svnURL = SVNURL.parseURIEncoded(url);
reporter.linkPath(svnURL, path, childEntry.getLockToken(), childEntry.getRevision(),
childEntry.getDepth(), startEmpty);
} else if (childEntry.getLockToken() != null ||
childEntry.getRevision() != dirRevision ||
childEntry.isIncomplete() ||
thisEntry.getDepth() == SVNDepth.EMPTY ||
thisEntry.getDepth() == SVNDepth.FILES ||
(thisEntry.getDepth() == SVNDepth.IMMEDIATES &&
childEntry.getDepth() != SVNDepth.EMPTY) ||
(SVNDepth.INFINITY.compareTo(childEntry.getDepth()) > 0 && depth == SVNDepth.INFINITY)) {
reporter.setPath(path, childEntry.getLockToken(), childEntry.getRevision(),
childEntry.getDepth(), startEmpty);
myReportedFilesCount++;
}
if (depth == SVNDepth.INFINITY || depth == SVNDepth.UNKNOWN) {
reportEntries(reporter, childArea, path, childEntry.getRevision(), startEmpty, depth);
}
}
}
}
private SVNAdminArea restoreFile(SVNAdminArea adminArea, String name) throws SVNException {
if (!myIsRestore) {
return adminArea;
}
if (myIsLockOnDemand && !adminArea.isLocked()) {
adminArea.lock(false);
adminArea = myInfo.getWCAccess().upgrade(adminArea.getRoot());
}
adminArea.restoreFile(name);
SVNEntry entry = adminArea.getEntry(name, true);
myInfo.getWCAccess().handleEvent(SVNEventFactory.createSVNEvent(adminArea.getFile(entry.getName()), entry.getKind(), null, entry.getRevision(), SVNEventAction.RESTORE, null, null, null));
return adminArea;
}
public long getReportedFilesCount() {
return myReportedFilesCount;
}
public long getTotalFilesCount() {
return myTotalFilesCount;
}
}