// 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 java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Class that holds the header elements of a QVCS archive file.
* @author Jim Voris
*/
public final class LogFileHeaderInfo implements java.io.Serializable {
private static final long serialVersionUID = 4972338412531779081L;
/*
* Data members that get serialized
*/
private LogFileHeader logFileHeader = null;
private RevisionDescriptor revisionDescriptor = null;
private LabelInfo[] labelInfoArray = null;
private String accessList = null;
private String modifierList = null;
private String commentPrefix = null;
private String owner = null;
private String moduleDescription = null;
private SupplementalHeaderInfo supplementalInfo = null;
private transient boolean sizeChanged = false;
/**
* Default constructor.
*/
public LogFileHeaderInfo() {
logFileHeader = new LogFileHeader();
}
/**
* For use in views only.
* @param header the header from the LogFile.
*/
public LogFileHeaderInfo(LogFileHeader header) {
logFileHeader = header;
}
/**
* Read header information from a logfile.
* @param inStream the stream to read from.
* @return true if the read was successful; false otherwise.
* @throws com.qumasoft.qvcslib.LogFileReadException if there are QVCS related problems reading the file.
*/
public boolean read(RandomAccessFile inStream) throws LogFileReadException {
boolean returnValue = true;
try {
long bytesAvailable = inStream.length();
if (bytesAvailable > 0) {
returnValue = logFileHeader.read(inStream);
if (returnValue) {
// Read the variable length information.
returnValue = readVariableInfo(inStream);
}
}
} catch (FileNotFoundException notFound) {
returnValue = false;
} catch (IOException ioProblem) {
returnValue = false;
}
return returnValue;
}
/**
* Write the header information to an archive file.
* @param outStream the stream to write to.
* @return true if the write was successful; false otherwise.
*/
public boolean write(RandomAccessFile outStream) {
boolean returnValue;
try {
returnValue = logFileHeader.write(outStream);
if (returnValue) {
// Write the variable length information.
returnValue = writeVariableInfo(outStream);
}
} catch (FileNotFoundException notFound) {
returnValue = false;
} catch (IOException ioProblem) {
returnValue = false;
}
return returnValue;
}
/**
* Method to read the variable length information from a logfile header. Assume the stream is correctly positioned.
* @param inStream the stream to read from.
* @return true if the read was successful; false otherwise.
* @throws java.io.IOException on read problems
*/
boolean readVariableInfo(RandomAccessFile inStream) throws IOException {
boolean returnValue = true;
// Read in the default branch information
if (logFileHeader.defaultDepth() > 0) {
revisionDescriptor = new RevisionDescriptor(logFileHeader.defaultDepth());
revisionDescriptor.read(inStream);
}
// Read in the access list
if (logFileHeader.accessSize() > 0) {
byte[] accessLst = new byte[logFileHeader.accessSize()];
int bytesRead = inStream.read(accessLst);
accessList = new String(accessLst, 0, accessLst.length - 1);
}
// Read in the modifier list
if (logFileHeader.modifierSize() > 0) {
byte[] modifierLst = new byte[logFileHeader.modifierSize()];
int bytesRead = inStream.read(modifierLst);
modifierList = new String(modifierLst, 0, modifierLst.length - 1);
}
// Read in the comment prefix
if (logFileHeader.commentSize() > 0) {
byte[] commentPfx = new byte[logFileHeader.commentSize()];
int bytesRead = inStream.read(commentPfx);
commentPrefix = new String(commentPfx, 0, commentPfx.length - 1);
}
// Read in the owner
if (logFileHeader.ownerSize() > 0) {
byte[] ownr = new byte[logFileHeader.ownerSize()];
int bytesRead = inStream.read(ownr);
owner = new String(ownr, 0, ownr.length - 1);
}
// Read in the Module description
if (logFileHeader.descriptionSize() > 0) {
byte[] moduleDesc = new byte[logFileHeader.descriptionSize()];
int bytesRead = inStream.read(moduleDesc);
moduleDescription = new String(moduleDesc, 0, moduleDesc.length - 1);
}
// Read in the supplemental information
if (logFileHeader.supplementalInfoSize() > 0) {
supplementalInfo = new SupplementalHeaderInfo(logFileHeader.supplementalInfoSize());
supplementalInfo.read(inStream);
// For really old QVCS/QVCS-Pro archives, we may need to adjust the size
// of the supplemental info area.
if (logFileHeader.supplementalInfoSize() != supplementalInfo.getSize()) {
logFileHeader.getSupplementalInfoSize().setValue(supplementalInfo.getSize());
}
}
// Read in any label information
if (logFileHeader.versionCount() > 0) {
labelInfoArray = new LabelInfo[logFileHeader.versionCount()];
for (int i = 0; i < logFileHeader.versionCount(); i++) {
labelInfoArray[i] = new LabelInfo();
labelInfoArray[i].read(inStream);
}
}
return returnValue;
}
/**
* Method to write the variable length information of a logfile header.
* @param outStream the stream to write to.
* @throws java.io.IOException if there is a write error.
*/
boolean writeVariableInfo(RandomAccessFile outStream) throws IOException {
boolean returnValue = true;
// Write out the default branch information
if (logFileHeader.defaultDepth() > 0) {
revisionDescriptor.write(outStream);
}
// Write out the access list
if (logFileHeader.accessSize() > 0) {
outStream.write(accessList.getBytes());
outStream.writeByte(0);
}
// Write out the modifier list
if (logFileHeader.modifierSize() > 0) {
outStream.write(modifierList.getBytes());
outStream.writeByte(0);
}
// Write out the comment prefix
if (logFileHeader.commentSize() > 0) {
outStream.write(commentPrefix.getBytes());
outStream.writeByte(0);
}
// Write out the owner
if (logFileHeader.ownerSize() > 0) {
outStream.write(owner.getBytes());
outStream.writeByte(0);
}
// Write out the Module description
if (logFileHeader.descriptionSize() > 0) {
outStream.write(moduleDescription.getBytes());
outStream.writeByte(0);
}
// Write out the supplemental information
if (logFileHeader.supplementalInfoSize() > 0) {
supplementalInfo.write(outStream);
}
// Write out any label information
if (logFileHeader.versionCount() > 0) {
for (int i = 0; i < logFileHeader.versionCount(); i++) {
labelInfoArray[i].write(outStream);
}
}
return returnValue;
}
/**
* Return the access list as a String.
* @return a String representation of the access list (comma separated user names).
*/
public String getAccessList() {
return accessList;
}
/**
* Set the access list.
* @param accessLst a comma separated list of QVCS user names.
*/
public void setAccessList(String accessLst) {
if (accessList != null) {
if (accessList.length() != accessLst.length()) {
sizeChanged = true;
}
} else {
sizeChanged = true;
}
accessList = accessLst;
logFileHeader.getAccessSize().setValue(1 + accessLst.getBytes().length);
}
/**
* Return the modifier list for this archive.
* @return the modifier list for this archive.
*/
public String getModifierList() {
return modifierList;
}
/**
* Set the modifier list.
* @param modifierLst a comma separated list of QVCS user names.
*/
public void setModifierList(String modifierLst) {
if (modifierList != null) {
if (modifierList.length() != modifierLst.length()) {
sizeChanged = true;
}
} else {
sizeChanged = true;
}
modifierList = modifierLst;
logFileHeader.getModifierSize().setValue(1 + modifierLst.getBytes().length);
}
/**
* Return the module description as a String.
* @return the module description as a String.
*/
public String getModuleDescription() {
return moduleDescription;
}
/**
* Set the module description.
* @param moduleDesc the module description.
*/
public void setModuleDescription(String moduleDesc) {
if (moduleDescription != null) {
if (moduleDescription.length() != moduleDesc.length()) {
sizeChanged = true;
}
} else {
sizeChanged = true;
}
moduleDescription = moduleDesc;
logFileHeader.getDescSize().setValue(1 + moduleDesc.getBytes().length);
}
/**
* Get the owner as a String.
* @return the owner as a String.
*/
public String getOwner() {
return owner;
}
/**
* Set the module owner.
* @param ownr the module owner.
*/
public void setOwner(String ownr) {
if (owner != null) {
if (owner.length() != ownr.length()) {
sizeChanged = true;
}
} else {
sizeChanged = true;
}
owner = ownr;
logFileHeader.getOwnerSize().setValue(1 + ownr.getBytes().length);
}
/**
* Get the comment prefix as a String.
* @return the comment prefix.
*/
public String getCommentPrefix() {
return commentPrefix;
}
/**
* Set the comment prefix as a String.
* @param commentPfx the comment prefix as a String.
*/
public void setCommentPrefix(String commentPfx) {
if (commentPrefix != null) {
if (commentPrefix.length() != commentPfx.length()) {
sizeChanged = true;
}
} else {
sizeChanged = true;
}
commentPrefix = commentPfx;
logFileHeader.getCommentSize().setValue(1 + commentPfx.getBytes().length);
}
/**
* Get the array of label information.
* @return the array of label information.
*/
public LabelInfo[] getLabelInfo() {
return labelInfoArray;
}
/**
* Set the label information.
* @param newLabelInfoArray the new label information.
*/
public void setLabelInfo(LabelInfo[] newLabelInfoArray) {
labelInfoArray = newLabelInfoArray;
if (newLabelInfoArray != null) {
logFileHeader.setVersionCount(newLabelInfoArray.length);
} else {
logFileHeader.setVersionCount(0);
}
}
/**
* Get the supplemental info for this header.
* @return the supplemental header info for this header.
*/
public SupplementalHeaderInfo getSupplementalHeaderInfo() {
return supplementalInfo;
}
/**
* Return true if this file has the given label; false otherwise.
* @param label the label to search for.
* @return true if the label has been applied to this file; false otherwise.
*/
public boolean hasLabel(String label) {
boolean flag = false;
LabelInfo[] labelInfo = getLabelInfo();
if (labelInfo != null) {
for (LabelInfo labelInfo1 : labelInfo) {
if (0 == labelInfo1.getLabelString().compareTo(label)) {
flag = true;
break;
}
}
}
return flag;
}
/**
* Is this file obsolete.
* @deprecated we don't use the obsolete marker anymore.
* @return true if the obsolete marker label has been applied to this file; false otherwise.
*/
public boolean getIsObsolete() {
boolean retVal = false;
if (labelInfoArray != null) {
for (LabelInfo labelInfo : labelInfoArray) {
if (labelInfo.getIsObsolete()) {
retVal = true;
break;
}
}
}
return retVal;
}
/**
* Set the obsolete state for this file.
* @deprecated we don't use the obsolete marker anymore.
* @param flag true to mark obsolete; false to mark not obsolete.
* @param creatorIndex the user index of the QVCS user making this change.
*/
@SuppressWarnings("ManualArrayToCollectionCopy")
public void setIsObsolete(boolean flag, int creatorIndex) {
if (flag) {
// Mark the file obsolete
if (!getIsObsolete()) {
sizeChanged = true;
// We need to add the obsolete marker label.
LabelInfo[] labelInfo = getLabelInfo();
int currentSize = 0;
if (labelInfo != null) {
currentSize = labelInfo.length;
}
// Create the obsolete marker.
LabelInfo[] newLabelInfo = new LabelInfo[currentSize + 1];
LabelInfo obsoleteMarker = new LabelInfo();
obsoleteMarker.setIsObsolete(true, creatorIndex);
// Put the obsolete marker as the 1st label in the label array
newLabelInfo[0] = obsoleteMarker;
for (int i = 0; i < currentSize; i++) {
newLabelInfo[i + 1] = labelInfo[i];
}
setLabelInfo(newLabelInfo);
}
} else {
// Mark the file not obsolete
if (getIsObsolete()) {
sizeChanged = true;
LabelInfo[] currentLabels = getLabelInfo();
LabelInfo[] newLabelInfo = null;
if (currentLabels.length > 1) {
newLabelInfo = new LabelInfo[currentLabels.length - 1];
// Remove the obsolete marker label...
int j = 0;
int removeCount = 0;
for (LabelInfo currentLabel : currentLabels) {
if (currentLabel.getIsObsolete()) {
removeCount++;
} else {
newLabelInfo[j++] = currentLabel;
}
}
if (removeCount > 1) {
// Oops. We had more than a single obsolete marker
// (which could happen because of an earlier bug!).
// We need to make the newLableInfo to be the correct
// size.
LabelInfo[] compactedNewLabelInfo = new LabelInfo[currentLabels.length - removeCount];
for (int i = 0; i < compactedNewLabelInfo.length; i++) {
compactedNewLabelInfo[i] = newLabelInfo[i];
}
newLabelInfo = compactedNewLabelInfo;
}
}
setLabelInfo(newLabelInfo);
}
}
}
/**
* Get the header.
* @return the header.
*/
public LogFileHeader getLogFileHeader() {
return logFileHeader;
}
/**
* Get the default revision's revision descriptor.
* @return the default revision's revision descriptor.
*/
public RevisionDescriptor getDefaultRevisionDescriptor() {
return revisionDescriptor;
}
/**
* Set the default revision descriptor.
* <p>
* <b>THIS SHOULD ONLY BE USED BY BRANCH ARCHIVE INFO CLASSES TO BUILD THE FAKE LOGFILEHEADERINFO THAT DESCRIBES THE
* BRANCH WAY OF LOOKING AT THE ASSOCIATED FILE</b></p>
* @param revisionDesc the revision descriptor to use here.
*/
public void setDefaultRevisionDescriptor(RevisionDescriptor revisionDesc) {
this.revisionDescriptor = revisionDesc;
}
/**
* Get the workfile name. Used to capture the checkout to location.
* @return the workfile name.
*/
public String getWorkfileName() {
if (supplementalInfo != null) {
return supplementalInfo.getWorkfileCheckedOutToLocation();
} else {
return null;
}
}
/**
* Set the workfile name.
* @param workfileName the workfile name.
*/
public void setWorkfileName(String workfileName) {
if (supplementalInfo == null) {
supplementalInfo = new SupplementalHeaderInfo();
logFileHeader.getSupplementalInfoSize().setValue(supplementalInfo.getSize());
}
supplementalInfo.setWorkfileCheckedOutToLocation(workfileName);
}
/**
* Get the revision count... i.e. the number of revisions in this archive file.
* @return the revision count.
*/
public int getRevisionCount() {
return logFileHeader.revisionCount();
}
/**
* Update the header 'in-place'. This can only be used if the size of the header has not changed. Assume the stream is positioned correctly.
* @param ioStream the stream to write to.
* @return true if the write succeeds.
*/
public boolean updateInPlace(java.io.RandomAccessFile ioStream) {
if (!sizeChanged) {
try {
ioStream.seek(0);
} catch (java.io.IOException e) {
return false;
}
return write(ioStream);
} else {
return false;
}
}
/**
* Get the revision string associated with the given label.
* @param label the label string.
* @return the revision associated with the label or an empty string if no revision is associated with the given label.
*/
public String getRevisionStringForLabel(String label) {
String revisionString = "";
LabelInfo[] labelInfo = getLabelInfo();
for (LabelInfo labelInfo1 : labelInfo) {
if (0 == labelInfo1.getLabelString().compareTo(label)) {
revisionString = labelInfo1.getLabelRevisionString();
break;
}
}
return revisionString;
}
/**
* Supply a useful String representation of the logfile header.
* @return a useful String representation of the logfile header.
*/
@Override
public String toString() {
StringBuilder returnString = new StringBuilder();
// show archive name
returnString.append("\nQVCS workfile name:\t").append(supplementalInfo.getWorkfileCheckedOutToLocation());
// show the owner ID
returnString.append("\nOwner Name:\t").append(owner);
// show the revision count
returnString.append("\nRevision Count:\t").append(logFileHeader.revisionCount());
// show default branch
returnString.append("\nDefault branch:\t");
if (logFileHeader.defaultDepth() == 0) {
returnString.append("TRUNK");
} else {
returnString.append(revisionDescriptor);
}
// show the latest trunk revision
returnString.append("\nLatest trunk revision:\t").append(logFileHeader.latestTrunkRevision());
// show the lock count
returnString.append("\nLock count:\t").append(logFileHeader.lockCount());
// show the comment prefix
returnString.append("\nComment prefix:\t'").append(getCommentPrefix()).append("'");
// show the access list
returnString.append("\nAccess List:\t").append(getAccessList());
// show the modifier list
returnString.append("\nModifier List:\t").append(getModifierList()).append("\n");
// show the attributes
returnString.append(logFileHeader.attributes());
// show the labels
if (logFileHeader.versionCount() > 0) {
returnString.append("Labels:\n");
for (int i = 0; i < logFileHeader.versionCount(); i++) {
returnString.append(labelInfoArray[i]);
}
}
returnString.append("Archive description:\n");
returnString.append(moduleDescription);
return returnString.toString();
}
}