| /* |
| * 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); |
| } |
| } |
| |
| } |