/* 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.qvcslib;
import com.qumasoft.qvcslib.commandargs.CheckInCommandArgs;
import com.qumasoft.qvcslib.commandargs.CheckOutCommandArgs;
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 java.io.File;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Merged information. Instances of this class contain both archive information and workfile information, hence the name mergedInfo. The class name choice pre-dates any thought
* of merge support, so don't think of this class as having anything to do directly with merge types of operations.
* @author Jim Voris
*/
public class MergedInfo implements MergedInfoInterface {
// Create our logger object
private static final Logger LOGGER = Logger.getLogger("com.qumasoft.qvcslib");
private static final String[] STATUS_STRINGS = {"Current",
"Stale",
"Your copy changed",
"Merge required",
"Different",
"Missing",
"Not controlled",
"Invalid"};
private static final String[] STATUS_SORT_VALUES = {"5", // current
"0", // stale
"1", // your copy has changed
"2", // merge required
"3", // different
"4", // missing
"6", // not controlled
"7"}; // invalid
private WorkfileInfoInterface workfileInfo = null;
private ArchiveInfoInterface archiveInfo = null;
private ArchiveDirManagerInterface archiveDirManager = null;
private AbstractProjectProperties projectProperties = null;
private String userName = null;
private String mergedInfoKey = null;
private static Date oldestDate = new Date(0);
/**
* Create a new merged info instance.
* @param workInfo the workfile info.
* @param archiveDirMgr the containing archive directory manager.
* @param projProperties the project properties.
* @param user the user name.
*/
public MergedInfo(WorkfileInfoInterface workInfo, ArchiveDirManagerInterface archiveDirMgr, AbstractProjectProperties projProperties, String user) {
workfileInfo = workInfo;
archiveDirManager = archiveDirMgr;
projectProperties = projProperties;
userName = user;
if (projectProperties.getIgnoreCaseFlag()) {
mergedInfoKey = workInfo.getShortWorkfileName().toLowerCase();
} else {
mergedInfoKey = workInfo.getShortWorkfileName();
}
}
/**
* Create a new merged info instance.
* @param archInfo the archive info
* @param archiveDirMgr the containing archive directory manager.
* @param projProperties the project properties.
* @param user the user name.
*/
public MergedInfo(ArchiveInfoInterface archInfo, ArchiveDirManagerInterface archiveDirMgr, AbstractProjectProperties projProperties, String user) {
archiveInfo = archInfo;
archiveDirManager = archiveDirMgr;
projectProperties = projProperties;
userName = user;
if (projectProperties.getIgnoreCaseFlag()) {
mergedInfoKey = archInfo.getShortWorkfileName().toLowerCase();
} else {
mergedInfoKey = archInfo.getShortWorkfileName();
}
}
/**
* {@inheritDoc}
*/
@Override
public void setArchiveInfo(ArchiveInfoInterface archInfo) {
archiveInfo = archInfo;
}
/**
* {@inheritDoc}
*/
@Override
public ArchiveInfoInterface getArchiveInfo() {
return archiveInfo;
}
/**
* {@inheritDoc}
*/
@Override
public void setWorkfileInfo(WorkfileInfoInterface workInfo) {
workfileInfo = workInfo;
}
/**
* {@inheritDoc}
*/
@Override
public WorkfileInfoInterface getWorkfileInfo() {
return workfileInfo;
}
private WorkfileInfoInterface getDigestWorkfileInfo() {
WorkfileInfoInterface digestWorkfileInfo = WorkfileDigestManager.getInstance().getDigestWorkfileInfo(workfileInfo);
return digestWorkfileInfo;
}
/**
* {@inheritDoc}
*/
@Override
public String getFullWorkfileName() {
String retVal = null;
if (workfileInfo != null) {
retVal = workfileInfo.getFullWorkfileName();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public String getShortWorkfileName() {
String retVal = null;
if (workfileInfo != null) {
retVal = workfileInfo.getShortWorkfileName();
} else if (archiveInfo != null) {
retVal = archiveInfo.getShortWorkfileName();
}
return retVal;
}
private byte[] getWorkfileDigest() {
byte[] retVal = null;
if (workfileInfo != null && archiveInfo != null) {
workfileInfo.setArchiveInfo(archiveInfo);
retVal = WorkfileDigestManager.getInstance().updateWorkfileDigestOnly(workfileInfo, projectProperties);
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public byte[] getDefaultRevisionDigest() {
byte[] retVal = null;
if (archiveInfo != null) {
retVal = archiveInfo.getDefaultRevisionDigest();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public Date getWorkfileLastChangedDate() {
Date retVal = null;
if (workfileInfo != null) {
retVal = workfileInfo.getWorkfileLastChangedDate();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public long getWorkfileSize() {
long retVal = 0;
if (workfileInfo != null) {
retVal = workfileInfo.getWorkfileSize();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public File getWorkfile() {
File retVal = null;
if (workfileInfo != null) {
retVal = workfileInfo.getWorkfile();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public long getFetchedDate() {
long retVal = 0L;
if (workfileInfo != null) {
retVal = workfileInfo.getFetchedDate();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public void setFetchedDate(long time) {
if (workfileInfo != null) {
workfileInfo.setFetchedDate(time);
}
}
/**
* {@inheritDoc}
*/
@Override
public String getWorkfileRevisionString() {
String retVal = null;
if (workfileInfo != null) {
retVal = workfileInfo.getWorkfileRevisionString();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public void setWorkfileRevisionString(String revisionString) {
if (workfileInfo != null) {
workfileInfo.setWorkfileRevisionString(revisionString);
}
}
/**
* {@inheritDoc}
*/
@Override
public String getStatusValue() {
return STATUS_SORT_VALUES[getStatusIndex()];
}
/**
* {@inheritDoc}
*/
@Override
public String getStatusString() {
return STATUS_STRINGS[getStatusIndex()];
}
/**
* Get the status string.
* @return the status string.
*/
public static String[] getStatusStrings() {
return STATUS_STRINGS;
}
/**
* {@inheritDoc}
*/
@Override
public int getStatusIndex() {
int retVal = INVALID_STATUS_INDEX;
if (workfileInfo != null && archiveInfo != null) {
// This is where I compare the workfile digest with
// the archive digest value to see if I can figure out if the
// file is stale.
if (!workfileInfo.getWorkfileExists()) {
// Missing.
retVal = MISSING_STATUS_INDEX;
} else if (digestsMatch()) {
// The workfile is current.
retVal = CURRENT_STATUS_INDEX;
// If we need to, update the digestWorkfileInfo object to have the
// default revision string and the current timestamp on the current
// workfile.
WorkfileInfoInterface digestWorkfileInfo = getDigestWorkfileInfo();
if (digestWorkfileInfo.getWorkfileRevisionString() == null) {
digestWorkfileInfo.setWorkfileRevisionString(getArchiveInfo().getDefaultRevisionString());
digestWorkfileInfo.setFetchedDate(workfileInfo.getWorkfile().lastModified());
} else if (digestWorkfileInfo.getWorkfileRevisionString().compareTo(getArchiveInfo().getDefaultRevisionString()) != 0) {
// This is for the case where a user has checked in changes
// via the IDE and that same user is running a copy of the
// client application at the same time.
digestWorkfileInfo.setWorkfileRevisionString(getArchiveInfo().getDefaultRevisionString());
digestWorkfileInfo.setFetchedDate(workfileInfo.getWorkfile().lastModified());
}
} else {
// Digests do not match. Figure out the status....
WorkfileInfoInterface digestWorkfileInfo = getDigestWorkfileInfo();
if (digestWorkfileInfo != null) {
workfileInfo.setFetchedDate(digestWorkfileInfo.getFetchedDate());
workfileInfo.setWorkfileRevisionString(digestWorkfileInfo.getWorkfileRevisionString());
}
// Compare the revision string that we fetched with the revision on
// the server.
if (workfileInfo.getWorkfileRevisionString() != null) {
if (workfileInfo.getWorkfileRevisionString().equals(archiveInfo.getDefaultRevisionString())) {
// User's copy has changed
retVal = YOUR_COPY_CHANGED_STATUS_INDEX;
} else {
// There is a different (newer?) revision on the server...
if (workfileInfo.getWorkfile().lastModified() > workfileInfo.getFetchedDate()) {
String lockedRevisionString = getLockedRevisionString(getUserName());
if (lockedRevisionString != null) {
RevisionInformation revisionInformation = getArchiveInfo().getRevisionInformation();
int lockedRevisionIndex = revisionInformation.getRevisionIndex(lockedRevisionString);
int defaultRevisionIndex = revisionInformation.getRevisionIndex(getArchiveInfo().getDefaultRevisionString());
int fetchedRevisionIndex = revisionInformation.getRevisionIndex(workfileInfo.getWorkfileRevisionString());
assert (defaultRevisionIndex != fetchedRevisionIndex);
if (defaultRevisionIndex == lockedRevisionIndex) {
if (defaultRevisionIndex != fetchedRevisionIndex) {
// defaultRevisionIndex == lockedRevisionIndex &&
// defaultRevisionIndex != fetchedRevisionIndex
// The user locked a file after they made edits to it. A merge is required.
retVal = MERGE_REQUIRED_STATUS_INDEX;
}
} else {
// defaultRevisionIndex != lockedRevisionIndex
if (defaultRevisionIndex != fetchedRevisionIndex) {
// defaultRevisionIndex != lockedRevisionIndex &&
// defaultRevisionIndex != fetchedRevisionIndex
if (fetchedRevisionIndex == lockedRevisionIndex) {
// The user has made edits to a non-tip revision which they have locked.
retVal = YOUR_COPY_CHANGED_STATUS_INDEX;
} else {
retVal = MERGE_REQUIRED_STATUS_INDEX;
}
}
}
} else {
// The user made edits, and there are changes on the server too!
// Merge required.
retVal = MERGE_REQUIRED_STATUS_INDEX;
}
} else {
// Stale. The user hasn't made any edits since they did the get.
retVal = STALE_STATUS_INDEX;
}
}
} else {
// We can't figure out much more than they are different.
retVal = DIFFERENT_STATUS_INDEX;
}
}
} else if (workfileInfo == null && archiveInfo != null) {
// Missing
retVal = MISSING_STATUS_INDEX;
} else if (workfileInfo != null && archiveInfo == null) {
// Not controlled
retVal = NOT_CONTROLLED_STATUS_INDEX;
} else {
// Invalid
retVal = INVALID_STATUS_INDEX;
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public int getLockCount() {
if (archiveInfo != null) {
return archiveInfo.getLockCount();
} else {
return 0;
}
}
/**
* {@inheritDoc}
*/
@Override
public String getLockedByString() {
String retVal = "";
if (archiveInfo != null) {
String lockedBy = archiveInfo.getLockedByString();
if (lockedBy != null) {
retVal = lockedBy;
}
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public Date getLastCheckInDate() {
if (archiveInfo != null) {
return archiveInfo.getLastCheckInDate();
} else {
return oldestDate;
}
}
/**
* {@inheritDoc}
*/
@Override
public String getWorkfileInLocation() {
if (archiveInfo != null) {
return archiveInfo.getWorkfileInLocation();
} else {
return "";
}
}
/**
* {@inheritDoc}
*/
@Override
public String getLastEditBy() {
if (archiveInfo != null) {
return archiveInfo.getLastEditBy();
} else {
return "";
}
}
/**
* {@inheritDoc}
*/
@Override
public ArchiveAttributes getAttributes() {
ArchiveAttributes retVal = null;
if (archiveInfo != null) {
retVal = archiveInfo.getAttributes();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public String getDefaultRevisionString() {
String retVal = null;
if (archiveInfo != null) {
retVal = archiveInfo.getDefaultRevisionString();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public String getLockedRevisionString(String user) {
String retVal = null;
if (archiveInfo != null) {
retVal = archiveInfo.getLockedRevisionString(user);
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getKeywordExpansionAttribute() {
boolean retVal = false;
if (archiveInfo != null) {
retVal = archiveInfo.getAttributes().getIsExpandKeywords();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getBinaryFileAttribute() {
boolean retVal = false;
if (archiveInfo != null) {
retVal = archiveInfo.getAttributes().getIsBinaryfile();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public LogfileInfo getLogfileInfo() {
LogfileInfo retVal = null;
if (archiveInfo != null) {
retVal = archiveInfo.getLogfileInfo();
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public String getRevisionDescription(final String revString) {
String retVal = null;
if (archiveInfo != null) {
retVal = archiveInfo.getRevisionDescription(revString);
}
return retVal;
}
/**
* Set the keyword expansion attribute for the workfile. This method should not be called here -- and exists to satisfy the interface.
* @param flag value for keyword expansion attribute.
*/
@Override
public void setKeywordExpansionAttribute(boolean flag) {
throw new java.lang.RuntimeException("Invalid call of setKeywordExpansionAttribute in MergedInfo");
}
/**
* {@inheritDoc}
*/
@Override
public void setBinaryFileAttribute(boolean flag) {
throw new java.lang.RuntimeException("Invalid call of setBinaryFileAttribute in MergedInfo");
}
/**
* {@inheritDoc}
*/
@Override
public String getProjectName() {
return getProjectProperties().getProjectName();
}
// Return true if the archive digest matches the workfile digest.
private boolean digestsMatch() {
boolean retVal = true;
byte[] workfileDigest = getWorkfileDigest();
byte[] archiveDigest = getDefaultRevisionDigest();
if ((workfileDigest != null) && (archiveDigest != null) && (workfileDigest.length == archiveDigest.length)) {
int count = workfileDigest.length;
for (int i = 0; i < count; i++) {
if (workfileDigest[i] != archiveDigest[i]) {
LOGGER.log(Level.FINEST, getShortWorkfileName() + ": digest non-match in loop at index " + i);
retVal = false;
break;
}
}
} else {
LOGGER.log(Level.FINEST, "digest non-match NO loop");
retVal = false;
}
return retVal;
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public boolean getRevision(GetRevisionCommandArgs commandLineArgs, String fetchToFileName) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.getRevision(commandLineArgs, fetchToFileName);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getForVisualCompare(GetRevisionCommandArgs commandLineArgs, String outputFileName) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.getForVisualCompare(commandLineArgs, outputFileName);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkOutRevision(CheckOutCommandArgs commandLineArgs, String fetchToFileName) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.checkOutRevision(commandLineArgs, fetchToFileName);
}
return false;
}
/**
* Check in a revision.
*
* @param commandArgs the command arguments.
* @param checkInFilename the check in file name.
* @param ignoreLocksToEnableBranchCheckinFlag this flag is ignored on the client side, and exists here only to satisfy the interface signature.
* @return true if things worked; false otherwise.
* @throws QVCSException if something goes wrong.
*/
@Override
public boolean checkInRevision(CheckInCommandArgs commandArgs, String checkInFilename, boolean ignoreLocksToEnableBranchCheckinFlag) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.checkInRevision(commandArgs, checkInFilename, ignoreLocksToEnableBranchCheckinFlag);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean lockRevision(LockRevisionCommandArgs commandArgs) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.lockRevision(commandArgs);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean unlockRevision(UnlockRevisionCommandArgs commandArgs) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.unlockRevision(commandArgs);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean breakLock(UnlockRevisionCommandArgs commandArgs) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.breakLock(commandArgs);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean labelRevision(LabelRevisionCommandArgs commandArgs) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.labelRevision(commandArgs);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean unLabelRevision(UnLabelRevisionCommandArgs commandArgs) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.unLabelRevision(commandArgs);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean setIsObsolete(String user, boolean flag) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.setIsObsolete(user, flag);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getIsObsolete() {
if (archiveInfo != null) {
return archiveInfo.getIsObsolete();
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean setAttributes(String user, ArchiveAttributes attributes) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.setAttributes(user, attributes);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean setCommentPrefix(String user, String commentPrefix) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.setCommentPrefix(user, commentPrefix);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean setModuleDescription(String user, String moduleDescription) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.setModuleDescription(user, moduleDescription);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean setRevisionDescription(SetRevisionDescriptionCommandArgs commandArgs) throws QVCSException {
if (archiveInfo != null) {
return archiveInfo.setRevisionDescription(commandArgs);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getIsRemote() {
boolean retVal = false;
if (archiveInfo != null) {
if (archiveInfo instanceof LogFileProxy) {
retVal = true;
}
} else {
if (archiveDirManager instanceof ArchiveDirManagerProxy) {
retVal = true;
}
}
return retVal;
}
/**
* {@inheritDoc}
*/
@Override
public AbstractProjectProperties getProjectProperties() {
return projectProperties;
}
/**
* {@inheritDoc}
*/
@Override
public String getMergedInfoKey() {
return mergedInfoKey;
}
/**
* {@inheritDoc}
*/
@Override
public String getUserName() {
return userName;
}
/**
* {@inheritDoc}
*/
@Override
public ArchiveDirManagerInterface getArchiveDirManager() {
return archiveDirManager;
}
/**
* 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[] workfileBuffer = null;
if (archiveInfo != null) {
workfileBuffer = archiveInfo.getRevisionAsByteArray(revisionString);
}
return workfileBuffer;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getWorkfileExists() {
if (workfileInfo != null) {
return workfileInfo.getWorkfileExists();
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public int getRevisionCount() {
int revisionCount = -1;
if (archiveInfo != null) {
revisionCount = archiveInfo.getRevisionCount();
}
return revisionCount;
}
/**
* {@inheritDoc}
*/
@Override
public RevisionInformation getRevisionInformation() {
RevisionInformation revisionInformation = null;
if (archiveInfo != null) {
revisionInformation = archiveInfo.getRevisionInformation();
}
return revisionInformation;
}
/**
* {@inheritDoc}
*/
@Override
public int getFileID() {
int fileID = -1;
if (archiveInfo != null) {
fileID = archiveInfo.getFileID();
}
return fileID;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getIsOverlap() {
boolean overlapFlag = false;
if (archiveInfo != null) {
overlapFlag = archiveInfo.getIsOverlap();
}
return overlapFlag;
}
/**
* {@inheritDoc}
*/
@Override
public InfoForMerge getInfoForMerge(String project, String view, String path) {
InfoForMerge infoForMerge = null;
if (archiveInfo != null) {
LogFileProxy logfileProxy = (LogFileProxy) archiveInfo;
infoForMerge = logfileProxy.getInfoForMerge(project, view, path, getFileID());
}
return infoForMerge;
}
/**
* {@inheritDoc}
*/
@Override
public ResolveConflictResults resolveConflictFromParentBranch(String project, String branch) {
ResolveConflictResults resolveConflictResults = null;
if (archiveInfo != null) {
LogFileProxy logfileProxy = (LogFileProxy) archiveInfo;
resolveConflictResults = logfileProxy.resolveConflictFromParentBranch(project, branch, getFileID());
}
return resolveConflictResults;
}
/**
* {@inheritDoc}
*/
@Override
public PromoteFileResults promoteFile(String project, String branch, String parentBranch, FilePromotionInfo filePromoInfo, int id) {
PromoteFileResults promoteFileResults = null;
if (archiveInfo != null) {
LogFileProxy logfileProxy = (LogFileProxy) archiveInfo;
promoteFileResults = logfileProxy.promoteFile(getUserName(), project, branch, parentBranch, filePromoInfo, id);
}
return promoteFileResults;
}
}