// Copyright 2004-2014 Jim Voris // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // $Filename: C:\qRoot\testQVCSWorkfiles\Remote Secure Java Project\QumaProjects\com\qumasoft\client\TestCheckOutArchive.java $ // $Date: Wednesday, February 12, 2003 10:02:22 PM $ // $Revision: 1.57 $ // // $Log5: C:\qRoot\testQVCSArchives\Remote Secure Java Project\QumaProjects\com\qumasoft\client\TestCheckOutArchive.kbwb $ // // LogfileImpl class. This is the main class for operating on a QVCS // archive. // // Revision 1.57 by: JimVoris Rev date: 2/12/2003 10:02:39 PM // Remove unneeded import statements. // // Revision 1.56 by: JimVoris Rev date: 2/2/2003 6:09:14 PM // Add functionality for unlock toolbar button. // // Revision 1.55 by: JimVoris Rev date: 2/1/2003 11:30:00 AM // Make the output file read/write so we can overwrite it. // // Revision 1.54 by: JimVoris Rev date: 1/8/2003 7:42:14 PM // Add getIsObsolete() method. // // Revision 1.53 by: JimVoris Rev date: 1/5/2003 5:03:59 PM // Generalize the static conversion methods so they should work with either forward or backslash characters a the pathSeparator. // // $Endlog$ // // $Copyright Insert your copyright information here. $ package TestFiles; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.RandomAccessFile; import java.io.FileNotFoundException; import java.io.File; import java.util.logging.Logger; import java.util.logging.Level; import com.qumasoft.operations.CompareFilesEditInformation; import com.qumasoft.qvcslib.AbstractProjectProperties; import com.qumasoft.qvcslib.AccessList; import com.qumasoft.qvcslib.ArchiveAttributes; import com.qumasoft.qvcslib.CompressionFactory; import com.qumasoft.qvcslib.Compressor; import com.qumasoft.qvcslib.LogFileHeaderInfo; import com.qumasoft.qvcslib.LogFileOperationCheckInCommandArgs; import com.qumasoft.qvcslib.LogFileOperationCheckOutCommandArgs; import com.qumasoft.qvcslib.LogFileOperationCreateArchiveCommandArgs; import com.qumasoft.qvcslib.LogFileOperationGetRevisionCommandArgs; import com.qumasoft.qvcslib.LogFileOperationLockRevisionCommandArgs; import com.qumasoft.qvcslib.LogFileOperationUnlockRevisionCommandArgs; import com.qumasoft.qvcslib.LogfileInfo; import com.qumasoft.qvcslib.MutableByteArray; import com.qumasoft.qvcslib.MutableInteger; import com.qumasoft.qvcslib.MutableString; import com.qumasoft.qvcslib.QVCSConstants; import com.qumasoft.qvcslib.QVCSException; import com.qumasoft.qvcslib.QumaAssert; import com.qumasoft.qvcslib.RevisionDescriptor; import com.qumasoft.qvcslib.RevisionHeader; import com.qumasoft.qvcslib.RevisionInformation; import com.qumasoft.qvcslib.WorkFile; class LogFileImpl { // Create our logger object private static Logger m_logger = Logger.getLogger("com.qumasoft.qvcslib"); /** define the size of the buffer we use for file copies */ private static final int FILE_COPY_BUFFER_SIZE = 32000; private RandomAccessFile m_inStream; /** * Flag to indicate if file is already open */ private boolean m_isOpen; /** Flag to indicate whether the header information has been read */ private boolean m_isHeaderInfoRead; private boolean m_isRevisionInfoRead; private RevisionInformation m_RevisionInfo; private String m_FileName; private String m_ShortArchiveName; private java.io.File m_File; private String m_TempFileName; private java.io.File m_TempFile; private String m_OldFileName; private java.io.File m_OldFile; private String m_ShortWorkfileName; private LogFileHeaderInfo m_HeaderInfo; private MutableByteArray m_ProcessedBuffer = new MutableByteArray(); private CompressionFactory m_CompressionFactory = new CompressionFactory(); private AccessList m_AccessList = null; private AccessList m_ModifierList = null; private boolean m_MustReadArchiveFile = true; private LogfileInfo m_LogfileInfo = null; RevisionHeader getRevisionHeader(int index) { if (index < getRevisionCount()) { return m_RevisionInfo.getRevisionHeader(index); } else { return null; } } RevisionInformation getRevisionInformation() { if (!isArchiveInformationRead()) { readInformation(); } return m_RevisionInfo; } LogfileInfo getLogfileInfo() { if (m_LogfileInfo == null) { m_LogfileInfo = new LogfileInfo(getLogFileHeaderInfo(), getRevisionInformation(), getFileName()); } return m_LogfileInfo; } boolean isArchiveInformationRead() { return m_isHeaderInfoRead && m_isRevisionInfoRead; } boolean isValidArchive() { if (!isArchiveInformationRead()) { readInformation(); } return isArchiveInformationRead(); } boolean getIsObsolete() { if (!isArchiveInformationRead()) { readInformation(); } return m_HeaderInfo.getIsObsolete(); } int getRevisionCount() { if (!isArchiveInformationRead()) { readInformation(); } return m_HeaderInfo.getRevisionCount(); } String getShortWorkfileName() { return m_ShortWorkfileName; } String getShortArchiveName() { return m_ShortArchiveName; } int getLockCount() { if (!isArchiveInformationRead()) { readInformation(); } return m_HeaderInfo.getLogFileHeader().lockCount(); } File getFile() { return m_File; } String getFileName() { return m_FileName; } File getOldFile() { return m_OldFile; } String getOldFileName() { return m_OldFileName; } File getTempFile() { return m_TempFile; } String getTempFileName() { return m_TempFileName; } String getLockedByString() { return "Unknown"; } /** * Return the user name(s) that hold any locks on this archive. The format of the * returned string is for use within the GUI. If there are multiple lockers, they are * all returned. */ String getLockedByUser() { String returnString; if (!isArchiveInformationRead()) { readInformation(); } if (isArchiveInformationRead()) { if (m_HeaderInfo.getLogFileHeader().lockCount() > 0) { StringBuffer lockerString = new StringBuffer(); int revisionCount = m_HeaderInfo.getRevisionCount(); int lockCount = m_HeaderInfo.getLogFileHeader().lockCount(); for (int i = 0, j = 0; (i < revisionCount) && (j < lockCount); i++) { RevisionHeader revHeader = m_RevisionInfo.getRevisionHeader(i); if (revHeader.isLocked()) { j++; lockerString.append(revHeader.getRevisionString() + "-" + indexToUsername(revHeader.getLockerIndex())); } } returnString = lockerString.toString(); } else { returnString = ""; } } else { returnString = ""; } return returnString; } String getWorkfileInLocation() { if (!isArchiveInformationRead()) { readInformation(); } String returnString = ""; if (m_HeaderInfo.getLogFileHeader().lockCount() > 0) { returnString = m_HeaderInfo.getWorkfileName(); } return returnString; } java.util.Date getLastCheckInDate() { RevisionHeader defaultRevision = getDefaultRevisionHeader(); return defaultRevision.getCheckInDate(); } String getLastEditBy() { RevisionHeader defaultRevision = getDefaultRevisionHeader(); return indexToUsername(defaultRevision.getCreatorIndex()); } RevisionHeader getDefaultRevisionHeader() { RevisionHeader returnHeader = null; if (!isArchiveInformationRead()) { readInformation(); } if (isArchiveInformationRead()) { // If the default branch isn't the trunk, then we have some work to do... if (m_HeaderInfo.getLogFileHeader().defaultDepth() > 0) { int revisionCount = m_HeaderInfo.getRevisionCount(); RevisionDescriptor defaultDescriptor = m_HeaderInfo.getDefaultRevisionDescriptor(); String defaultBranchString = defaultDescriptor.toString(); for (int i = 0; i < revisionCount; i++) { RevisionHeader revHeader = m_RevisionInfo.getRevisionHeader(i); if (revHeader.getDepth() == defaultDescriptor.getElementCount() - 1) { if (revHeader.isTip()) { String revisionString = revHeader.getRevisionString(); int lastDot = revisionString.lastIndexOf('.'); String truncatedRevisionString = revisionString.substring(0, lastDot); if (truncatedRevisionString.compareToIgnoreCase(defaultBranchString) == 0) { returnHeader = revHeader; break; } } } } } else { // The default branch is the trunk. Things are simple here. returnHeader = m_RevisionInfo.getRevisionHeader(0); } } else { returnHeader = null; } return returnHeader; } String indexToUsername(int index) { if (!isArchiveInformationRead()) { readInformation(); } return m_ModifierList.indexToUser(index); } /** * This recursive routine will fetch the requested revision into the output file. * This routine does handle compressed revisions, but it does not perform keyword * expansion. */ boolean fetchRevision(RevisionHeader revisionHeader, String outputFilename, boolean recurseFlag, MutableByteArray processedBuffer) { boolean bRetVal = false; boolean streamIsOpen = false; BufferedOutputStream outStream = null; try { if ((revisionHeader.getDepth() == 0) && revisionHeader.isTip()) { // Read the archive file to retrieve the revision. byte[] unCompressedRevisionData; byte[] revisionData = new byte[(int)revisionHeader.getRevisionSize()]; m_inStream.seek(revisionHeader.getRevisionDataStartPosition()); m_inStream.read(revisionData); // Decompress the buffer if we need to. if (revisionHeader.isCompressed()) { unCompressedRevisionData = deCompressRevisionData(revisionHeader, revisionData); } else { unCompressedRevisionData = revisionData; } processedBuffer.setValue(unCompressedRevisionData); revisionData = null; bRetVal = true; } else { // They are requesting an older revision. if (revisionHeader.getParentRevisionHeader() == null) { // We've reached the tip revision for the requested revision. // but we're recursing and this must be a non-tip revision. // All non-tip revisions MUST have a parent, so to get here // is a boo-boo. QumaAssert.isTrue(false); } else { // We're still working our way to the tip revision. bRetVal = fetchRevision(revisionHeader.getParentRevisionHeader(), outputFilename, true, processedBuffer); // We got our parent. Now apply our edits to our parent to get the result. if (bRetVal) { byte[] editBuffer = new byte[(int)revisionHeader.getRevisionSize()]; byte[] uncompressedEditBuffer; m_inStream.seek(revisionHeader.getRevisionDataStartPosition()); m_inStream.read(editBuffer); if (revisionHeader.isCompressed()) { uncompressedEditBuffer = deCompressRevisionData(revisionHeader, editBuffer); } else { uncompressedEditBuffer = editBuffer; } byte[] afterEdits = applyEdits(uncompressedEditBuffer, processedBuffer.getValue()); processedBuffer.setValue(afterEdits); editBuffer = null; uncompressedEditBuffer = null; bRetVal = true; } } } } catch (Exception e) { m_logger.log(Level.WARNING, "Failed to fetch revision: " + revisionHeader.getRevisionString()); m_logger.log(Level.WARNING, e.getLocalizedMessage()); e.printStackTrace(); bRetVal = false; } // Write the result. if (!recurseFlag && bRetVal) { try { WorkFile outputFile = new WorkFile(outputFilename); // Overwrite if it is write protected. if (!outputFile.canWrite()) { outputFile.setReadWrite(); } outStream = new BufferedOutputStream(new FileOutputStream(outputFile)); streamIsOpen = true; outStream.write(processedBuffer.getValue()); } catch (Exception e) { e.printStackTrace(); bRetVal = false; } finally { if (streamIsOpen) { try { outStream.close(); } catch (Exception e) { e.printStackTrace(); } } } } return bRetVal; } private byte[] applyEdits(byte[] edits, byte[] originalData) throws QVCSException { DataInputStream editStream = new DataInputStream(new ByteArrayInputStream(edits)); CompareFilesEditInformation editInfo = new CompareFilesEditInformation(); byte[] editedBuffer = new byte[edits.length + originalData.length]; // It can't be any bigger than this. byte[] returnedBuffer = null; int inIndex = 0; int outIndex = 0; int deletedBytesCount = 0; int insertedBytesCount = 0; int bytesTillChange = 0; try { while(editStream.available() > 0) { editInfo.read(editStream); bytesTillChange = (int)editInfo.getSeekPosition() - inIndex; System.arraycopy(originalData, inIndex, editedBuffer, outIndex, bytesTillChange); inIndex += bytesTillChange; outIndex += bytesTillChange; deletedBytesCount = (int)editInfo.getDeletedBytesCount(); insertedBytesCount = (int)editInfo.getInsertedBytesCount(); switch (editInfo.getEditType()) { case CompareFilesEditInformation.qvcsEDIT_DELETE: /* Delete input */ { // Just skip over deleted bytes inIndex += deletedBytesCount; break; } case CompareFilesEditInformation.qvcsEDIT_INSERT: /* Insert edit lines */ { editStream.read(editedBuffer, outIndex, insertedBytesCount); outIndex += insertedBytesCount; break; } case CompareFilesEditInformation.qvcsEDIT_REPLACE: /* Replace input line with edit line */ { /* * First skip over the bytes to be replaced, then copy * the replacing bytes from the edit file to the output file. */ inIndex += deletedBytesCount; editStream.read(editedBuffer, outIndex, insertedBytesCount); outIndex += insertedBytesCount; break; } default: { continue; } } } // Copy the rest of the input "file" to the output "file". int remainingBytes = originalData.length - inIndex; if (remainingBytes > 0) { System.arraycopy(originalData, inIndex, editedBuffer, outIndex, remainingBytes); outIndex += remainingBytes; } returnedBuffer = new byte[outIndex]; System.arraycopy(editedBuffer, 0, returnedBuffer, 0, outIndex); } catch (Exception e) { e.printStackTrace(); m_logger.log(Level.WARNING, " editInfo.seekPosition: " + editInfo.getSeekPosition() + " originalData.length: " + originalData.length + " inIndex: " + inIndex + " editedBuffer.length: " + editedBuffer.length + " outIndex: " + outIndex + " bytesTillChange: " + bytesTillChange); m_logger.log(Level.WARNING, e.getLocalizedMessage()); throw new QVCSException("Internal error!! for " + getShortWorkfileName()); } finally { editedBuffer = null; editInfo = null; try { editStream.close(); } catch (Exception e) { e.printStackTrace(); } } return returnedBuffer; } /** * Find the requested revision within the archive. Return true if we find * that revision; also return the index of that revision. If the revision * is not found, return false. */ boolean findRevision(String revision, MutableInteger revisionIndex) { QumaAssert.isTrue(m_isHeaderInfoRead); QumaAssert.isTrue(m_isRevisionInfoRead); boolean bRetVal = false; int revisionCount = m_HeaderInfo.getRevisionCount(); for (int i = 0; i < revisionCount; i++) { RevisionHeader revHeader = m_RevisionInfo.getRevisionHeader(i); String revisionString = revHeader.getRevisionString(); if (revision.compareTo(revisionString) == 0) { bRetVal = true; revisionIndex.setValue(i); break; } } return bRetVal; } private byte[] deCompressRevisionData(RevisionHeader revisionHeader, byte[] revisionData) { Compressor compressor = m_CompressionFactory.getCompressor(revisionHeader.getCompressionHeader()); return compressor.expand(revisionHeader.getCompressionHeader(), revisionData); } @Override public String toString() { String returnString = new String(); returnString = "QVCS archive name:\t" + m_FileName; returnString += m_HeaderInfo.toString(); return returnString; } public LogFileImpl(String fullArchiveFilename) { m_isOpen = false; m_isHeaderInfoRead = false; m_isRevisionInfoRead = false; // Set the filename m_FileName = fullArchiveFilename; m_File = new File(m_FileName); // Set the name of the temp file for the archive m_TempFileName = new String (m_FileName + ".temp"); m_TempFile = new java.io.File(m_TempFileName); // Set the name of the old file for the archive m_OldFileName = new String (m_FileName + ".old"); m_OldFile = new java.io.File(m_OldFileName); // Figure out the short workfile name m_ShortWorkfileName = convertArchiveNameToShortWorkfileName(fullArchiveFilename, File.separator); // Figure out the short archive name m_ShortArchiveName = convertWorkfileNameToShortArchiveName(m_ShortWorkfileName); } private static String deducePathSeparator(String filename) { String pathSeparator = "/"; // Search for a path separator.... it might be '\', or it might be '/' int pathSeparatorCandidateForwardSlashIndex = filename.indexOf('/'); int pathSeparatorCandidateBackSlashIndex = filename.indexOf('\\'); if (pathSeparatorCandidateForwardSlashIndex > -1) { pathSeparator = "/"; } else if (pathSeparatorCandidateBackSlashIndex > -1) { pathSeparator = "\\"; } else { pathSeparator = "/"; } return pathSeparator; } static String convertArchiveNameToShortWorkfileName(String archiveName, String pathSeparator) { String nameSeparator = new String("."); pathSeparator = deducePathSeparator(archiveName); // Search the archiveName backwards, looking for the path separator. int pathSeparatorIndex = archiveName.lastIndexOf(pathSeparator); // Make a StringBuffer we'll use for changing the name StringBuffer tempWorkfileName = new StringBuffer(archiveName); // Search the archiveName backwards, looking for the name separator. int nameSeparatorIndex = archiveName.lastIndexOf(nameSeparator); if (nameSeparatorIndex != -1) { String extension = archiveName.substring(nameSeparatorIndex); if (0 == extension.compareTo(".____")) { // Strip off the fake extension to create the workfile name. tempWorkfileName.setLength(archiveName.length() - 4); } else { int lastIndex = archiveName.length() - 1; int extensionIndex = nameSeparatorIndex + 1; for (;extensionIndex <= lastIndex; extensionIndex++) { switch (archiveName.charAt(extensionIndex)) { case '?': case '*': case '_': case '^': case '~': case '!': case '-': case '{': case '\'': { break; } case 'a': { tempWorkfileName.setCharAt(extensionIndex,'z'); break; } case 'A': { tempWorkfileName.setCharAt(extensionIndex,'Z'); break; } case '0': { tempWorkfileName.setCharAt(extensionIndex,'9'); break; } default: { tempWorkfileName.setCharAt(extensionIndex, (char)(archiveName.charAt(extensionIndex) - 1)); break; } } } } } String returnString = new String(tempWorkfileName); if (pathSeparatorIndex != -1) { returnString = returnString.substring(pathSeparatorIndex + 1); } return returnString; } static String convertWorkfileNameToShortArchiveName(String workfileName) { String pathSeparator = deducePathSeparator(workfileName); String nameSeparator = new String("."); // Search the workfileName backwards, looking for the path separator. int pathSeparatorIndex = workfileName.lastIndexOf(pathSeparator); // Make a StringBuffer we'll use for changing the name StringBuffer tempArchiveName = new StringBuffer(workfileName); // Search the archiveName backwards, looking for the name separator. int nameSeparatorIndex = workfileName.lastIndexOf(nameSeparator); if (nameSeparatorIndex != -1) { String extension = workfileName.substring(nameSeparatorIndex); if (extension.length() == 0) { // Append the "___" to the workfile name to create the archive name. tempArchiveName.append("___"); } else { int lastIndex = workfileName.length() - 1; int extensionIndex = nameSeparatorIndex + 1; for (;extensionIndex <= lastIndex; extensionIndex++) { switch (workfileName.charAt(extensionIndex)) { case '?': case '*': case '_': case '^': case '~': case '!': case '-': case '{': case '\'': { break; } case 'z': { tempArchiveName.setCharAt(extensionIndex,'a'); break; } case 'Z': { tempArchiveName.setCharAt(extensionIndex,'A'); break; } case '9': { tempArchiveName.setCharAt(extensionIndex,'0'); break; } default: { tempArchiveName.setCharAt(extensionIndex, (char)(workfileName.charAt(extensionIndex) + 1)); break; } } } } } else { // Append the ".___" to the workfile name to create the archive name. tempArchiveName.append(".___"); } String returnString = new String(tempArchiveName); if (pathSeparatorIndex != -1) { returnString = returnString.substring(pathSeparatorIndex + 1); } return returnString; } static String convertWorkfileNameToShortWorkfileName(String workfileName) { String retVal = workfileName; byte[] bytes = workfileName.getBytes(); int index = -1; // We cannot use File.separator since the client may not use the // same separator as the server. for (int i = bytes.length - 1; i >= 0; i--) { if (bytes[i] == '/' || bytes[i] == '\\') { index = i; break; } } if (index != -1) { retVal = workfileName.substring(index + 1); } return retVal; } /** * open the archive file. */ synchronized boolean open() { if (!m_isOpen) { // Make sure the file exists if (m_File.exists()) { try { m_inStream = new RandomAccessFile(m_File, "r"); m_isOpen = true; } catch (FileNotFoundException e) { m_isOpen = false; e.printStackTrace(); } catch (Exception e) { m_isOpen = false; e.printStackTrace(); } } } return m_isOpen; } protected synchronized void close() { if (m_isOpen) { try { m_inStream.close(); m_isOpen = false; } catch (Exception e) { e.printStackTrace(); } } } boolean reReadInformation() { m_isHeaderInfoRead = false; m_isRevisionInfoRead = false; m_LogfileInfo = null; return readInformation(); } boolean readInformation() { boolean bRetValue = true; try { if (open()) { bRetValue = readHeaderInformation(); if (bRetValue) { bRetValue = readRevisionInformation(); } } } catch (Exception e) { bRetValue = false; } finally { close(); } return bRetValue; } /** * read revision information for this archive */ private boolean readRevisionInformation() { try { if (open() && !m_isHeaderInfoRead) { m_HeaderInfo = new LogFileHeaderInfo(); m_HeaderInfo.read(m_inStream); } if (m_isHeaderInfoRead && !m_isRevisionInfoRead) { m_RevisionInfo = new RevisionInformation(m_HeaderInfo.getRevisionCount(), new AccessList(m_HeaderInfo.getAccessList()), new AccessList(m_HeaderInfo.getModifierList())); m_RevisionInfo.read(m_inStream); m_isRevisionInfoRead = true; } } catch (Exception e) { m_isRevisionInfoRead = false; } return m_isRevisionInfoRead; } /** * read header information for this archive */ private boolean readHeaderInformation() { try { if (open() && !m_isHeaderInfoRead) { m_HeaderInfo = new LogFileHeaderInfo(); m_isHeaderInfoRead = m_HeaderInfo.read(m_inStream); if (m_isHeaderInfoRead) { m_AccessList = new AccessList(m_HeaderInfo.getAccessList()); m_ModifierList = new AccessList(m_HeaderInfo.getModifierList()); } } } catch (Exception e) { m_isHeaderInfoRead = false; } return m_isHeaderInfoRead; } boolean getTipRevision(String fetchToFileName) throws QVCSException { LogFileOperationGetRevisionCommandArgs commandArgs = new LogFileOperationGetRevisionCommandArgs(); commandArgs.setRevisionString(QVCSConstants.QVCS_DEFAULT_REVISION); commandArgs.setShortWorkfileName(getShortWorkfileName()); commandArgs.setOutputFileName(fetchToFileName); boolean retVal = false; Object[] args = new Object[3]; args[0] = this; args[1] = fetchToFileName; args[2] = commandArgs; return retVal; } LogFileHeaderInfo getLogFileHeaderInfo() { if (!isArchiveInformationRead()) { readInformation(); } return m_HeaderInfo; } void setHeaderInfo(LogFileHeaderInfo headerInfo) throws QVCSException { if (isArchiveInformationRead()) { throw new QVCSException("Invalid use of setHeaderInfo method!!"); } else { m_HeaderInfo = headerInfo; m_MustReadArchiveFile = false; } } boolean createArchive(LogFileOperationCreateArchiveCommandArgs commandLineArgs, AbstractProjectProperties projectProperties, String filename) throws QVCSException { boolean retVal = false; Object[] args = new Object[4]; args[0] = this; args[1] = filename; args[2] = projectProperties; args[3] = commandLineArgs; return retVal; } /** * Get a revision from the archive file and write it into the * filename provided. Return true if successful, false otherwise. * Keywords are NOT expanded by this method. * */ boolean getRevision(LogFileOperationGetRevisionCommandArgs commandLineArgs, String fetchToFileName) throws QVCSException { boolean retVal = false; Object[] args = new Object[3]; args[0] = this; args[1] = fetchToFileName; args[2] = commandLineArgs; return retVal; } boolean checkOutRevision(LogFileOperationCheckOutCommandArgs commandLineArgs, String fetchToFileName) throws QVCSException { boolean retVal = false; Object[] args = new Object[3]; args[0] = this; args[1] = fetchToFileName; args[2] = commandLineArgs; return retVal; } boolean lockRevision(LogFileOperationLockRevisionCommandArgs commandLineArgs) throws QVCSException { boolean retVal = false; Object[] args = new Object[2]; args[0] = this; args[1] = commandLineArgs; return retVal; } boolean checkInRevision(LogFileOperationCheckInCommandArgs commandLineArgs, String filename) throws QVCSException { boolean retVal = false; Object[] args = new Object[3]; args[0] = this; args[1] = filename; args[2] = commandLineArgs; return retVal; } boolean unlockRevision(LogFileOperationUnlockRevisionCommandArgs commandLineArgs) throws QVCSException { boolean retVal = false; Object[] args = new Object[2]; args[0] = this; args[1] = commandLineArgs; return retVal; } boolean unlockRevision(String userName, MutableString revisionString) throws QVCSException { boolean retVal = false; // Make sure user is on the access list. if (!isOnAccessList(userName)) { throw new QVCSException(userName + " is not on the access list for " + getShortWorkfileName()); } // Figure out the default revision string if we need to. if (0 == revisionString.getValue().compareTo(QVCSConstants.QVCS_DEFAULT_REVISION)) { revisionString.setValue(getDefaultRevisionHeader().getRevisionString()); } // Make sure the revision is already locked. MutableInteger revisionIndex = new MutableInteger(); if (!isRevisionLocked(revisionString, revisionIndex)) { throw new QVCSException("Revision " + revisionString + " is not locked for " + getShortWorkfileName()); } // Make sure the current user holds the lock on the given revision. if (m_ModifierList.userToIndex(userName) != getRevisionHeader(revisionIndex.getValue()).getLockerIndex()) { throw new QVCSException("Revision " + revisionString + " for " + getShortWorkfileName() + " is not locked by " + userName); } // Make a copy of the archive. We'll operate on this copy. if (false == createCopyOfArchive()) { throw new QVCSException("Unable to create temporary copy of archive for " + getShortWorkfileName()); } java.io.RandomAccessFile ioStream = null; try { ioStream = new java.io.RandomAccessFile(m_TempFile, "rw"); // Seek to the location of this revision in the file. RevisionHeader revInfo = getRevisionHeader(revisionIndex.getValue()); ioStream.seek(revInfo.getRevisionStartPosition()); // Update the header with info about this locker. getLogFileHeaderInfo().getLogFileHeader().decrementLockCount(); // Update the header information in the stream. getLogFileHeaderInfo().updateInPlace(ioStream); } catch (Exception e) { e.printStackTrace(); retVal = false; } finally { try { if (ioStream != null) { ioStream.close(); } } catch (Exception e) { e.printStackTrace(); retVal = false; } } // Remove any old archives. m_OldFile.delete(); if (m_File.renameTo(m_OldFile)) { if (m_TempFile.renameTo(m_File)) { retVal = true; m_OldFile.delete(); } else { m_OldFile.renameTo(m_File); throw new QVCSException("Unable to rename temporary copy of archive for " + getShortWorkfileName()); } } else { throw new QVCSException("Unable to rename archive file for " + getShortWorkfileName()); } return retVal; } AccessList getAccessList() { if (!isArchiveInformationRead()) { readInformation(); } return m_AccessList; } AccessList getModifierList() { if (!isArchiveInformationRead()) { readInformation(); } return m_ModifierList; } boolean isOnAccessList(String userName) { return false; } boolean isRevisionLocked (MutableString revisionString, MutableInteger revisionIndex) throws QVCSException { boolean retVal = true; // Figure out the default revision string if we need to. if (0 == revisionString.getValue().compareTo(QVCSConstants.QVCS_DEFAULT_REVISION)) { revisionString.setValue(getDefaultRevisionHeader().getRevisionString()); } if (findRevision(revisionString.getValue(), revisionIndex)) { RevisionHeader revInfo = getRevisionHeader(revisionIndex.getValue()); retVal = revInfo.isLocked(); } else { throw new QVCSException("Revision " + revisionString.getValue() + " not found in " + getShortWorkfileName()); } return retVal; } String getLockedRevisionString(String userName) { String retVal = null; RevisionInformation revisionInformation = getRevisionInformation(); AccessList modifierList = new AccessList("Ralph,Joe"); int revisionCount = getLogFileHeaderInfo().getRevisionCount(); for (int i = 0; i < revisionCount; i++) { RevisionHeader revHeader = revisionInformation.getRevisionHeader(i); if (revHeader.isLocked()) { String lockerName = modifierList.indexToUser(revHeader.getLockerIndex()); if (0 == lockerName.compareTo(userName)) { retVal = revHeader.getRevisionString(); break; } } } return retVal; } /** * Determine whether the given user holds any locks for this archive. */ boolean isLockedByUser (String userName) { boolean retVal = false; if (!isArchiveInformationRead()) { readInformation(); } if (isArchiveInformationRead()) { if (m_HeaderInfo.getLogFileHeader().lockCount() > 0) { int revisionCount = m_HeaderInfo.getRevisionCount(); int lockCount = m_HeaderInfo.getLogFileHeader().lockCount(); for (int i = 0, j = 0; (i < revisionCount) && (j < lockCount); i++) { RevisionHeader revHeader = m_RevisionInfo.getRevisionHeader(i); if (revHeader.isLocked()) { j++; if (0 == userName.compareTo(indexToUsername(revHeader.getLockerIndex()))) { retVal = true; break; } } } } } return retVal; } boolean createCopyOfArchive() { boolean retVal = false; try { // Make sure the temp file is gone. m_TempFile.delete(); // Copy the archive to the tempfile. retVal = copyFile(m_File, m_TempFile); } catch (Exception e) { e.printStackTrace(); } return retVal; } private boolean copyFile(java.io.File fromFile, java.io.File toFile) { boolean retVal = false; java.io.BufferedInputStream inStream = null; java.io.BufferedOutputStream outStream = null; // We can only copy a file if it exists. if (fromFile.exists()) { try { inStream = new java.io.BufferedInputStream (new java.io.FileInputStream (fromFile)); outStream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(toFile)); if (fromFile.length() > 0) { byte[] readBuffer = new byte[FILE_COPY_BUFFER_SIZE]; while (true) { int byteCount = inStream.read(readBuffer); if (byteCount > 0) { outStream.write(readBuffer, 0, byteCount); } else { break; } } readBuffer = null; retVal = true; } } catch (Exception e) { e.printStackTrace(); retVal = false; } finally { try { if (inStream != null) { inStream.close(); } if (outStream != null) { outStream.close(); } } catch (Exception e) { e.printStackTrace(); retVal = false; } } } return retVal; } ArchiveAttributes getAttributes() { if (m_MustReadArchiveFile && !isArchiveInformationRead()) { readInformation(); } return m_HeaderInfo.getLogFileHeader().attributes(); } }