/*
* ====================================================================
* 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.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.tmatesoft.svn.core.SVNCancelException;
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.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNHashSet;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.internal.wc.ISVNFileFetcher;
import org.tmatesoft.svn.core.internal.wc.ISVNUpdateEditor;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileType;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.SVNMergeCallback;
import org.tmatesoft.svn.core.internal.wc.SVNMergeCallback15;
import org.tmatesoft.svn.core.internal.wc.SVNMergeDriver;
import org.tmatesoft.svn.core.internal.wc.SVNObjectsPool;
import org.tmatesoft.svn.core.internal.wc.SVNUpdateEditor;
import org.tmatesoft.svn.core.internal.wc.SVNUpdateEditor15;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.SVNDiffOptions;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class SVNWCAccess implements ISVNEventHandler {
public static final int INFINITE_DEPTH = -1;
private ISVNEventHandler myEventHandler;
private ISVNOptions myOptions;
private Map myAdminAreas;
private Map myCleanupHandlers;
private SVNObjectsPool myObjectsPool;
private File myAnchor;
public static SVNWCAccess newInstance(ISVNEventHandler eventHandler) {
return new SVNWCAccess(eventHandler);
}
private static boolean isObjectsPoolEnabled() {
return Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty("svnkit.entry.pool", "true"));
}
private SVNWCAccess(ISVNEventHandler handler) {
myEventHandler = handler;
if (isObjectsPoolEnabled()) {
myObjectsPool = new SVNObjectsPool();
}
}
public void setEventHandler(ISVNEventHandler handler) {
myEventHandler = handler;
}
public ISVNEventHandler getEventHandler() {
return myEventHandler;
}
public void checkCancelled() throws SVNCancelException {
if (myEventHandler != null) {
myEventHandler.checkCancelled();
}
}
public void handleEvent(SVNEvent event) throws SVNException {
handleEvent(event, ISVNEventHandler.UNKNOWN);
}
public void registerCleanupHandler(SVNAdminArea area, ISVNCleanupHandler handler) {
if (area == null || handler == null) {
return;
}
if (myCleanupHandlers == null) {
myCleanupHandlers = new SVNHashMap();
}
myCleanupHandlers.put(area, handler);
}
public void handleEvent(SVNEvent event, double progress) throws SVNException {
if (myEventHandler != null) {
try {
myEventHandler.handleEvent(event, progress);
} catch (SVNException e) {
throw e;
} catch (Throwable th) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "Error while dispatching event: {0}", th.getMessage());
SVNErrorManager.error(err, th, SVNLogType.WC);
}
}
}
public void setOptions(ISVNOptions options) {
myOptions = options;
}
public ISVNOptions getOptions() {
if (myOptions == null) {
myOptions = new DefaultSVNOptions();
}
return myOptions;
}
public void setAnchor(File anchor) {
myAnchor = anchor;
}
public File getAnchor() {
return myAnchor;
}
public SVNAdminAreaInfo openAnchor(File path, boolean writeLock, int depth) throws SVNException {
File parent = path.getParentFile();
if (parent == null || "..".equals(path.getName())) {
SVNAdminArea anchor = open(path, writeLock, depth);
return new SVNAdminAreaInfo(this, anchor, anchor, "");
}
String name = path.getName();
SVNAdminArea parentArea = null;
SVNAdminArea targetArea = null;
SVNException parentError = null;
try {
parentArea = open(parent, writeLock, false, 0);
} catch (SVNException svne) {
if (writeLock && svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LOCKED) {
try {
parentArea = open(parent, false, false, 0);
} catch (SVNException svne2) {
throw svne;
}
parentError = svne;
} else if (svne.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) {
throw svne;
}
}
try {
targetArea = open(path, writeLock, false, depth);
} catch (SVNException svne) {
if (parentArea == null || svne.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) {
try {
close();
} catch (SVNException svne2) {
//
}
throw svne;
}
}
if (parentArea != null && targetArea != null) {
SVNEntry parentEntry = null;
SVNEntry targetEntry = null;
SVNEntry targetInParent = null;
try {
targetInParent = parentArea.getEntry(name, false);
targetEntry = targetArea.getEntry(targetArea.getThisDirName(), false);
parentEntry = parentArea.getEntry(parentArea.getThisDirName(), false);
} catch (SVNException svne) {
try {
close();
} catch (SVNException svne2) {
//
}
throw svne;
}
SVNURL parentURL = parentEntry != null ? parentEntry.getSVNURL() : null;
SVNURL targetURL = targetEntry != null ? targetEntry.getSVNURL() : null;
String encodedName = SVNEncodingUtil.uriEncode(name);
if (targetInParent == null || (parentURL != null && targetURL != null &&
(!parentURL.equals(targetURL.removePathTail()) || !encodedName.equals(SVNPathUtil.tail(targetURL.getURIEncodedPath()))))) {
if (myAdminAreas != null) {
myAdminAreas.remove(parent);
}
try {
doClose(parentArea, false);
} catch (SVNException svne) {
try {
close();
} catch (SVNException svne2) {
//
}
throw svne;
}
parentArea = null;
}
}
if (parentArea != null) {
if (parentError != null && targetArea != null) {
if (parentError.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LOCKED) {
// try to work without 'anchor'
try {
doClose(parentArea, false);
} catch (SVNException svne) {
try {
close();
} catch (SVNException svne2) {
//
}
throw svne;
}
parentArea = null;
} else {
try {
close();
} catch (SVNException svne) {
//
}
throw parentError;
}
}
}
if (targetArea == null) {
SVNEntry targetEntry = null;
try {
targetEntry = parentArea.getEntry(name, false);
} catch (SVNException svne) {
try {
close();
} catch (SVNException svne2) {
//
}
throw svne;
}
if (targetEntry != null && targetEntry.isDirectory()) {
if (myAdminAreas != null) {
myAdminAreas.put(path, null);
}
}
}
SVNAdminArea anchor = parentArea != null ? parentArea : targetArea;
SVNAdminArea target = targetArea != null ? targetArea : parentArea;
return new SVNAdminAreaInfo(this, anchor, target, parentArea == null ? "" : name);
}
public SVNAdminArea open(File path, boolean writeLock, int depth) throws SVNException {
return open(path, writeLock, false, depth);
}
public SVNAdminArea open(File path, boolean writeLock, boolean stealLock, int depth) throws SVNException {
return open(path, writeLock, stealLock, true, depth, Level.FINE);
}
public SVNAdminArea open(File path, boolean writeLock, boolean stealLock, boolean upgradeFormat, int depth, Level logLevel) throws SVNException {
Map tmp = new SVNHashMap();
SVNAdminArea area;
try {
area = doOpen(path, writeLock, stealLock, upgradeFormat, depth, tmp, logLevel);
} finally {
for(Iterator paths = tmp.keySet().iterator(); paths.hasNext();) {
Object childPath = paths.next();
SVNAdminArea childArea = (SVNAdminArea) tmp.get(childPath);
myAdminAreas.put(childPath, childArea);
}
}
return area;
}
public SVNAdminArea probeOpen(File path, boolean writeLock, int depth) throws SVNException {
return probeOpen(path, writeLock, depth, Level.FINE);
}
public SVNAdminArea probeOpen(File path, boolean writeLock, int depth, Level logLevel) throws SVNException {
File dir = probe(path, logLevel);
if (dir == null) {
// we tried to open root which is not wc.
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "''{0}'' is not a working copy", path);
SVNErrorManager.error(err, logLevel, SVNLogType.WC);
}
if (!path.equals(dir)) {
depth = 0;
}
SVNAdminArea adminArea = null;
try {
adminArea = open(dir, writeLock, false, true, depth, logLevel);
} catch (SVNException svne) {
SVNFileType childKind = SVNFileType.getType(path);
SVNErrorCode errCode = svne.getErrorMessage().getErrorCode();
if (!path.equals(dir) && (childKind == SVNFileType.DIRECTORY || childKind == SVNFileType.NONE) && errCode == SVNErrorCode.WC_NOT_DIRECTORY) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "''{0}'' is not a working copy", path);
SVNErrorManager.error(err, logLevel, SVNLogType.WC);
} else {
throw svne;
}
}
return adminArea;
}
public SVNAdminArea probeTry(File path, boolean writeLock, int depth) throws SVNException {
SVNAdminArea adminArea = null;
try {
adminArea = probeRetrieve(path);
} catch (SVNException svne) {
if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
try {
adminArea = probeOpen(path, writeLock, depth);
} catch (SVNException svne2) {
if (svne2.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) {
throw svne2;
}
}
} else {
throw svne;
}
}
return adminArea;
}
public void close() throws SVNException {
if (myAdminAreas != null) {
doClose(myAdminAreas, false);
myAdminAreas.clear();
}
myCleanupHandlers = null;
if (getObjectsPool() != null) {
getObjectsPool().clear();
}
}
public void closeAdminArea(File path) throws SVNException {
if (myAdminAreas != null) {
SVNAdminArea area = (SVNAdminArea) myAdminAreas.get(path);
if (area != null) {
doClose(area, false);
myAdminAreas.remove(path);
}
}
}
private SVNAdminArea doOpen(File path, boolean writeLock, boolean stealLock, boolean upgradeFormat, int depth, Map tmp, Level logLevel) throws SVNException {
// no support for 'under consturction here' - it will go to adminAreaFactory.
tmp = tmp == null ? new SVNHashMap() : tmp;
if (myAdminAreas != null) {
SVNAdminArea existing = (SVNAdminArea) myAdminAreas.get(path);
if (myAdminAreas.containsKey(path) && existing != null) {
SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.WC_LOCKED, "Working copy ''{0}'' locked", path);
SVNErrorManager.error(error, SVNLogType.WC);
}
} else {
myAdminAreas = new SVNHashMap();
}
SVNAdminArea area = SVNAdminAreaFactory.open(path, logLevel);
area.setWCAccess(this);
if (writeLock) {
area.lock(stealLock);
if (upgradeFormat) {
area = SVNAdminAreaFactory.upgrade(area);
}
}
tmp.put(path, area);
if (depth != 0) {
if (depth > 0) {
depth--;
}
for(Iterator entries = area.entries(false); entries.hasNext();) {
try {
checkCancelled();
} catch (SVNCancelException e) {
doClose(tmp, false);
throw e;
}
SVNEntry entry = (SVNEntry) entries.next();
if (entry.getKind() != SVNNodeKind.DIR || area.getThisDirName().equals(entry.getName())) {
continue;
}
if (entry.getDepth() == SVNDepth.EXCLUDE) {
continue;
}
File childPath = new File(path, entry.getName());
try {
// this method will put created area into our map.
doOpen(childPath, writeLock, stealLock, upgradeFormat, depth, tmp, logLevel);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) {
doClose(tmp, false);
throw e;
}
// only for missing!
tmp.put(childPath, null);
}
SVNAdminArea childArea = (SVNAdminArea) tmp.get(childPath);
if (childArea != null) {
SVNEntry childRootEntry = childArea.getEntry(childArea.getThisDirName(), false);
SVNEntry thisRootEntry = area.getEntry(childArea.getThisDirName(), false);
String childRoot = childRootEntry.getRepositoryRoot();
String expectedRoot = thisRootEntry.getRepositoryRoot();
if (childRoot != null && !childRoot.equals(expectedRoot)) {
Map toClose = new SVNHashMap();
toClose.put(childPath, childArea);
String childPathAbs = childPath.getAbsolutePath().replace(File.separatorChar, '/');
for (Iterator paths = tmp.keySet().iterator(); paths.hasNext();) {
File p = (File) paths.next();
String pAbs = p.getAbsolutePath().replace(File.separatorChar, '/');
if (SVNPathUtil.isAncestor(childPathAbs, pAbs)) {
toClose.put(p, tmp.get(p));
paths.remove();
}
}
tmp.put(childPath, null);
doClose(toClose, false);
}
}
}
}
return area;
}
private void doClose(Map adminAreas, boolean preserveLocks) throws SVNException {
Set closedAreas = new SVNHashSet();
while(!adminAreas.isEmpty()) {
Map copy = new SVNHashMap(adminAreas);
try {
for (Iterator paths = copy.keySet().iterator(); paths.hasNext();) {
File path = (File) paths.next();
SVNAdminArea adminArea = (SVNAdminArea) copy.get(path);
if (adminArea == null) {
closedAreas.add(path);
continue;
}
doClose(adminArea, preserveLocks);
closedAreas.add(path);
}
} finally {
for (Iterator paths = closedAreas.iterator(); paths.hasNext();) {
File path = (File) paths.next();
adminAreas.remove(path);
}
}
}
}
private void doClose(SVNAdminArea adminArea, boolean preserveLocks) throws SVNException {
if (adminArea == null) {
return;
}
if (myCleanupHandlers != null) {
ISVNCleanupHandler handler = (ISVNCleanupHandler) myCleanupHandlers.remove(adminArea);
if (handler != null) {
handler.cleanup(adminArea);
}
}
if (!preserveLocks && adminArea.isLocked()) {
adminArea.unlock();
}
adminArea.close();
}
public SVNAdminArea probeRetrieve(File path) throws SVNException {
File dir = probe(path, Level.FINE);
if (dir == null) {
// we tried to open root which is not wc.
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "''{0}'' is not a working copy", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
return retrieve(dir);
}
public boolean isMissing(File path) {
if (myAdminAreas != null) {
return myAdminAreas.containsKey(path) && myAdminAreas.get(path) == null;
}
return false;
}
public boolean isLocked(File path) throws SVNException {
File lockFile = new File(path, SVNFileUtil.getAdminDirectoryName());
lockFile = new File(lockFile, "lock");
if (SVNFileType.getType(lockFile) == SVNFileType.FILE) {
return true;
} else if (SVNFileType.getType(lockFile) == SVNFileType.NONE) {
return false;
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_LOCKED,
"Lock file ''{0}'' is not a regular file", lockFile);
SVNErrorManager.error(err, SVNLogType.WC);
return false;
}
public boolean isWCRoot(File path) throws SVNException {
SVNEntry entry = getEntry(path, false);
File parent = path.getParentFile();
if (parent == null && entry != null) {
return true;
}
SVNAdminArea parentArea = getAdminArea(parent);
SVNWCAccess tmpAccess = null;
SVNWCAccess access = this;
try {
if (parentArea == null) {
tmpAccess = new SVNWCAccess(null);
try {
parentArea = tmpAccess.probeOpen(parent, false, 0, Level.FINEST);
} catch (SVNException svne) {
return true;
}
access = tmpAccess;
}
SVNEntry parentEntry = access.getEntry(parent, false);
if (parentEntry == null || !parentEntry.isThisDir()) {
return true;
}
if (parentEntry.getURL() == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL,
"''{0}'' has no ancestry information", parent);
SVNErrorManager.error(err, SVNLogType.WC);
}
// what about switched paths?
/*
if (entry != null && entry.getURL() != null) {
if (!entry.getURL().equals(SVNPathUtil.append(parentEntry.getURL(), SVNEncodingUtil.uriEncode(path.getName())))) {
return true;
}
}*/
entry = parentArea.getEntry(path.getName(), false);
if (entry == null) {
return true;
}
} finally {
if (tmpAccess != null) {
try {
tmpAccess.close();
} catch (SVNException svne) {
//
}
}
}
return false;
}
public SVNTreeConflictDescription getTreeConflict(File path) throws SVNException {
File parent = path.getParentFile();
if (parent == null) {
return null;
}
boolean closeParentArea = false;
SVNAdminArea parentArea = null;
try {
parentArea = retrieve(parent);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
e = null;
try {
parentArea = open(parent, false, 0);
closeParentArea = true;
} catch (SVNException internal) {
if (internal.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY
|| internal.getErrorMessage().getErrorCode() == SVNErrorCode.WC_UNSUPPORTED_FORMAT) {
return null;
}
e = internal;
}
}
if (e != null) {
throw e;
}
}
SVNTreeConflictDescription treeConflict = parentArea.getTreeConflict(path.getName());
if (closeParentArea) {
closeAdminArea(parent);
}
return treeConflict;
}
public boolean hasTreeConflict(File path) throws SVNException {
SVNTreeConflictDescription treeConflict = getTreeConflict(path);
return treeConflict != null;
}
public SVNEntry getEntry(File path, boolean showHidden) throws SVNException {
SVNAdminArea adminArea = getAdminArea(path);
String entryName = null;
if (adminArea == null) {
adminArea = getAdminArea(path.getParentFile());
entryName = path.getName();
} else {
entryName = adminArea.getThisDirName();
}
if (adminArea != null) {
return adminArea.getEntry(entryName, showHidden);
}
return null;
}
public SVNEntry getVersionedEntry(File path, boolean showHidden) throws SVNException {
SVNEntry entry = getEntry(path, showHidden);
if (entry == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND,
"''{0}'' is not under version control", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
return entry;
}
public void setRepositoryRoot(File path, SVNURL reposRoot) throws SVNException {
SVNEntry entry = getEntry(path, false);
if (entry == null) {
return;
}
SVNAdminArea adminArea = null;
String name = null;
if (entry.isFile()) {
adminArea = getAdminArea(path.getParentFile());
name = path.getName();
} else {
adminArea = getAdminArea(path);
name = adminArea != null ? adminArea.getThisDirName() : null;
}
if (adminArea == null) {
return;
}
if (adminArea.tweakEntry(name, null, reposRoot.toString(), -1, false)) {
adminArea.saveEntries(false);
}
}
public SVNAdminArea[] getAdminAreas() {
if (myAdminAreas != null) {
return (SVNAdminArea[]) myAdminAreas.values().toArray(new SVNAdminArea[myAdminAreas.size()]);
}
return new SVNAdminArea[0];
}
/**
* Ugrades SVNAdminArea associated with the path and cached in this SVNWCAccess instance.
* Updates caches if upgrade was done.
*
* @param path path associated with already retrieved and locked SVNAdminArea
* @return newly created SVNAdminArea object if upgrade was done or already cached SVNAdminArea instance otherwise.
* @throws SVNException
*/
public SVNAdminArea upgrade(File path) throws SVNException {
SVNAdminArea upgradedArea = null;
if (myAdminAreas != null) {
SVNAdminArea area = (SVNAdminArea) myAdminAreas.get(path);
if (area != null) {
ISVNCleanupHandler cleanupHandler = null;
if (myCleanupHandlers != null) {
cleanupHandler = (ISVNCleanupHandler) myCleanupHandlers.get(area);
}
upgradedArea = SVNAdminAreaFactory.upgrade(area);
if (upgradedArea != area) {
myAdminAreas.put(path, upgradedArea);
if (cleanupHandler != null) {
myCleanupHandlers.remove(area);
myCleanupHandlers.put(upgradedArea, cleanupHandler);
}
}
}
}
return upgradedArea;
}
public SVNAdminArea retrieve(File path) throws SVNException {
SVNAdminArea adminArea = getAdminArea(path);
if (adminArea == null) {
SVNEntry subEntry = null;
try {
SVNAdminArea dirAdminArea = getAdminArea(path.getParentFile());
if (dirAdminArea != null) {
subEntry = dirAdminArea.getEntry(path.getName(), true);
}
} catch (SVNException svne) {
subEntry = null;
}
SVNFileType type = SVNFileType.getType(path);
if (subEntry != null) {
if (subEntry.getKind() == SVNNodeKind.DIR && type == SVNFileType.FILE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Expected ''{0}'' to be a directory but found a file", path);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (subEntry.getKind() == SVNNodeKind.FILE && type == SVNFileType.DIRECTORY) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Expected ''{0}'' to be a file but found a directory", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
File adminDir = new File(path, SVNFileUtil.getAdminDirectoryName());
SVNFileType wcType = SVNFileType.getType(adminDir);
if (type == SVNFileType.NONE) {
SVNErrorMessage childErr = SVNErrorMessage.create(SVNErrorCode.WC_PATH_NOT_FOUND, "Directory ''{0}'' is missing", path);
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Directory ''{0}'' is missing", path);
err.setChildErrorMessage(childErr);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (type == SVNFileType.DIRECTORY && wcType == SVNFileType.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Directory ''{0}'' containing working copy admin area is missing", adminDir);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (type == SVNFileType.DIRECTORY && wcType == SVNFileType.DIRECTORY) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Unable to lock ''{0}''", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_LOCKED, "Working copy ''{0}'' is not locked", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
return adminArea;
}
//analogous to retrieve_internal
public SVNAdminArea getAdminArea(File path) {
//internal retrieve
SVNAdminArea adminArea = null;
if (myAdminAreas != null) {
adminArea = (SVNAdminArea) myAdminAreas.get(path);
}
return adminArea;
}
public void walkEntries(File path, ISVNEntryHandler handler, boolean showHidden, SVNDepth depth) throws SVNException {
walkEntries(path, handler, showHidden, false, depth);
}
public void walkEntries(File path, ISVNEntryHandler handler, boolean showHidden, boolean includeTC, SVNDepth depth) throws SVNException {
// wrap handler into tc handler
if (includeTC) {
handler = new TCEntryHandler(path, this, handler, depth);
}
SVNEntry entry = getEntry(path, showHidden);
if (entry == null) {
if (includeTC) {
SVNTreeConflictDescription tc = getTreeConflict(path);
if (tc != null) {
handler.handleEntry(path, null);
return;
}
}
handler.handleError(path, SVNErrorMessage.create(SVNErrorCode.UNVERSIONED_RESOURCE,
"''{0}'' is not under version control", path));
return;
}
if (entry.isFile()) {
try {
handler.handleEntry(path, entry);
} catch (SVNException svne) {
handler.handleError(path, svne.getErrorMessage());
}
} else if (entry.isDirectory()) {
SVNAdminArea adminArea = entry.getAdminArea();
try {
adminArea.walkThisDirectory(handler, includeTC ? true : showHidden, depth);
} catch (SVNException svne) {
handler.handleError(path, svne.getErrorMessage());
}
} else {
handler.handleError(path, SVNErrorMessage.create(SVNErrorCode.NODE_UNKNOWN_KIND,
"''{0}'' has an unrecognized node kind", path));
}
}
private static boolean ourNeverDescendIntoSymlinks = Boolean.getBoolean("svnkit.symlinks.neverDescend");
private File probe(File path, Level logLevel) throws SVNException {
int wcFormat = -1;
SVNFileType type = SVNFileType.getType(path);
boolean eligible = type == SVNFileType.DIRECTORY;
// only treat as directories those, that are not versioned in parent wc.
if (eligible) {
wcFormat = SVNAdminAreaFactory.checkWC(path, true, logLevel);
} else if (type == SVNFileType.SYMLINK && path.isDirectory()) {
// either wc root which is a link or link within wc.
// check for being root.
eligible = !ourNeverDescendIntoSymlinks && isWCRoot(path);
if (eligible) {
wcFormat = SVNAdminAreaFactory.checkWC(path, true, logLevel);
}
} else {
wcFormat = 0;
}
//non wc
if (!eligible || wcFormat == 0) {
if ("..".equals(path.getName()) || ".".equals(path.getName())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_BAD_PATH, "Path ''{0}'' ends in ''{1}'', which is unsupported for this operation", new Object[]{path, path.getName()});
SVNErrorManager.error(err, SVNLogType.WC);
}
path = path.getParentFile();
}
return path;
}
public static boolean matchesChangeList(Collection changeLists, SVNEntry entry) {
return changeLists == null || changeLists.isEmpty() || (entry != null && entry.getChangelistName() != null && changeLists.contains(entry.getChangelistName()));
}
int getMaxFormatVersion() {
int maxVersion = -1;
for (Iterator iterator = myAdminAreas.values().iterator(); iterator.hasNext();) {
SVNAdminArea adminArea = (SVNAdminArea) iterator.next();
if (adminArea != null && adminArea.getFormatVersion() > maxVersion) {
maxVersion = adminArea.getFormatVersion();
}
}
return maxVersion;
}
public ISVNUpdateEditor createUpdateEditor(SVNAdminAreaInfo info, String switchURL,
boolean allowUnversionedObstructions, boolean depthIsSticky, SVNDepth depth,
String[] preservedExtensions, ISVNFileFetcher fileFetcher, boolean lockOnDemand) throws SVNException {
int maxVersion = getMaxFormatVersion();
if (0 < maxVersion && maxVersion < SVNAdminAreaFactory.WC_FORMAT_16) {
return SVNUpdateEditor15.createUpdateEditor(info, switchURL, allowUnversionedObstructions, depthIsSticky, depth, preservedExtensions, fileFetcher, lockOnDemand);
}
return SVNUpdateEditor.createUpdateEditor(info, switchURL, allowUnversionedObstructions, depthIsSticky, depth, preservedExtensions, fileFetcher, lockOnDemand);
}
public SVNMergeCallback createMergeCallback(SVNMergeDriver mergeDriver, SVNAdminArea adminArea, SVNURL url,
SVNDiffOptions mergeOptions, Map conflictedPaths, boolean force, boolean dryRun) {
int maxVersion = getMaxFormatVersion();
if (maxVersion < SVNAdminAreaFactory.WC_FORMAT_16) {
return new SVNMergeCallback15(adminArea, url, force, dryRun,
mergeOptions, conflictedPaths, mergeDriver);
}
return new SVNMergeCallback(adminArea, url, force, dryRun, mergeOptions, conflictedPaths, mergeDriver);
}
private static class TCEntryHandler implements ISVNEntryHandler {
private ISVNEntryHandler myDelegate;
private SVNDepth myDepth;
private File myTargetPath;
private SVNWCAccess myWCAccess;
public TCEntryHandler(File target, SVNWCAccess wcAccess, ISVNEntryHandler delegate, SVNDepth depth) {
myDelegate = delegate;
myDepth = depth;
myTargetPath = target;
myWCAccess = wcAccess;
}
public void handleEntry(File path, SVNEntry entry) throws SVNException {
myDelegate.handleEntry(path, entry);
if (entry == null || !entry.isDirectory() || entry.isHidden()) {
return;
}
boolean checkChildren = false;
if (myDepth == SVNDepth.IMMEDIATES || myDepth == SVNDepth.FILES) {
checkChildren = path.equals(myTargetPath);
} else if (myDepth == SVNDepth.INFINITY || myDepth == SVNDepth.EXCLUDE || myDepth == SVNDepth.UNKNOWN) {
checkChildren = true;
} else {
return;
}
if (!checkChildren) {
return;
}
Map tcs = entry.getTreeConflicts();
for(Iterator paths = tcs.keySet().iterator(); paths.hasNext();) {
File p = (File) paths.next();
SVNTreeConflictDescription tc = (SVNTreeConflictDescription) tcs.get(p);
if (tc.getNodeKind() == SVNNodeKind.DIR && myDepth == SVNDepth.FILES) {
continue;
}
SVNEntry conflictEntry = myWCAccess.getEntry(p, true);
if (conflictEntry == null || conflictEntry.isDeleted()) {
myDelegate.handleEntry(p, null);
}
}
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
if (error != null && error.getErrorCode() == SVNErrorCode.UNVERSIONED_RESOURCE) {
SVNTreeConflictDescription tc = myWCAccess.getTreeConflict(path);
if (tc != null) {
myDelegate.handleEntry(path, null);
return;
}
}
myDelegate.handleError(path, error);
}
}
public SVNObjectsPool getObjectsPool() {
return myObjectsPool;
}
}