/*
* ====================================================================
* 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.TreeSet;
import java.util.logging.Level;
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.wc.SVNClassLoader;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public abstract class SVNAdminAreaFactory implements Comparable {
public static final int WC_FORMAT_13 = 4;
public static final int WC_FORMAT_14 = 8;
public static final int WC_FORMAT_15 = 9;
public static final int WC_FORMAT_16 = 10;
private static final Collection<SVNAdminAreaFactory> ourFactories = new TreeSet<SVNAdminAreaFactory>();
private static boolean ourIsUpgradeEnabled = Boolean.valueOf(System.getProperty("svnkit.upgradeWC", System.getProperty("javasvn.upgradeWC", "true"))).booleanValue();
private static ISVNAdminAreaFactorySelector ourSelector;
private static ISVNAdminAreaFactorySelector ourDefaultSelector = new DefaultSelector();
static {
SVNAdminAreaFactory.registerFactories();
}
public static void setUpgradeEnabled(boolean enabled) {
ourIsUpgradeEnabled = enabled;
}
public static boolean isUpgradeEnabled() {
return ourIsUpgradeEnabled;
}
public static void setSelector(ISVNAdminAreaFactorySelector selector) {
ourSelector = selector;
}
public static ISVNAdminAreaFactorySelector getSelector() {
return ourSelector != null ? ourSelector : ourDefaultSelector;
}
public static int checkWC(File path, boolean useSelector) throws SVNException {
return checkWC(path, useSelector, Level.FINE);
}
public static int checkWC(File path, boolean useSelector, Level logLevel) throws SVNException {
Collection enabledFactories = ourFactories;
if (useSelector) {
enabledFactories = getSelector().getEnabledFactories(path, enabledFactories, false);
}
SVNErrorMessage error = null;
int version = -1;
for(Iterator factories = enabledFactories.iterator(); factories.hasNext();) {
SVNAdminAreaFactory factory = (SVNAdminAreaFactory) factories.next();
try {
version = factory.doCheckWC(path, logLevel);
if (version == 0) {
return version;
}
if (version > factory.getSupportedVersion()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_UNSUPPORTED_FORMAT,
"The path ''{0}'' appears to be part of a Subversion 1.7 or greater\n" +
"working copy. Please upgrade your Subversion client to use this\n" +
"working copy.",
path);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (version < factory.getSupportedVersion()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_UNSUPPORTED_FORMAT,
"Working copy format of {0} is too old ({1}); please check out your working copy again",
new Object[] {path, new Integer(version)});
SVNErrorManager.error(err, SVNLogType.WC);
}
} catch (SVNException e) {
if (error != null) {
error.setChildErrorMessage(e.getErrorMessage());
} else {
error = e.getErrorMessage();
}
continue;
}
return version;
}
if (error == null) {
error = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "''{0}'' is not a working copy", path);
}
if (error.getErrorCode() == SVNErrorCode.WC_UNSUPPORTED_FORMAT) {
error.setChildErrorMessage(null);
}
SVNErrorManager.error(error, logLevel, SVNLogType.WC);
return 0;
}
public static SVNAdminArea open(File path, Level logLevel) throws SVNException {
SVNErrorMessage error = null;
int wcFormatVersion = -1;
Collection enabledFactories = getSelector().getEnabledFactories(path, ourFactories, false);
File adminDir = new File(path, SVNFileUtil.getAdminDirectoryName());
if (adminDir.isDirectory()) {
for (Iterator factories = enabledFactories.iterator(); factories.hasNext();) {
SVNAdminAreaFactory factory = (SVNAdminAreaFactory) factories.next();
try {
wcFormatVersion = factory.getVersion(path);
if (wcFormatVersion > factory.getSupportedVersion()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_UNSUPPORTED_FORMAT,
"The path ''{0}'' appears to be part of a Subversion 1.7 or greater\n" +
"working copy. Please upgrade your Subversion client to use this\n" +
"working copy.",
path);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (wcFormatVersion < factory.getSupportedVersion()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_UNSUPPORTED_FORMAT,
"Working copy format of {0} is too old ({1}); please check out your working copy again",
new Object[]{path, new Integer(wcFormatVersion)});
SVNErrorManager.error(err, SVNLogType.WC);
}
} catch (SVNException e) {
if (error != null) {
error.setChildErrorMessage(e.getErrorMessage());
} else {
error = e.getErrorMessage();
}
continue;
}
SVNAdminArea adminArea = factory.doOpen(path, wcFormatVersion);
if (adminArea != null) {
adminArea.setWorkingCopyFormatVersion(wcFormatVersion);
return adminArea;
}
}
}
if (error == null) {
error = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "''{0}'' is not a working copy", path);
}
if (error.getErrorCode() == SVNErrorCode.WC_UNSUPPORTED_FORMAT) {
error.setChildErrorMessage(null);
}
SVNErrorManager.error(error, logLevel, SVNLogType.WC);
return null;
}
public static SVNAdminArea upgrade(SVNAdminArea area) throws SVNException {
if (isUpgradeEnabled() && !ourFactories.isEmpty()) {
Collection enabledFactories = getSelector().getEnabledFactories(area.getRoot(), ourFactories, true);
if (!enabledFactories.isEmpty()) {
SVNAdminAreaFactory newestFactory = (SVNAdminAreaFactory) enabledFactories.iterator().next();
SVNAdminArea newArea = newestFactory.doChangeWCFormat(area);
if (newArea != null && newArea != area && newArea.getWCAccess() != null) {
SVNEvent event = SVNEventFactory.createSVNEvent(newArea.getRoot(), SVNNodeKind.DIR, null, SVNRepository.INVALID_REVISION, SVNEventAction.UPGRADE, null, null, null);
newArea.getWCAccess().handleEvent(event, ISVNEventHandler.UNKNOWN);
}
area = newArea;
}
}
return area;
}
public static SVNAdminArea changeWCFormat(SVNAdminArea adminArea, int format) throws SVNException {
SVNAdminAreaFactory factory = getAdminAreaFactory(format);
SVNAdminArea newArea = factory.doChangeWCFormat(adminArea);
if (newArea != null && newArea != adminArea && newArea.getWCAccess() != null) {
SVNEvent event = SVNEventFactory.createSVNEvent(newArea.getRoot(), SVNNodeKind.DIR, null, SVNRepository.INVALID_REVISION, SVNEventAction.UPGRADE, null, null, null);
newArea.getWCAccess().handleEvent(event, ISVNEventHandler.UNKNOWN);
}
adminArea = newArea;
return adminArea;
}
private static SVNAdminAreaFactory getAdminAreaFactory(int wcFormat) throws SVNException {
for (Iterator<SVNAdminAreaFactory> ourFactoriesIter = ourFactories.iterator(); ourFactoriesIter.hasNext();) {
SVNAdminAreaFactory nextFactory = ourFactoriesIter.next();
if (nextFactory.getSupportedVersion() == wcFormat) {
return nextFactory;
}
}
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.WC_UNSUPPORTED_FORMAT), SVNLogType.DEFAULT);
return null;
}
private static int readFormatVersion(File adminDir) throws SVNException {
SVNErrorMessage error = null;
int version = -1;
Collection enabledFactories = getSelector().getEnabledFactories(adminDir.getParentFile(), ourFactories, false);
for(Iterator factories = enabledFactories.iterator(); factories.hasNext();) {
SVNAdminAreaFactory factory = (SVNAdminAreaFactory) factories.next();
try {
version = factory.getVersion(adminDir);
} catch (SVNException e) {
error = e.getErrorMessage();
continue;
}
return version;
}
if (error == null) {
error = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "''{0}'' is not a working copy", adminDir);
}
SVNErrorManager.error(error, SVNLogType.WC);
return -1;
}
public static void createVersionedDirectory(File path, String url, String rootURL, String uuid, long revNumber, SVNDepth depth) throws SVNException {
if (!ourFactories.isEmpty()) {
if (!checkAdminAreaExists(path, url, revNumber)) {
Collection enabledFactories = getSelector().getEnabledFactories(path, ourFactories, true);
if (!enabledFactories.isEmpty()) {
SVNAdminAreaFactory newestFactory = (SVNAdminAreaFactory) enabledFactories.iterator().next();
newestFactory.doCreateVersionedDirectory(path, url, rootURL, uuid, revNumber, depth);
}
}
}
}
public static void createVersionedDirectory(File path, SVNURL url, SVNURL rootURL, String uuid, long revNumber, SVNDepth depth) throws SVNException {
createVersionedDirectory(path, url != null ? url.toString() : null, rootURL != null ? rootURL.toString() : null, uuid, revNumber, depth);
}
private static boolean checkAdminAreaExists(File dir, String url, long revision) throws SVNException {
File adminDir = new File(dir, SVNFileUtil.getAdminDirectoryName());
if (adminDir.exists() && !adminDir.isDirectory()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "''{0}'' is not a directory", dir);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (!adminDir.exists()) {
return false;
}
boolean wcExists = false;
try {
readFormatVersion(adminDir);
wcExists = true;
} catch (SVNException svne) {
return false;
}
if (wcExists) {
SVNWCAccess wcAccess = SVNWCAccess.newInstance(null);
SVNAdminArea adminArea = null;
SVNEntry entry = null;
try {
adminArea = wcAccess.open(dir, false, 0);
entry = adminArea.getVersionedEntry(adminArea.getThisDirName(), false);
} finally {
wcAccess.closeAdminArea(dir);
}
if (!entry.isScheduledForDeletion()) {
if (entry.getRevision() != revision) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Revision {0} doesn''t match existing revision {1} in ''{2}''", new Object[]{new Long(revision), new Long(entry.getRevision()), dir});
SVNErrorManager.error(err, SVNLogType.WC);
}
if (!url.equals(entry.getURL())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "URL ''{0}'' doesn''t match existing URL ''{1}'' in ''{2}''", new Object[]{url, entry.getURL(), dir});
SVNErrorManager.error(err, SVNLogType.WC);
}
}
}
return wcExists;
}
public abstract int getSupportedVersion();
protected abstract int getVersion(File path) throws SVNException;
protected abstract SVNAdminArea doOpen(File path, int version) throws SVNException;
protected abstract SVNAdminArea doChangeWCFormat(SVNAdminArea area) throws SVNException;
protected abstract void doCreateVersionedDirectory(File path, String url, String rootURL, String uuid, long revNumber, SVNDepth depth) throws SVNException;
protected abstract int doCheckWC(File path, Level logLevel) throws SVNException;
protected static void registerFactory(SVNAdminAreaFactory factory) {
if (factory != null) {
ourFactories.add(factory);
}
}
public int compareTo(Object o) {
if (o == null || !(o instanceof SVNAdminAreaFactory)) {
return -1;
}
int version = ((SVNAdminAreaFactory) o).getSupportedVersion();
return getSupportedVersion() > version ? -1 : (getSupportedVersion() < version) ? 1 : 0;
}
private static class DefaultSelector implements ISVNAdminAreaFactorySelector {
public Collection getEnabledFactories(File path, Collection factories, boolean writeAccess) throws SVNException {
return factories;
}
}
private static void registerFactories() {
ourFactories.addAll(SVNClassLoader.getAvailableAdminAreaFactories());
}
}