/* 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. */ package com.qumasoft.server; import com.qumasoft.qvcslib.AbstractProjectProperties; import com.qumasoft.qvcslib.ArchiveAttributes; import com.qumasoft.qvcslib.ArchiveDirManagerInterface; import com.qumasoft.qvcslib.ArchiveInfoInterface; import com.qumasoft.qvcslib.LogFileHeaderInfo; import com.qumasoft.qvcslib.LogFileInterface; import com.qumasoft.qvcslib.LogfileInfo; import com.qumasoft.qvcslib.LogfileListenerInterface; import com.qumasoft.qvcslib.QVCSException; import com.qumasoft.qvcslib.ReadWriteLock; import com.qumasoft.qvcslib.RevisionInformation; import com.qumasoft.qvcslib.Utility; import com.qumasoft.qvcslib.commandargs.CheckInCommandArgs; import com.qumasoft.qvcslib.commandargs.CheckOutCommandArgs; import com.qumasoft.qvcslib.commandargs.CreateArchiveCommandArgs; import com.qumasoft.qvcslib.commandargs.GetRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.LabelRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.LockRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.SetRevisionDescriptionCommandArgs; import com.qumasoft.qvcslib.commandargs.UnLabelRevisionCommandArgs; import com.qumasoft.qvcslib.commandargs.UnlockRevisionCommandArgs; import com.qumasoft.qvcslib.logfileaction.ActionType; import com.qumasoft.qvcslib.logfileaction.ChangeOnBranch; import com.qumasoft.qvcslib.logfileaction.CheckIn; import com.qumasoft.qvcslib.logfileaction.CheckOut; import com.qumasoft.qvcslib.logfileaction.Create; import com.qumasoft.qvcslib.logfileaction.Label; import com.qumasoft.qvcslib.logfileaction.Lock; import com.qumasoft.qvcslib.logfileaction.Remove; import com.qumasoft.qvcslib.logfileaction.SetAttributes; import com.qumasoft.qvcslib.logfileaction.SetCommentPrefix; import com.qumasoft.qvcslib.logfileaction.SetIsObsolete; import com.qumasoft.qvcslib.logfileaction.SetModuleDescription; import com.qumasoft.qvcslib.logfileaction.SetRevisionDescription; import com.qumasoft.qvcslib.logfileaction.UnLabel; import com.qumasoft.qvcslib.logfileaction.Unlock; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * The logfile class. This is the wrapper class for accessing a QVCS archive file. It guarantees that read and write operations are synchronized. If you want to * do anything with a QVCS archive file, you have to use this class, or you will be breaking the world. * @author Jim Voris */ public class LogFile implements ArchiveInfoInterface, LogFileInterface { private String fullArchiveFilename = null; private LogFileImpl logFileImpl = null; private ReadWriteLock readWriteLock = null; private List<LogfileListenerInterface> listeners = null; // Create our logger object private static final Logger LOGGER = Logger.getLogger("com.qumasoft.server"); /** * Creates a new instance of LogFile. * * @param fullArchiveName the full name of the archive file. */ public LogFile(String fullArchiveName) { fullArchiveFilename = fullArchiveName; logFileImpl = new LogFileImpl(fullArchiveName); readWriteLock = new ReadWriteLock(); } @Override public String getFullArchiveFilename() { return fullArchiveFilename; } /** * Read the archive file so that the header and revision information are at hand. * @return true if things worked; false otherwise. */ public boolean readInformation() { boolean retVal = false; try { readWriteLock.getReadLock(); retVal = logFileImpl.readInformation(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public int getFileID() { int retVal = -1; try { readWriteLock.getReadLock(); retVal = logFileImpl.getFileID(); } catch (QVCSException e) { LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e)); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Set the file id and remove view and branch labels. This is used after a 'reset' of the server, where we need to start fresh. In that case, the archive files need * to have any QVCS internal labels removed so the archive is in a known state, without any 'internal' labels. * @param fileID the new file id to use. */ public void setFileIDAndRemoveViewAndBranchLabels(int fileID) { try { readWriteLock.getWriteLock(); logFileImpl.setFileIDAndRemoveViewAndBranchLabels(fileID); } catch (QVCSException e) { LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e)); } finally { readWriteLock.releaseWriteLock(); } } @Override public ArchiveAttributes getAttributes() { ArchiveAttributes retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getAttributes(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public boolean setAttributes(String userName, ArchiveAttributes attributes) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.setAttributes(userName, attributes); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new SetAttributes(attributes)); } return retVal; } /** * Get the comment prefix. * @return the comment prefix. */ public String getCommentPrefix() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getCommentPrefix(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public boolean setCommentPrefix(String userName, String newCommentPrefix) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.setCommentPrefix(userName, newCommentPrefix); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new SetCommentPrefix(newCommentPrefix)); } return retVal; } /** * Get the module description. * @return the module description. */ public String getModuleDescription() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getModuleDescription(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public boolean setModuleDescription(String userName, String moduleDescription) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.setModuleDescription(userName, moduleDescription); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new SetModuleDescription(moduleDescription)); } return retVal; } @Override public boolean setRevisionDescription(SetRevisionDescriptionCommandArgs commandLineArgs) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.setRevisionDescription(commandLineArgs); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new SetRevisionDescription(commandLineArgs)); } return retVal; } @Override public String getRevisionDescription(final String revisionString) { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getRevisionDescription(revisionString); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public String getShortWorkfileName() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getShortWorkfileName(); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Get the short <i>archive</i> file name. * @return the short <i>archive</i> file name. */ public String getShortArchiveName() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getShortArchiveName(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public String getLockedByString() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLockedByString(); } catch (QVCSException e) { LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e)); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Get a String showing who has locked revisions. * @return a String showing who has locked revisions. */ public String getLockedByUser() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLockedByUser(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public java.util.Date getLastCheckInDate() { java.util.Date retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLastCheckInDate(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public String getLastEditBy() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLastEditBy(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public String getWorkfileInLocation() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getWorkfileInLocation(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public boolean getIsObsolete() { boolean retVal = false; try { readWriteLock.getReadLock(); retVal = logFileImpl.getIsObsolete(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public int getLockCount() { int retVal = 0; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLockCount(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public byte[] getDefaultRevisionDigest() { byte[] retVal = null; try { readWriteLock.getReadLock(); String defaultRevisionString = logFileImpl.getDefaultRevisionHeader().getRevisionString(); retVal = ArchiveDigestManager.getInstance().getArchiveDigest(this, defaultRevisionString); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Get a revision digest. This is the MD5 digest for the non-expanded workfile associated with the given revision. * @param revisionString the revision we're interested in. * @return the digest for the given revision. */ public byte[] geRevisionDigest(String revisionString) { byte[] retVal = null; try { readWriteLock.getReadLock(); retVal = ArchiveDigestManager.getInstance().getArchiveDigest(this, revisionString); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Get the logfile header info. * @return the logfile header info. */ public LogFileHeaderInfo getLogFileHeaderInfo() { LogFileHeaderInfo retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLogFileHeaderInfo(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public int getRevisionCount() { int retVal = 0; try { readWriteLock.getReadLock(); retVal = logFileImpl.getRevisionCount(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public RevisionInformation getRevisionInformation() { RevisionInformation retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getRevisionInformation(); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public String getLockedRevisionString(String userName) { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLockedRevisionString(userName); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Create an archive file. * @param commandLineArgs the command arguments. * @param projectProperties the project properties. * @param filename the file that we'll use to create the 1st revision. * @return true if things worked; false otherwise. */ public boolean createArchive(CreateArchiveCommandArgs commandLineArgs, AbstractProjectProperties projectProperties, String filename) { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.createArchive(commandLineArgs, projectProperties, filename); } catch (QVCSException e) { LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e)); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new Create(commandLineArgs)); } return retVal; } /** * Rename an archive. * @param userName the user name. * @param appendedPath the appended path. * @param oldShortWorkfileName the original short workfile name. * @param newShortWorkfileName the new short workfile name. * @param date the date to use for the operation. * @return true if things worked; false otherwise. */ public boolean renameArchive(String userName, String appendedPath, String oldShortWorkfileName, String newShortWorkfileName, final Date date) { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.renameArchive(userName, appendedPath, oldShortWorkfileName, newShortWorkfileName, date); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new Remove()); } return retVal; } /** * Move an archive. * @param userName user name. * @param appendedPath the appended path. * @param targetArchiveDirManagerInterface the destination directory manager. * @param shortWorkfileName the short workfile name. * @param date the date to use for the operation. * @return true if things worked; false otherwise. */ public boolean moveArchive(String userName, String appendedPath, ArchiveDirManagerInterface targetArchiveDirManagerInterface, String shortWorkfileName, final Date date) { boolean retVal = false; try { // Cast the target to an actual ArchiveDirManager, since that is what // it MUST be here. if (targetArchiveDirManagerInterface instanceof ArchiveDirManager) { ArchiveDirManager targetArchiveDirManager = (ArchiveDirManager) targetArchiveDirManagerInterface; readWriteLock.getWriteLock(); retVal = logFileImpl.moveArchive(userName, appendedPath, targetArchiveDirManager, shortWorkfileName, date); } } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new Remove()); } return retVal; } /** * Delete an archive on a translucent branch. * @param userName the user name. * @param appendedPath the appended path. * @param shortWorkfileName the short workfile name. * @param date the date for the operation. * @param branchLabel the branch label that identifies the translucent branch. * @param archiveInfoForTranslucentBranch the archive info of the file on the translucent branch. * @return true if things worked; false otherwise. */ public boolean deleteArchiveOnTranslucentBranch(String userName, String appendedPath, String shortWorkfileName, final Date date, final String branchLabel, ArchiveInfoForTranslucentBranch archiveInfoForTranslucentBranch) { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.deleteArchiveOnTranslucentBranch(userName, appendedPath, shortWorkfileName, date, branchLabel, archiveInfoForTranslucentBranch); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { ChangeOnBranch logfileActionChangeOnBranch = new ChangeOnBranch(this, ChangeOnBranch.DELETE_ON_BRANCH); notifyLogfileListeners(logfileActionChangeOnBranch); } return retVal; } /** * Move a file on a translucent branch. * @param userName the user name. * @param appendedPath the appended path. * @param targetArchiveDirManager the directory manager for the target directory. * @param shortWorkfileName the short workfile name. * @param date the date for the operation. * @param branchLabel the branch label that identifies the translucent branch. * @param archiveInfoForTranslucentBranch the archive info of the file on the translucent branch. * @return true if things worked; false otherwise. */ public boolean moveArchiveOnTranslucentBranch(String userName, String appendedPath, ArchiveDirManagerForTranslucentBranch targetArchiveDirManager, String shortWorkfileName, final Date date, final String branchLabel, ArchiveInfoForTranslucentBranch archiveInfoForTranslucentBranch) { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.moveArchiveOnTranslucentBranch(userName, appendedPath, targetArchiveDirManager, shortWorkfileName, date, branchLabel, archiveInfoForTranslucentBranch); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { ChangeOnBranch logfileActionChangeOnBranch = new ChangeOnBranch(this, ChangeOnBranch.MOVE_ON_BRANCH); notifyLogfileListeners(logfileActionChangeOnBranch); } return retVal; } /** * Rename a file on a translucent branch. * * @param userName the user name. * @param appendedPath the appended path * @param oldShortWorkfileName the old (original) short workfile name. * @param newShortWorkfileName the new short workfile name. * @param date the date of the transaction. * @param branchLabel the label that defines this translucent branch. * @param archiveInfoForTranslucentBranch the archive info object for the renamed file. * @return true of the rename succeeds; false otherwise. */ public boolean renameArchiveOnTranslucentBranch(String userName, String appendedPath, String oldShortWorkfileName, String newShortWorkfileName, final Date date, final String branchLabel, ArchiveInfoForTranslucentBranch archiveInfoForTranslucentBranch) { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.renameArchiveOnTranslucentBranch(userName, appendedPath, oldShortWorkfileName, newShortWorkfileName, date, branchLabel, archiveInfoForTranslucentBranch); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { ChangeOnBranch logfileActionChangeOnBranch = new ChangeOnBranch(this, ChangeOnBranch.RENAME_ON_BRANCH); logfileActionChangeOnBranch.setOldShortWorkfileName(oldShortWorkfileName); notifyLogfileListeners(logfileActionChangeOnBranch); } return retVal; } /** * Delete an archive (really move it to the cemetery). * @param userName the user name. * @param appendedPath the appended path. * @param targetArchiveDirManagerInterface the cemetery directory manager. * @param shortWorkfileName the short workfile name. * @param date the date to use for the operation. * @return true if things worked; false otherwise. */ public boolean deleteArchive(String userName, String appendedPath, ArchiveDirManagerInterface targetArchiveDirManagerInterface, String shortWorkfileName, final Date date) { boolean retVal = false; try { // Cast the target to an actual ArchiveDirManager, since that is what // it MUST be here. if (targetArchiveDirManagerInterface instanceof ArchiveDirManager) { ArchiveDirManager targetArchiveDirManager = (ArchiveDirManager) targetArchiveDirManagerInterface; readWriteLock.getWriteLock(); retVal = logFileImpl.deleteArchive(userName, appendedPath, targetArchiveDirManager, shortWorkfileName, date); } } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new Remove()); } return retVal; } @Override public boolean checkInRevision(CheckInCommandArgs commandLineArgs, String filename, boolean ignoreLocksToEnableBranchCheckinFlag) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.checkInRevision(commandLineArgs, filename, ignoreLocksToEnableBranchCheckinFlag); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new CheckIn(commandLineArgs)); } return retVal; } @Override public boolean getForVisualCompare(GetRevisionCommandArgs commandLineArgs, String outputFileName) throws QVCSException { return getRevision(commandLineArgs, outputFileName); } @Override public boolean getRevision(GetRevisionCommandArgs commandLineArgs, String fetchToFileName) throws QVCSException { boolean retVal = false; try { readWriteLock.getReadLock(); retVal = logFileImpl.getRevision(commandLineArgs, fetchToFileName); } finally { readWriteLock.releaseReadLock(); } return retVal; } @Override public boolean checkOutRevision(CheckOutCommandArgs commandLineArgs, String fetchToFileName) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.checkOutRevision(commandLineArgs, fetchToFileName); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new CheckOut(commandLineArgs)); } return retVal; } @Override public boolean lockRevision(LockRevisionCommandArgs commandLineArgs) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.lockRevision(commandLineArgs); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new Lock(commandLineArgs)); } return retVal; } @Override public boolean unlockRevision(UnlockRevisionCommandArgs commandLineArgs) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.unlockRevision(commandLineArgs); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new Unlock(commandLineArgs)); } return retVal; } @Override public boolean breakLock(UnlockRevisionCommandArgs commandLineArgs) throws QVCSException { throw new QVCSException("Unexpected call to breakLock method."); } @Override public boolean labelRevision(LabelRevisionCommandArgs commandLineArgs) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.labelRevision(commandLineArgs); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new Label(commandLineArgs)); } return retVal; } @Override public boolean unLabelRevision(UnLabelRevisionCommandArgs commandLineArgs) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.unLabelRevision(commandLineArgs); } finally { readWriteLock.releaseWriteLock(); } if (retVal) { notifyLogfileListeners(new UnLabel(commandLineArgs)); } return retVal; } /** * Add a logfile listener. * @param l the listener to add. */ public synchronized void addListener(LogfileListenerInterface l) { if (listeners == null) { listeners = new ArrayList<>(); } listeners.add(l); } /** * Remove a logfile listener. * @param l the listener to remove. */ public synchronized void removeListener(LogfileListenerInterface l) { if (listeners == null) { listeners = new ArrayList<>(); } else { listeners.remove(l); } } private synchronized void notifyLogfileListeners(ActionType action) { if (listeners != null) { // Make a copy of the listener array so we can avoid concurrent modification exceptions // which happen when we handle the delete notification (which removes itself as a listener). ArrayList<LogfileListenerInterface> listenersCopy = new ArrayList<>(listeners); Iterator<LogfileListenerInterface> i = listenersCopy.iterator(); while (i.hasNext()) { LogfileListenerInterface listener = i.next(); listener.notifyLogfileListener(this, action); } } } synchronized List<LogfileListenerInterface> getLogfileListeners() { return listeners; } synchronized void clearLogfileListeners() { listeners = null; } @Override public LogfileInfo getLogfileInfo() { LogfileInfo retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getLogfileInfo(); } catch (QVCSException e) { LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e)); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Get the LogFileImpl for this LogFile. This should <i>only</i> be used for performing work in the server package -- e.g. * moving a file on a translucent branch. * * @return the LogFileImpl for this LogFile. */ public LogFileImpl getLogFileImpl() { return logFileImpl; } @Override public boolean setIsObsolete(String userName, boolean flag) throws QVCSException { boolean retVal = false; try { readWriteLock.getWriteLock(); retVal = logFileImpl.setIsObsolete(userName, flag); if (retVal) { notifyLogfileListeners(new SetIsObsolete(flag)); } } finally { readWriteLock.releaseWriteLock(); } return retVal; } /** * Return a buffer that contains the requested revision. This method is synchronous. * * @param revisionString the revision that should be fetched * @return a byte array containing the non-keyword expanded copy of the requested revision, or null if the revision cannot be * retrieved. */ @Override public byte[] getRevisionAsByteArray(String revisionString) { byte[] worfileBuffer = null; try { readWriteLock.getReadLock(); worfileBuffer = logFileImpl.getRevisionAsByteArray(revisionString); } finally { readWriteLock.releaseReadLock(); } return worfileBuffer; } @Override public String getDefaultRevisionString() { String retVal = null; try { readWriteLock.getReadLock(); retVal = logFileImpl.getDefaultRevisionHeader().getRevisionString(); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * Has the given label been applied to this logfile. * @param label the label to search for. * @return true if this archive has the given label; false otherwise. */ public boolean hasLabel(final String label) { boolean retVal = false; try { readWriteLock.getReadLock(); retVal = logFileImpl.hasLabel(label); } finally { readWriteLock.releaseReadLock(); } return retVal; } /** * We never have overlap in a logfile... only on branches. * * @return false, because we never report overlap on the trunk. */ @Override public boolean getIsOverlap() { return false; } }