blob: 2c30c89b5291da595221753b8772274c4cd0935d [file] [log] [blame]
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.lib.cvsclient.admin;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NonNls;
import org.netbeans.lib.cvsclient.CvsRoot;
import org.netbeans.lib.cvsclient.IClientEnvironment;
import org.netbeans.lib.cvsclient.SmartCvsSrcBundle;
import org.netbeans.lib.cvsclient.file.*;
import org.netbeans.lib.cvsclient.util.BugLog;
import java.io.*;
/**
* A handler for administrative information that maintains full compatibility
* with the one employed by the original C implementation of a CVS client.
* <p>This implementation strives to provide complete compatibility with
* the standard CVS client, so that operations on locally checked-out
* files can be carried out by either this library or the standard client
* without causing the other to fail. Any such failure should be considered
* a bug in this library.
*
* @author Robert Greig
*/
public final class AdminWriter implements IAdminWriter {
private final String myLineSeparator;
protected final String myCharset;
private final EntriesWriter myEntriesWriter;
// for tests only!
public static boolean WRITE_RELATIVE_PATHS = true;
@NonNls private static final String CVS_DIR_NAME = "CVS";
@NonNls private static final String TAG_FILE_NAME = "Tag";
@NonNls private static final String ENTRIES_STATIC_FILE_NAME = "Entries.Static";
@NonNls private static final String CVS_TEMPLATE_FILE_PATH = "CVS/Template";
@NonNls private static final String ROOT_FILE_NAME = "Root";
@NonNls private static final String REPOSITORY_FILE_NAME = "Repository";
@NonNls private static final String ENTRIES_FILE_NAME = "Entries";
@NonNls private static final String CVS_ROOT_FILE_PATH = "CVS/Root";
@NonNls private static final String CVS_REPOSITORY_FILE_PATH = "CVS/Repository";
@NonNls private static final String CVS_BASE_FILE_PATH = "CVS/Base/";
@NonNls private static final String CVS_BASEREV_FILE_PATH = "CVS/Baserev";
// Setup ==================================================================
public AdminWriter(String lineSeparator, final String charset, final EntriesWriter creator) {
myLineSeparator = lineSeparator;
myCharset = charset;
myEntriesWriter = creator;
}
public AdminWriter(String lineSeparator, final String charset) {
this(lineSeparator, charset, new SimpleEntriesWriter(charset, lineSeparator));
}
// Implemented ============================================================
public void ensureCvsDirectory(DirectoryObject directoryObject, String repositoryPath, CvsRoot cvsRoot, ICvsFileSystem cvsFileSystem)
throws IOException {
final File cvsDirectory = ensureCvsDirectory(directoryObject, cvsFileSystem);
// now ensure that the Root and Repository files exist
ensureExistingRootFile(cvsDirectory, cvsRoot);
ensureRepositoryFile(cvsDirectory, WRITE_RELATIVE_PATHS ? cvsFileSystem.getRelativeRepositoryPath(repositoryPath) : repositoryPath);
ensureExistingEntriesFile(cvsDirectory);
}
public void removeEntryForFile(AbstractFileObject fileObject, ICvsFileSystem cvsFileSystem) throws IOException {
final File file = cvsFileSystem.getAdminFileSystem().getFile(fileObject);
final File directory = file.getParentFile();
if (directory == null) {
throw new IOException(SmartCvsSrcBundle.message("file.does.not.have.a.parent.directory.error.message", file));
}
final EntriesHandler entriesHandler = new EntriesHandler(directory);
final boolean entriesUpdated = entriesHandler.read(myCharset);
final boolean entryRemoved = entriesHandler.getEntries().removeEntry(file.getName());
if (entriesUpdated || entryRemoved) {
entriesHandler.write(myLineSeparator, myCharset);
}
}
public void pruneDirectory(DirectoryObject directoryObject, ICvsFileSystem cvsFileSystem) {
deleteDirectoryRecursively(cvsFileSystem.getLocalFileSystem().getFile(directoryObject));
deleteDirectoryRecursively(cvsFileSystem.getAdminFileSystem().getFile(directoryObject));
}
public void setStickyTagForDirectory(DirectoryObject directoryObject, String tag, ICvsFileSystem cvsFileSystem) throws IOException {
final File cvsDirectory = new File(cvsFileSystem.getAdminFileSystem().getFile(directoryObject), CVS_DIR_NAME);
if (!cvsDirectory.isDirectory()) {
return;
}
final File tagFile = new File(cvsDirectory, TAG_FILE_NAME);
if (tag != null) {
FileUtils.writeLine(tagFile, tag);
}
else {
FileUtil.delete(tagFile);
}
}
public void editFile(FileObject fileObject, Entry entry, ICvsFileSystem cvsFileSystem, IFileReadOnlyHandler fileReadOnlyHandler)
throws IOException {
createBaserevEntry(fileObject, cvsFileSystem, entry);
final File localFile = cvsFileSystem.getLocalFileSystem().getFile(fileObject);
FileUtils.copyFile(localFile, getEditBackupFile(fileObject, cvsFileSystem));
fileReadOnlyHandler.setFileReadOnly(localFile, false);
}
public void uneditFile(FileObject fileObject, ICvsFileSystem cvsFileSystem, IFileReadOnlyHandler fileReadOnlyHandler) throws IOException {
final File editBackupFile = getEditBackupFile(fileObject, cvsFileSystem);
if (!editBackupFile.isFile()) {
return;
}
FileUtil.delete(editBackupFile);
removeBaserevEntry(fileObject, cvsFileSystem);
fileReadOnlyHandler.setFileReadOnly(cvsFileSystem.getLocalFileSystem().getFile(fileObject), true);
}
public void setEntriesDotStatic(DirectoryObject directoryObject, boolean set, ICvsFileSystem cvsFileSystem) throws IOException {
final File localDirectory = cvsFileSystem.getAdminFileSystem().getFile(directoryObject);
if (set) {
final File cvsDirectory = getCvsDirectoryForLocalDirectory(localDirectory);
if (cvsDirectory.exists()) {
final File staticFile = new File(cvsDirectory, ENTRIES_STATIC_FILE_NAME);
FileUtil.createIfDoesntExist(staticFile);
}
}
else {
final File entriesDotStaticFile = new File(getCvsDirectoryForLocalDirectory(localDirectory), ENTRIES_STATIC_FILE_NAME);
if (entriesDotStaticFile.exists()) {
FileUtil.delete(entriesDotStaticFile);
}
}
}
/**
* Set the Entry in the specified directory.
*
* @throws IOException if an error occurs writing the details
*/
public void setEntry(DirectoryObject directoryObject, Entry entry, ICvsFileSystem cvsFileSystem) throws IOException {
BugLog.getInstance().assertNotNull(entry);
final File directory = cvsFileSystem.getAdminFileSystem().getFile(directoryObject);
myEntriesWriter.addEntry(directory, entry);
}
public void writeTemplateFile(DirectoryObject directoryObject,
int fileLength,
InputStream inputStream,
IReaderFactory readerFactory,
IClientEnvironment clientEnvironment) throws IOException {
final FileObject fileObject = FileObject.createInstance(directoryObject, CVS_TEMPLATE_FILE_PATH);
final IFileSystem adminFileSystem = clientEnvironment.getCvsFileSystem().getAdminFileSystem();
if (fileLength == 0) {
FileUtil.delete(adminFileSystem.getFile(fileObject));
return;
}
clientEnvironment.getLocalFileWriter()
.writeTextFile(fileObject, fileLength, inputStream, false, readerFactory, clientEnvironment.getFileReadOnlyHandler(), adminFileSystem,
null);
}
public void directoryAdded(DirectoryObject directoryObject, ICvsFileSystem cvsFileSystem) throws IOException {
final DirectoryObject parent = directoryObject.getParent();
final String cvsRoot = getCvsRoot(parent, cvsFileSystem);
final String repositoryPath = getRepository(parent, cvsFileSystem) + '/' + directoryObject.getName();
final String stickyTag = AdminUtils.getStickyTagForDirectory(parent, cvsFileSystem);
final File cvsDirectory = ensureCvsDirectory(directoryObject, cvsFileSystem);
// now ensure that the Root and Repository files exist
FileUtils.writeLine(new File(cvsDirectory, ROOT_FILE_NAME), cvsRoot);
FileUtils.writeLine(new File(cvsDirectory, REPOSITORY_FILE_NAME), repositoryPath);
new Entries().write(new File(cvsDirectory, ENTRIES_FILE_NAME), myLineSeparator, myCharset);
setStickyTagForDirectory(directoryObject, stickyTag, cvsFileSystem);
addDirectoryToParentEntriesFile(cvsDirectory.getParentFile());
}
// Utils ==================================================================
private String getCvsRoot(DirectoryObject directoryObject, ICvsFileSystem cvsFileSystem) throws IOException {
final File cvsRootFile = cvsFileSystem.getAdminFileSystem().getFile(FileObject.createInstance(directoryObject, CVS_ROOT_FILE_PATH));
return FileUtils.readLineFromFile(cvsRootFile);
}
private String getRepository(DirectoryObject directoryObject, ICvsFileSystem cvsFileSystem) throws IOException {
final File cvsRootFile =
cvsFileSystem.getAdminFileSystem().getFile(FileObject.createInstance(directoryObject, CVS_REPOSITORY_FILE_PATH));
return FileUtils.readLineFromFile(cvsRootFile);
}
private File getCvsDirectoryForLocalDirectory(File directory) {
return new File(directory, CVS_DIR_NAME);
}
private void deleteDirectoryRecursively(File directory) {
final File[] files = directory.listFiles();
if (files == null) {
return;
}
for (int i = 0; i < files.length; i++) {
final File file = files[i];
if (file.isDirectory()) {
deleteDirectoryRecursively(file);
}
else {
FileUtil.delete(file);
}
}
FileUtil.delete(directory);
}
private File ensureCvsDirectory(DirectoryObject directoryObject, ICvsFileSystem cvsFileSystem) {
final File cvsDirectory = new File(cvsFileSystem.getAdminFileSystem().getFile(directoryObject), CVS_DIR_NAME);
cvsDirectory.mkdirs();
return cvsDirectory;
}
private void ensureExistingRootFile(File cvsDirectory, CvsRoot cvsRoot) throws IOException {
final File rootFile = new File(cvsDirectory, ROOT_FILE_NAME);
if (rootFile.exists()) {
return;
}
FileUtils.writeLine(rootFile, cvsRoot.getCvsRoot());
}
private void ensureRepositoryFile(File cvsDirectory, String repositoryPath) throws IOException {
final File repositoryFile = new File(cvsDirectory, REPOSITORY_FILE_NAME);
if (repositoryFile.exists()) {
return;
}
FileUtils.writeLine(repositoryFile, repositoryPath);
}
private void ensureExistingEntriesFile(File cvsDirectory) throws IOException {
final File entriesFile = new File(cvsDirectory, ENTRIES_FILE_NAME);
if (entriesFile.exists()) {
return;
}
new Entries().write(entriesFile, myLineSeparator, myCharset);
// need to know if we had to addEntry any directories so that we can
// update the CVS/Entries file in the *parent* director
addDirectoryToParentEntriesFile(cvsDirectory.getParentFile());
}
private void addDirectoryToParentEntriesFile(File directory) throws IOException {
try {
myEntriesWriter.addEntry(directory.getParentFile(), Entry.createDirectoryEntry(directory.getName()));
}
catch (FileNotFoundException ex) {
// The Entries file will not exist in the case where this is the top level of the module
}
}
private void createBaserevEntry(FileObject fileObject, ICvsFileSystem cvsFileSystem, Entry entry) throws IOException {
if (entry == null || entry.getRevision() == null || entry.isAddedFile() || entry.isRemoved()) {
throw new IllegalArgumentException("File does not have an Entry or Entry is invalid!");
}
final File file = cvsFileSystem.getAdminFileSystem().getFile(fileObject);
final File baserevFile = new File(file.getParentFile(), CVS_BASEREV_FILE_PATH);
final File backupFile = new File(baserevFile.getAbsolutePath() + '~');
BufferedReader reader = null;
BufferedWriter writer = null;
boolean append = true;
boolean writeFailed = true;
final String entryStart = 'B' + file.getName() + '/';
try {
writer = new BufferedWriter(new FileWriter(backupFile));
writeFailed = false;
reader = new BufferedReader(new FileReader(baserevFile));
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
if (line.startsWith(entryStart)) {
append = false;
}
writeFailed = true;
writer.write(line);
writer.newLine();
writeFailed = false;
}
}
catch (IOException ex) {
if (writeFailed) {
throw ex;
}
}
finally {
if (reader != null) {
try {
reader.close();
}
catch (IOException ex) {
// ignore
}
}
if (writer != null) {
if (append && !writeFailed) {
writer.write(entryStart + entry.getRevision() + '/');
writer.newLine();
}
try {
writer.close();
}
catch (IOException ex) {
// ignore
}
}
}
FileUtil.delete(baserevFile);
FileUtil.rename(backupFile, baserevFile);
}
private void removeBaserevEntry(FileObject fileObject, ICvsFileSystem cvsFileSystem) throws IOException {
final File file = cvsFileSystem.getAdminFileSystem().getFile(fileObject);
final File baserevFile = new File(file.getParentFile(), CVS_BASEREV_FILE_PATH);
final File backupFile = new File(baserevFile.getAbsolutePath() + '~');
BufferedReader reader = null;
BufferedWriter writer = null;
final String entryStart = 'B' + file.getName() + '/';
try {
writer = new BufferedWriter(new FileWriter(backupFile));
reader = new BufferedReader(new FileReader(baserevFile));
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
if (line.startsWith(entryStart)) {
continue;
}
writer.write(line);
writer.newLine();
}
}
catch (FileNotFoundException ex) {
// ignore
}
finally {
if (writer != null) {
try {
writer.close();
}
catch (IOException ex) {
// ignore
}
}
if (reader != null) {
try {
reader.close();
}
catch (IOException ex) {
// ignore
}
}
}
FileUtil.delete(baserevFile);
if (backupFile.length() > 0) {
FileUtil.rename(backupFile, baserevFile);
}
else {
FileUtil.delete(backupFile);
}
}
private File getEditBackupFile(FileObject fileObject, ICvsFileSystem cvsFileSystem) {
final File file = cvsFileSystem.getAdminFileSystem().getFile(fileObject);
return new File(file.getParentFile(), CVS_BASE_FILE_PATH + file.getName());
}
private static class SimpleEntriesWriter implements EntriesWriter {
private final String myCharset;
private final String myLineSeparator;
private SimpleEntriesWriter(final String charset, final String lineSeparator) {
myCharset = charset;
myLineSeparator = lineSeparator;
}
public void addEntry(final File directory, final Entry entry) throws IOException {
final EntriesHandler entriesHandler = new EntriesHandler(directory);
entriesHandler.read(myCharset);
entriesHandler.getEntries().addEntry(entry);
entriesHandler.write(myLineSeparator, myCharset);
}
}
}