// 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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* QVCS Keyword manager. Expand and contract QVCS keywords.
* @author Jim Voris
*/
public final class QVCSKeywordManager implements KeywordManagerInterface {
// This class is a singleton.
private static final QVCSKeywordManager KEYWORDMANAGER = new QVCSKeywordManager();
// Create our logger object
private static final Logger LOGGER = Logger.getLogger("com.qumasoft.qvcslib.QVCSKeywordManager");
private final String authorKeyword;
private final String commentKeyword;
private final String copyrightKeyword;
private final String dateKeyword;
private final String endlogKeyword;
private final String filenameKeyword;
private final String filePathKeyword;
private final String headerKeyword;
private final String headerPathKeyword;
private final String logKeyword;
private final String logfileKeyword;
private final String ownerKeyword;
private final String revisionKeyword;
private final String verKeyword;
private final String versionKeyword;
private final String labelKeyword;
private final String projectKeyword;
private final String markerCharacter;
private final String terminatorMarker;
private final String noLabelLabel;
private final String multipleLabels;
private final byte keywordPrefix;
private final byte terminatorByte;
private byte[] eol = null;
private byte[] spaces = null;
private String copyrightMessage = null;
private int wordWrapColumn;
private final DateFormat dateFormat = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy");
private final DateFormat timeFormat = new SimpleDateFormat("h:mm:ss aaa");
private DateFormat copyrightYearFormat = null;
// private boolean isBinaryFileFlag = false;
private boolean useUnixPathSeparatorFlag = false;
private KeywordProperties keywordProperties = null;
private static final int DEFAULT_WORD_WRAP_COLUMN = 72;
private static final int RESERVE_SPACE_FOR_BINARY_COPYRIGHT = 4;
private static final int MAXIMUM_CHARACTERS_FOR_LOGX_REVISION_COUNT = 5;
private static final int EXTRACT_COMMENT_START_INDEX_OFFSET = 2;
private static final int EXTRACT_COMMENT_ADJUST_LENGTH = 3;
/**
* Get the singleton keyword manager.
* @return the singleton keyword manager.
*/
public static KeywordManagerInterface getInstance() {
return KEYWORDMANAGER;
}
/**
* Get our own (not the singleton) keyword manager. We build a new one, instead of re-using the singleton.
* @return a new keyword manager.
*/
public static KeywordManagerInterface getNewInstance() {
KeywordManagerInterface keywordManagerInterface;
keywordManagerInterface = new QVCSKeywordManager();
return keywordManagerInterface;
}
/**
* Creates a new instance of QVCSKeywordManager.
*/
private QVCSKeywordManager() {
keywordProperties = new KeywordProperties();
authorKeyword = keywordProperties.getStringValue(KeywordProperties.AUTHOR_KEY);
commentKeyword = keywordProperties.getStringValue(KeywordProperties.COMMENT_KEY);
copyrightKeyword = keywordProperties.getStringValue(KeywordProperties.COPYRIGHT_KEY);
dateKeyword = keywordProperties.getStringValue(KeywordProperties.DATE_KEY);
endlogKeyword = keywordProperties.getStringValue(KeywordProperties.ENDLOG_KEY);
filenameKeyword = keywordProperties.getStringValue(KeywordProperties.FILENAME_KEY);
filePathKeyword = keywordProperties.getStringValue(KeywordProperties.FILEPATH_KEY);
headerKeyword = keywordProperties.getStringValue(KeywordProperties.HEADER_KEY);
headerPathKeyword = keywordProperties.getStringValue(KeywordProperties.HEADERPATH_KEY);
logKeyword = keywordProperties.getStringValue(KeywordProperties.LOG_KEY);
logfileKeyword = keywordProperties.getStringValue(KeywordProperties.LOGFILE_KEY);
ownerKeyword = keywordProperties.getStringValue(KeywordProperties.OWNER_KEY);
revisionKeyword = keywordProperties.getStringValue(KeywordProperties.REVISION_KEY);
verKeyword = keywordProperties.getStringValue(KeywordProperties.VER_KEY);
versionKeyword = keywordProperties.getStringValue(KeywordProperties.VERSION_KEY);
labelKeyword = keywordProperties.getStringValue(KeywordProperties.LABEL_KEY);
projectKeyword = keywordProperties.getStringValue(KeywordProperties.PROJECT_KEY);
markerCharacter = keywordProperties.getStringValue(KeywordProperties.MARKER_TAG);
terminatorMarker = keywordProperties.getStringValue(KeywordProperties.TERMINATOR_MARKER);
wordWrapColumn = keywordProperties.getIntegerValue(KeywordProperties.WORDWRAP_COLUMN_TAG);
noLabelLabel = keywordProperties.getStringValue(KeywordProperties.NO_LABEL_LABEL_TAG);
multipleLabels = keywordProperties.getStringValue(KeywordProperties.MULTIPLE_LABELS_TAG);
eol = keywordProperties.getEOLSequence();
copyrightMessage = keywordProperties.getStringValue(KeywordProperties.COPYRIGHT_MESSAGE_TAG);
useUnixPathSeparatorFlag = keywordProperties.getUseUnixPathSeparator();
if (wordWrapColumn == 0) {
wordWrapColumn = DEFAULT_WORD_WRAP_COLUMN;
}
keywordPrefix = (byte) markerCharacter.charAt(0);
terminatorByte = (byte) terminatorMarker.charAt(0);
copyrightYearFormat = DateFormat.getDateInstance(DateFormat.YEAR_FIELD);
spaces = new byte[2];
spaces[0] = ' ';
spaces[1] = ' ';
}
@Override
public void expandKeywords(final FileInputStream inStream, KeywordExpansionContext keywordExpansionContext) throws java.io.IOException, QVCSException {
RevisionHeader revisionHeader = keywordExpansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(keywordExpansionContext.getRevisionIndex());
if (revisionHeader == null) {
throw new QVCSException("Revision index: " + keywordExpansionContext.getRevisionIndex() + " does not exist!");
}
// Read the entire inStream into a buffer...
FileChannel inputChannel = null;
try {
// We use a channel to figure out the size of the input stream.
inputChannel = inStream.getChannel();
long size = inputChannel.size();
// Read the stream in the vanilla way.
byte[] inputBuffer = new byte[(int) size];
inStream.read(inputBuffer);
expandKeywordsToStream(inputBuffer, revisionHeader, keywordExpansionContext);
} finally {
if (inputChannel != null) {
try {
inputChannel.close();
} catch (IOException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
}
}
}
}
@Override
public void expandKeywords(final byte[] inputBuffer, KeywordExpansionContext keywordExpansionContext) throws java.io.IOException, QVCSException {
RevisionHeader revisionHeader = keywordExpansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(keywordExpansionContext.getRevisionIndex());
if (revisionHeader == null) {
throw new QVCSException("Revision index: " + keywordExpansionContext.getRevisionIndex() + " does not exist!");
}
expandKeywordsToStream(inputBuffer, revisionHeader, keywordExpansionContext);
}
private void expandKeywordsToStream(byte[] inputBuffer, RevisionHeader revisionHeader, KeywordExpansionContext keywordExpansionContext) throws java.io.IOException {
OutputStream outStream = keywordExpansionContext.getOutStream();
int arraySize = inputBuffer.length;
int index;
int startIndex;
int count;
for (index = 0, startIndex = 0, count = 1; index < arraySize; index++, count++) {
// Check for the keyword prefix character
if (inputBuffer[index] == keywordPrefix) {
// Write what we've looked at so far (including the keyword prefix)
outStream.write(inputBuffer, startIndex, count);
count = 0;
if (expandAKeyword(inputBuffer, index, revisionHeader, keywordExpansionContext)) {
// We found a keyword. expandAKeyword expanded it for us,
// and wrote it out. Look for the terminating character so
// we can start copying the rest of the file from there.
do {
index++;
} while (inputBuffer[index] != keywordPrefix);
}
startIndex = index + 1;
}
}
// Write out what's left.
if (startIndex < arraySize) {
outStream.write(inputBuffer, startIndex, arraySize - startIndex);
}
}
private boolean expandAKeyword(byte[] inputBuffer, int index, RevisionHeader revisionHeader, KeywordExpansionContext keywordExpansionContext) throws java.io.IOException {
LogfileInfo logfileInfo = keywordExpansionContext.getLogfileInfo();
OutputStream outStream = keywordExpansionContext.getOutStream();
int revisionIndex = keywordExpansionContext.getRevisionIndex();
File outputFile = keywordExpansionContext.getOutputFile();
String appendedPath = keywordExpansionContext.getAppendedPath();
String labelString = keywordExpansionContext.getLabelString();
AbstractProjectProperties projectProperties = keywordExpansionContext.getProjectProperties();
keywordExpansionContext.setBinaryFileFlag(false);
boolean retVal = false;
byte searchTerminator = keywordPrefix;
int useIndex = index + 1;
LogFileHeaderInfo headerInfo = logfileInfo.getLogFileHeaderInfo();
AtomicInteger revisionCountForLogX = new AtomicInteger(1);
// Make some adjustments for binary files.
if (headerInfo.getLogFileHeader().attributes().getIsBinaryfile()) {
keywordExpansionContext.setBinaryFileFlag(true);
searchTerminator = terminatorByte;
}
// We are positioned at the keyword candidate... See if it's a keyword we know.
if (isKeyword(inputBuffer, useIndex, authorKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
AccessList modifierList = new AccessList(headerInfo.getModifierList());
String author;
if (keywordExpansionContext.getBinaryFileFlag()) {
author = "Bogus Author";
} else {
author = modifierList.indexToUser(revisionHeader.getCreatorIndex());
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(authorKeyword, author, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(authorKeyword, author, outStream);
}
retVal = true;
} else if (isCopyrightKeyword(inputBuffer, useIndex, copyrightKeyword, keywordExpansionContext)) {
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryCopyrightKeyword(copyrightKeyword, revisionIndex, copyrightMessage, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeCopyrightKeyword(copyrightKeyword, revisionIndex, logfileInfo, copyrightMessage, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, dateKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String dateTime;
if (keywordExpansionContext.getBinaryFileFlag()) {
dateTime = "Bogus DateTime";
} else {
dateTime = dateFormat.format(revisionHeader.getCheckInDate()) + " "
+ timeFormat.format(revisionHeader.getCheckInDate());
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(dateKeyword, dateTime, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(dateKeyword, dateTime, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, filenameKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String fileName = outputFile.getCanonicalPath();
fileName = formatFilename(fileName);
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(filenameKeyword, fileName, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(filenameKeyword, fileName, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, filePathKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String fileName;
if (appendedPath.length() > 0) {
fileName = appendedPath + "/" + logfileInfo.getShortWorkfileName();
} else {
fileName = logfileInfo.getShortWorkfileName();
}
fileName = formatFilename(fileName);
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(filePathKeyword, fileName, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(filePathKeyword, fileName, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, headerKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
retVal = expandHeaderKeyword(revisionHeader, headerInfo, inputBuffer, useIndex, keywordExpansionContext);
} else if (isKeyword(inputBuffer, useIndex, headerPathKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
retVal = expandHeaderPathKeyword(revisionHeader, headerInfo, inputBuffer, useIndex, keywordExpansionContext);
} else if (isKeyword(inputBuffer, useIndex, logKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
if (!keywordExpansionContext.getBinaryFileFlag()) {
writeLog(logfileInfo, revisionIndex, outStream, appendedPath, projectProperties, -1);
retVal = true;
}
} else if (isLogXKeyword(inputBuffer, useIndex, logKeyword, revisionCountForLogX, searchTerminator)) {
if (!keywordExpansionContext.getBinaryFileFlag()) {
writeLog(logfileInfo, revisionIndex, outStream, appendedPath, projectProperties, revisionCountForLogX.get());
retVal = true;
}
} else if (isKeyword(inputBuffer, useIndex, logfileKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String logfileName;
if (keywordExpansionContext.getBinaryFileFlag()) {
logfileName = "Bogus Logfilename";
} else {
logfileName = formatArchiveFilename(logfileInfo, appendedPath, projectProperties);
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(logfileKeyword, logfileName, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(logfileKeyword, logfileName, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, ownerKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String owner;
if (keywordExpansionContext.getBinaryFileFlag()) {
owner = "Bogus owner";
} else {
owner = headerInfo.getOwner();
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(ownerKeyword, owner, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(ownerKeyword, owner, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, revisionKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String revision;
if (keywordExpansionContext.getBinaryFileFlag()) {
revision = "Bogus revision";
} else {
revision = revisionHeader.getRevisionString();
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(revisionKeyword, revision, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(revisionKeyword, revision, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, versionKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String useLabelString;
if (keywordExpansionContext.getBinaryFileFlag()) {
useLabelString = "Bogus version";
} else {
LabelInfo[] labelInfo = headerInfo.getLabelInfo();
useLabelString = deduceLabelString(labelInfo, revisionHeader, labelString);
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(versionKeyword, useLabelString, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(versionKeyword, useLabelString, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, labelKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String useLabelString;
if (keywordExpansionContext.getBinaryFileFlag()) {
useLabelString = "Bogus label";
} else {
LabelInfo[] labelInfo = headerInfo.getLabelInfo();
useLabelString = deduceLabelString(labelInfo, revisionHeader, labelString);
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(labelKeyword, useLabelString, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(labelKeyword, useLabelString, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, projectKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String projectString;
if (keywordExpansionContext.getBinaryFileFlag()) {
projectString = "Bogus Project";
} else {
projectString = projectProperties.getProjectName();
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(projectKeyword, projectString, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(projectKeyword, projectString, outStream);
}
retVal = true;
} else if (isKeyword(inputBuffer, useIndex, verKeyword, searchTerminator, keywordExpansionContext.getBinaryFileFlag())) {
String verKeywordValue;
if (keywordExpansionContext.getBinaryFileFlag()) {
verKeywordValue = "Bogus VER";
} else {
String useLabelString;
LabelInfo[] labelInfo = headerInfo.getLabelInfo();
String fileNamePrefix = outputFile.getName();
fileNamePrefix = fileNamePrefix.substring(0, fileNamePrefix.indexOf('.'));
useLabelString = deduceLabelString(labelInfo, revisionHeader, labelString);
verKeywordValue = fileNamePrefix + " " + useLabelString;
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(verKeyword, verKeywordValue, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(verKeyword, verKeywordValue, outStream);
}
retVal = true;
}
return retVal;
}
private boolean isKeyword(byte[] inputBuffer, int index, String keyword, byte searchTerminator, boolean binaryFileFlag) {
boolean retVal;
// It can only be a keyword if there is room in the input buffer for it.
if (index + keyword.length() > inputBuffer.length) {
retVal = false;
} else {
retVal = false;
String candidateString = new String(inputBuffer, index, keyword.length());
if (candidateString.compareTo(keyword) == 0) {
// Make sure the following byte is the expected terminator.
if (inputBuffer[index + keyword.length()] == searchTerminator) {
if (binaryFileFlag) {
retVal = isExpandedKeyword(inputBuffer, index, keyword, new AtomicInteger());
} else {
retVal = true;
}
}
}
}
return retVal;
}
private boolean isCopyrightKeyword(byte[] inputBuffer, int index, String keyword, KeywordExpansionContext keywordExpansionContext) {
boolean retVal;
// It can only be a keyword if there is room in the input buffer for it.
if (index + keyword.length() > inputBuffer.length) {
retVal = false;
} else {
retVal = false;
String candidateString = new String(inputBuffer, index, keyword.length());
if (candidateString.compareTo(keyword) == 0) {
if (keywordExpansionContext.getBinaryFileFlag()) {
// Make sure the following byte is the expected terminator.
if (inputBuffer[index + keyword.length()] == (byte) ' ') {
retVal = isExpandedCopyrightKeyword(inputBuffer, index, keyword, new AtomicInteger());
}
} else {
// Make sure the following byte is the expected terminator.
if (inputBuffer[index + keyword.length()] == keywordPrefix) {
retVal = true;
}
}
}
}
return retVal;
}
private boolean isLogXKeyword(byte[] inputBuffer, int index, String keyword, AtomicInteger revisionCount, byte searchTerminator) {
boolean retVal;
// It can only be a keyword if there is room in the input buffer for it.
if (index + keyword.length() > inputBuffer.length) {
retVal = false;
} else {
retVal = false;
String candidateString = new String(inputBuffer, index, keyword.length());
if (candidateString.compareTo(keyword) == 0) {
// Make sure the following byte(s) are numbers followed by the
// terminator character.
int numberIndex = index + keyword.length();
int startNumberIndex = numberIndex;
int bufferSize = inputBuffer.length;
byte contents = inputBuffer[numberIndex];
while (numberIndex < bufferSize) {
contents = inputBuffer[numberIndex++];
if ((contents < '0') || (contents > '9')) {
break;
}
}
if (contents == searchTerminator) {
retVal = true;
try {
String revisionCountString = new String(inputBuffer, startNumberIndex, numberIndex - startNumberIndex - 1);
revisionCount.set(Integer.parseInt(revisionCountString));
} catch (NumberFormatException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
revisionCount.set(1);
}
}
}
}
return retVal;
}
private String formatArchiveFilename(LogfileInfo logfileInfo, String appendedPath, AbstractProjectProperties projectProperties) {
String retVal;
String temp = QVCSConstants.QVCS_REMOTE_PROJECT_TYPE + "//" + projectProperties.getProjectName() + "//" + appendedPath + File.separator
+ logfileInfo.getShortWorkfileName();
retVal = convertToCanonicalFormat(temp, projectProperties);
return retVal;
}
private String formatFilename(String fileName) {
String formattedFileName = fileName;
if (useUnixPathSeparatorFlag) {
byte[] fileNameBytes = fileName.getBytes();
for (int i = 0; i < fileNameBytes.length; i++) {
if (fileNameBytes[i] == '\\') {
fileNameBytes[i] = '/';
}
}
formattedFileName = new String(fileNameBytes);
}
return formattedFileName;
}
private String convertToCanonicalFormat(String inputName, AbstractProjectProperties projectProperties) {
String retVal = inputName;
if (projectProperties.isRemoteProject() || useUnixPathSeparatorFlag) {
byte[] bytes = inputName.getBytes();
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] == '\\') {
bytes[i] = '/';
}
}
retVal = new String(bytes);
}
return retVal;
}
private String deduceLabelString(LabelInfo[] labelInfos, RevisionHeader revisionHeader, String inputLabelString) {
String labelString = noLabelLabel;
String revisionString = revisionHeader.getRevisionString();
boolean labelFoundFlag = false;
if (inputLabelString == null) {
if (labelInfos != null) {
for (LabelInfo labelInfo : labelInfos) {
if (labelInfo.getLabelRevisionString().compareTo(revisionString) == 0) {
if (!labelFoundFlag) {
labelString = labelInfo.getLabelString();
labelFoundFlag = true;
} else {
labelString = multipleLabels;
break;
}
}
}
if (!labelFoundFlag) {
// See if a floating label matches.
if (revisionHeader.isTip()) {
revisionString = revisionString.substring(0, revisionString.lastIndexOf('.'));
for (LabelInfo labelInfo : labelInfos) {
if (labelInfo.getLabelRevisionString().compareTo(revisionString) == 0) {
labelString = labelInfo.getLabelString();
break;
}
}
}
}
}
} else {
for (LabelInfo labelInfo : labelInfos) {
if (labelInfo.getLabelString().compareTo(inputLabelString) == 0) {
labelFoundFlag = true;
break;
}
}
if (labelFoundFlag) {
labelString = inputLabelString;
}
}
return labelString;
}
private void writeKeyword(String keyword, String keywordInsertionValue, final OutputStream outStream) throws java.io.IOException {
outStream.write(keyword.getBytes()); // write the keyword
outStream.write(terminatorByte); // write the ':' byte
outStream.write(' '); // write a space
outStream.write(keywordInsertionValue.getBytes());
outStream.write(' '); // write a space
outStream.write(keywordPrefix); // write the trailing '$'
}
private void writeBinaryKeyword(String keyword, String keywordInsertionValue, final OutputStream outStream, byte[] inputBuffer, int index,
KeywordExpansionContext keywordExpansionContext) throws java.io.IOException {
// Find the trailing '$' character. Note that we are guaranteed that
// the trailing '$' character exists because the isKeyword method makes
// that guarantee for binary files.
int i;
for (i = index + keyword.length(); i < inputBuffer.length; i++) {
if (inputBuffer[i] == keywordPrefix) {
break;
}
}
// Figure out how many characters we can write.
int bytesAvailableToWrite = i - index + 1;
byte[] bytesToWrite = new byte[bytesAvailableToWrite];
// Fill this up with space characters.
for (int j = 0; j < bytesAvailableToWrite; j++) {
bytesToWrite[j] = ' ';
}
// Copy the keyword into the array.
byte[] keywordBytes = keyword.getBytes();
for (i = 0; i < keywordBytes.length; i++) {
bytesToWrite[i] = keywordBytes[i];
}
// Put the ':' and ' ' after the keyword.
bytesToWrite[i++] = terminatorByte;
bytesToWrite[i++] = ' ';
if (!keywordExpansionContext.getBinaryFileFlag()) {
// Copy the insertion value into this array.
byte[] insertionBytes = keywordInsertionValue.getBytes();
for (int j = 0; i < bytesAvailableToWrite && j < keywordInsertionValue.length(); j++, i++) {
bytesToWrite[i] = insertionBytes[j];
}
}
// The last two bytes are ALWAYS space followed by the '$' character.
bytesToWrite[bytesAvailableToWrite - 2] = ' ';
bytesToWrite[bytesAvailableToWrite - 1] = '$';
// And finally write this puppy out.
outStream.write(bytesToWrite);
}
private void writeCopyrightKeyword(String keyword, int revisionIndex, LogfileInfo logfileInfo, String keywordInsertionValue,
final OutputStream outStream) throws java.io.IOException {
outStream.write(keyword.getBytes()); // write the keyword
outStream.write(' '); // write a space
outStream.write(deduceCopyrightYears(revisionIndex, logfileInfo).getBytes());
outStream.write(' '); // write a space
outStream.write(keywordInsertionValue.getBytes());
outStream.write(' '); // write a space
outStream.write(keywordPrefix); // write the trailing '$'
}
private void writeBinaryCopyrightKeyword(String keyword, int revisionIndex, String keywordInsertionValue,
byte[] inputBuffer, int index, KeywordExpansionContext keywordExpansionContext) throws java.io.IOException {
LogfileInfo logfileInfo = keywordExpansionContext.getLogfileInfo();
OutputStream outStream = keywordExpansionContext.getOutStream();
// We have to use byte arrays in order to get the copyright
// character to work correctly. Otherwise, we could just just StringBuffer
// to do the concatenation.
String copyrightInsertionValue;
if (keywordExpansionContext.getBinaryFileFlag()) {
copyrightInsertionValue = "Bogus Copyright";
} else {
String copyrightYears = deduceCopyrightYears(revisionIndex, logfileInfo);
byte[] insertionBytes = new byte[RESERVE_SPACE_FOR_BINARY_COPYRIGHT + copyrightYears.length() + keywordInsertionValue.length()];
int i = 0;
insertionBytes[i++] = ' ';
insertionBytes[i++] = ' ';
insertionBytes[i++] = ' ';
byte[] copyrightYearBytes = copyrightYears.getBytes();
for (int j = 0; j < copyrightYears.length(); i++, j++) {
insertionBytes[i] = copyrightYearBytes[j];
}
insertionBytes[i++] = ' ';
byte[] keywordBytes = keywordInsertionValue.getBytes();
for (int j = 0; j < keywordInsertionValue.length(); i++, j++) {
insertionBytes[i] = keywordBytes[j];
}
copyrightInsertionValue = new String(insertionBytes);
}
// Find the trailing '$' character. Note that we are guaranteed that
// the trailing '$' character exists because the isKeyword method makes
// that guarantee for binary files.
int i;
for (i = index + keyword.length(); i < inputBuffer.length; i++) {
if (inputBuffer[i] == keywordPrefix) {
break;
}
}
// Figure out how many characters we can write.
int bytesAvailableToWrite = i - index + 1;
byte[] bytesToWrite = new byte[bytesAvailableToWrite];
// Fill this up with space characters.
for (int j = 0; j < bytesAvailableToWrite; j++) {
bytesToWrite[j] = ' ';
}
// Copy the keyword into the array.
byte[] keywordBytes = keyword.getBytes();
for (i = 0; i < keywordBytes.length; i++) {
bytesToWrite[i] = keywordBytes[i];
}
// Put the ':' and ' ' after the keyword.
bytesToWrite[i++] = ' ';
bytesToWrite[i++] = ' ';
bytesToWrite[i++] = ' ';
if (!keywordExpansionContext.getBinaryFileFlag()) {
// Copy the insertion value into this array.
byte[] insertionBytes = copyrightInsertionValue.getBytes();
for (int j = 0; i < bytesAvailableToWrite && j < copyrightInsertionValue.length(); j++, i++) {
bytesToWrite[i] = insertionBytes[j];
}
}
// The last two bytes are ALWAYS space followed by the '$' character.
bytesToWrite[bytesAvailableToWrite - 2] = ' ';
bytesToWrite[bytesAvailableToWrite - 1] = '$';
// And finally write this puppy out.
outStream.write(bytesToWrite);
}
private void writeLog(LogfileInfo logfileInfo,
int revisionIndex,
final OutputStream outStream,
String appendedPath,
AbstractProjectProperties projectProperties,
int revisionCountForLogX) throws java.io.IOException {
byte[] commentPrefix = logfileInfo.getLogFileHeaderInfo().getCommentPrefix().getBytes();
int useRevisionCountForLogX = revisionCountForLogX;
// Write the header portion of the log;
outStream.write(logKeyword.getBytes()); // write the keyword
if (revisionCountForLogX > 0) {
outStream.write(Integer.toString(revisionCountForLogX).getBytes());
}
outStream.write(terminatorByte); // write the ':' byte
outStream.write(' '); // write a space
outStream.write(formatArchiveFilename(logfileInfo, appendedPath, projectProperties).getBytes());
outStream.write(' '); // write a space
outStream.write(keywordPrefix); // write the trailing '$'
outStream.write(eol);
outStream.write(commentPrefix);
outStream.write(eol);
writeDescriptionString(commentPrefix, outStream, logfileInfo.getLogFileHeaderInfo().getModuleDescription());
if (revisionCountForLogX < 0) {
useRevisionCountForLogX = logfileInfo.getLogFileHeaderInfo().getRevisionCount();
} else {
if (revisionCountForLogX > logfileInfo.getLogFileHeaderInfo().getRevisionCount()) {
useRevisionCountForLogX = logfileInfo.getLogFileHeaderInfo().getRevisionCount();
}
}
int[] revIndexes = deduceRevisionsToShow(revisionIndex, logfileInfo);
outStream.write(commentPrefix);
RevisionInformation revisionInformation = logfileInfo.getRevisionInformation();
for (int i = 0; i < useRevisionCountForLogX && i < revIndexes.length; i++) {
writeRevisionInfo(logfileInfo, revisionInformation.getRevisionHeader(revIndexes[i]), outStream);
}
outStream.write(eol);
outStream.write(commentPrefix);
outStream.write(keywordPrefix);
outStream.write(endlogKeyword.getBytes());
outStream.write(keywordPrefix);
}
private void writeRevisionInfo(LogfileInfo logfileInfo, RevisionHeader revisionHeader, final OutputStream outStream) throws java.io.IOException {
byte[] commentPrefix = logfileInfo.getLogFileHeaderInfo().getCommentPrefix().getBytes();
AccessList modifierList = new AccessList(logfileInfo.getLogFileHeaderInfo().getModifierList());
outStream.write(eol);
outStream.write(commentPrefix);
outStream.write(revisionKeyword.getBytes());
outStream.write(' ');
outStream.write(revisionHeader.getRevisionString().getBytes());
outStream.write(' ');
outStream.write(authorKeyword.getBytes());
outStream.write(terminatorByte); // write the ':' byte
outStream.write(' ');
outStream.write(modifierList.indexToUser(revisionHeader.getCreatorIndex()).getBytes());
outStream.write(' ');
outStream.write(dateKeyword.getBytes());
outStream.write(terminatorByte); // write the ':' byte
outStream.write(' ');
String dateTime = dateFormat.format(revisionHeader.getCheckInDate()) + " "
+ timeFormat.format(revisionHeader.getCheckInDate());
outStream.write(dateTime.getBytes());
outStream.write(eol);
writeDescriptionString(commentPrefix, outStream, revisionHeader.getRevisionDescription());
outStream.write(commentPrefix);
}
private String deduceCopyrightYears(int revisionIndex, LogfileInfo logfileInfo) throws java.io.IOException {
RevisionInformation revisionInformation = logfileInfo.getRevisionInformation();
RevisionHeader revisionHeader = revisionInformation.getRevisionHeader(revisionIndex);
int revisionCount = logfileInfo.getLogFileHeaderInfo().getRevisionCount();
long newestRevisionSeconds = Long.MIN_VALUE;
long oldestRevisionSeconds = Long.MAX_VALUE;
String copyrightYears;
// The idea is to walk the set of revisions looking for those that are included
// given that the newest revision is the one passed in.
if (revisionHeader.getDepth() == 0) {
// We are dealing with the tip revision. This is the easy case.
for (int i = revisionIndex; i < revisionCount; i++) {
RevisionHeader currentRevision = revisionInformation.getRevisionHeader(i);
if (currentRevision.getDepth() == 0) {
long revisionCheckInTime = currentRevision.getCheckInDate().getTime();
if (revisionCheckInTime > newestRevisionSeconds) {
newestRevisionSeconds = revisionCheckInTime;
}
if (revisionCheckInTime < oldestRevisionSeconds) {
oldestRevisionSeconds = revisionCheckInTime;
}
}
}
} else {
// For branch revisions, we need to look through all revisions,
// since some of the revisions we show may be located at a lower
// revision index than the one we are expanding the log for.
for (int i = 0; i < revisionCount; i++) {
RevisionHeader currentRevision = revisionInformation.getRevisionHeader(i);
if (isAncestor(currentRevision, revisionHeader)) {
long revisionCheckInTime = currentRevision.getCheckInDate().getTime();
if (revisionCheckInTime > newestRevisionSeconds) {
newestRevisionSeconds = revisionCheckInTime;
}
if (revisionCheckInTime < oldestRevisionSeconds) {
oldestRevisionSeconds = revisionCheckInTime;
}
}
}
}
// Get the respective years
Date oldestDate = new Date(oldestRevisionSeconds);
FieldPosition oldestDateFieldPosition = new FieldPosition(DateFormat.YEAR_FIELD);
StringBuffer oldestDateBuffer = new StringBuffer();
oldestDateBuffer = copyrightYearFormat.format(oldestDate, oldestDateBuffer, oldestDateFieldPosition);
String oldestYear = oldestDateBuffer.substring(oldestDateFieldPosition.getBeginIndex(), oldestDateFieldPosition.getEndIndex());
Date newestDate = new Date(newestRevisionSeconds);
FieldPosition newestDateFieldPosition = new FieldPosition(DateFormat.YEAR_FIELD);
StringBuffer newestDateBuffer = new StringBuffer();
newestDateBuffer = copyrightYearFormat.format(newestDate, newestDateBuffer, newestDateFieldPosition);
String newestYear = newestDateBuffer.substring(newestDateFieldPosition.getBeginIndex(), newestDateFieldPosition.getEndIndex());
if (0 == oldestYear.compareTo(newestYear)) {
copyrightYears = oldestYear;
} else {
copyrightYears = oldestYear + "-" + newestYear;
}
return copyrightYears;
}
private int[] deduceRevisionsToShow(int revisionIndex, LogfileInfo logfileInfo) throws java.io.IOException {
RevisionInformation revisionInformation = logfileInfo.getRevisionInformation();
RevisionHeader revisionHeader = revisionInformation.getRevisionHeader(revisionIndex);
int revisionCount = logfileInfo.getLogFileHeaderInfo().getRevisionCount();
Map<String, Integer> revisions = Collections.synchronizedMap(new TreeMap<String, Integer>());
// The idea is to walk the set of revisions looking for those that need to be displayed,
// given that the newest revision is the one passed in.
if (revisionHeader.getDepth() == 0) {
// We are dealing with the tip revision. This is the easy case.
for (int i = revisionIndex; i < revisionCount; i++) {
RevisionHeader currentRevision = revisionInformation.getRevisionHeader(i);
if (currentRevision.getDepth() == 0) {
revisions.put(currentRevision.getRevisionDescriptor().toString(), Integer.valueOf(i));
}
}
} else {
// For branch revisions, we need to look through all revisions,
// since some of the revisions we show may be located at a lower
// revision index than the one we are expanding the log for.
for (int i = 0; i < revisionCount; i++) {
RevisionHeader currentRevision = revisionInformation.getRevisionHeader(i);
if (isAncestor(currentRevision, revisionHeader)) {
revisions.put(currentRevision.getRevisionDescriptor().toString(), Integer.valueOf(i));
}
}
}
// Bundle the result in a nice package.
int[] returnedIndexes = new int[revisions.size()];
int maxIndex = revisions.size() - 1;
Iterator<Integer> iterator = revisions.values().iterator();
for (int i = 0; iterator.hasNext(); i++) {
Integer integerIndex = iterator.next();
returnedIndexes[maxIndex - i] = integerIndex.intValue();
}
return returnedIndexes;
}
private boolean isAncestor(RevisionHeader ancestorCandidate, RevisionHeader decendantRevision) {
boolean retVal;
if (ancestorCandidate.getDepth() > decendantRevision.getDepth()) {
// There is no way for a revision that is deeper than the decendant
// to be an ancestor of that decendant.
retVal = false;
} else if (ancestorCandidate.getDepth() == decendantRevision.getDepth()) {
// If the depths are equal, then all elements except the final minor
// number must match; the final minor number must be less than the
// minor number of the decendant.
int i;
retVal = true;
for (i = 0; retVal && (i < ancestorCandidate.getDepth()); i++) {
MajorMinorRevisionPair ancestorPair = ancestorCandidate.getRevisionDescriptor().getRevisionPairs()[i];
MajorMinorRevisionPair decendantPair = decendantRevision.getRevisionDescriptor().getRevisionPairs()[i];
if (ancestorPair.getMajorNumber() == decendantPair.getMajorNumber()
&& ancestorPair.getMinorNumber() == decendantPair.getMinorNumber()) {
continue;
} else {
retVal = false;
}
}
// Look at the last pair...
if (retVal) {
MajorMinorRevisionPair ancestorPair = ancestorCandidate.getRevisionDescriptor().getRevisionPairs()[i];
MajorMinorRevisionPair decendantPair = decendantRevision.getRevisionDescriptor().getRevisionPairs()[i];
if (ancestorPair.getMajorNumber() == decendantPair.getMajorNumber()) {
if (ancestorPair.getMinorNumber() > decendantPair.getMinorNumber()) {
retVal = false;
}
} else {
retVal = false;
}
}
} else {
// If the candidate depth is less than the decendant, then
// all the major/minor pairs of the candidate must match the
// decendant's.
int i;
retVal = true;
for (i = ancestorCandidate.getDepth(); retVal && (i > 0); i--) {
MajorMinorRevisionPair ancestorPair = ancestorCandidate.getRevisionDescriptor().getRevisionPairs()[i];
MajorMinorRevisionPair decendantPair = decendantRevision.getRevisionDescriptor().getRevisionPairs()[i];
if (ancestorPair.getMajorNumber() == decendantPair.getMajorNumber()
&& ancestorPair.getMinorNumber() == decendantPair.getMinorNumber()) {
continue;
} else {
retVal = false;
}
}
// For trunk revisions, the major number must be <=, and the minor number must be lower
if (retVal) {
MajorMinorRevisionPair ancestorPair = ancestorCandidate.getRevisionDescriptor().getRevisionPairs()[0];
MajorMinorRevisionPair decendantPair = decendantRevision.getRevisionDescriptor().getRevisionPairs()[0];
if (ancestorPair.getMajorNumber() > decendantPair.getMajorNumber()) {
retVal = false;
} else {
if (ancestorPair.getMinorNumber() > decendantPair.getMinorNumber()) {
retVal = false;
}
}
}
}
return retVal;
}
private void writeDescriptionString(byte[] commentPrefix, final OutputStream outStream, String description) throws java.io.IOException {
byte[] descriptionBuffer = description.getBytes();
// First convert any newline characters to spaces so we can put
// word wrap in whatever column the property file says.
for (int i = 0; i < descriptionBuffer.length; i++) {
if (descriptionBuffer[i] == '\n') {
descriptionBuffer[i] = ' ';
}
}
// Figure out where to word wrap.
int column = 0;
int preceedingSpaceIndex = 0;
for (int i = 0; i < descriptionBuffer.length; i++, column++) {
if (descriptionBuffer[i] == ' ') {
if ((preceedingSpaceIndex > 0) && (column > wordWrapColumn)) {
descriptionBuffer[preceedingSpaceIndex] = '\n';
column = 0;
}
preceedingSpaceIndex = i;
}
}
// Write the description to the stream, each line preceeded by the
// comment prefix.
int startingIndex = 0;
for (int i = 0; i < descriptionBuffer.length; i++, column++) {
if (descriptionBuffer[i] == '\n') {
outStream.write(commentPrefix);
outStream.write(spaces);
outStream.write(descriptionBuffer, startingIndex, i - startingIndex);
outStream.write(eol);
startingIndex = ++i;
}
}
// Write out any remaining description.
if (startingIndex < descriptionBuffer.length) {
outStream.write(commentPrefix);
outStream.write(spaces);
outStream.write(descriptionBuffer, startingIndex, descriptionBuffer.length - startingIndex);
outStream.write(eol);
}
}
@Override
public void contractKeywords(final FileInputStream inStream, final OutputStream outStream, AtomicReference<String> checkInComment,
AbstractProjectProperties projectProperties, boolean binaryFileFlag) throws java.io.IOException {
// Read the entire inStream into a buffer...
FileChannel inputChannel = null;
try {
// We use a channel to figure out the size of the input stream.
inputChannel = inStream.getChannel();
long size = inputChannel.size();
// Read the stream in the vanilla way.
byte[] inputBuffer = new byte[(int) size];
inStream.read(inputBuffer);
contractKeywordsToStream(inputBuffer, outStream, checkInComment, binaryFileFlag);
} finally {
if (inputChannel != null) {
try {
inputChannel.close();
} catch (IOException e) {
LOGGER.log(Level.WARNING, Utility.expandStackTraceToString(e));
}
}
}
}
private void contractKeywordsToStream(byte[] inputBuffer, final OutputStream outStream, AtomicReference<String> checkInComment,
boolean binaryFileFlag) throws java.io.IOException {
int arraySize = inputBuffer.length;
int index;
int startIndex;
int count;
AtomicInteger continueIndex = new AtomicInteger();
for (index = 0, startIndex = 0, count = 0; index < arraySize; index++, count++) {
// Check for the keyword prefix character
if (inputBuffer[index] == keywordPrefix) {
// Write what we've looked at so far (including the keyword prefix)
outStream.write(inputBuffer, startIndex, count);
count = 0;
if (contractAKeyword(inputBuffer, index, outStream, checkInComment, continueIndex, binaryFileFlag)) {
// We found a keyword. contractAKeyword contracted it for us,
// and wrote it out. This is where we continue.
index = continueIndex.get();
}
startIndex = index;
}
}
// Write out what's left.
if (startIndex < arraySize) {
outStream.write(inputBuffer, startIndex, arraySize - startIndex);
}
}
private boolean contractAKeyword(byte[] inputBuffer, int index, final OutputStream outStream, AtomicReference<String> checkInComment,
AtomicInteger continueIndex, boolean binaryFileFlag) throws java.io.IOException {
boolean retVal = false;
int useIndex = index + 1;
AtomicInteger revisionCountForLogX = new AtomicInteger(1);
// We are positioned at the keyword candidate... See if it's a keyword we know.
if (isExpandedKeyword(inputBuffer, useIndex, authorKeyword, continueIndex)) {
writeContractedKeyword(authorKeyword, outStream);
retVal = true;
} else if (isExpandedCopyrightKeyword(inputBuffer, useIndex, copyrightKeyword, continueIndex)) {
writeContractedKeyword(copyrightKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, dateKeyword, continueIndex)) {
writeContractedKeyword(dateKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, filenameKeyword, continueIndex)) {
writeContractedKeyword(filenameKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, filePathKeyword, continueIndex)) {
writeContractedKeyword(filePathKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, headerKeyword, continueIndex)) {
writeContractedKeyword(headerKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, headerPathKeyword, continueIndex)) {
writeContractedKeyword(headerPathKeyword, outStream);
retVal = true;
} else if (isExpandedLogKeyword(inputBuffer, useIndex, logKeyword, continueIndex, binaryFileFlag)) {
writeContractedKeyword(logKeyword, outStream);
retVal = true;
} else if (isExpandedLogXKeyword(inputBuffer, useIndex, logKeyword, revisionCountForLogX, continueIndex, binaryFileFlag)) {
writeContractedLogXKeyword(logKeyword, outStream, revisionCountForLogX.get());
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, logfileKeyword, continueIndex)) {
writeContractedKeyword(logfileKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, ownerKeyword, continueIndex)) {
writeContractedKeyword(ownerKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, revisionKeyword, continueIndex)) {
writeContractedKeyword(revisionKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, versionKeyword, continueIndex)) {
writeContractedKeyword(versionKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, labelKeyword, continueIndex)) {
writeContractedKeyword(labelKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, projectKeyword, continueIndex)) {
writeContractedKeyword(projectKeyword, outStream);
retVal = true;
} else if (isExpandedKeyword(inputBuffer, useIndex, verKeyword, continueIndex)) {
writeContractedKeyword(verKeyword, outStream);
retVal = true;
} else if (isExpandedCommentKeyword(inputBuffer, useIndex, commentKeyword, checkInComment, continueIndex)) {
// Note that we don't write anything for this one!!
retVal = true;
}
return retVal;
}
private boolean isExpandedKeyword(byte[] inputBuffer, int index, String keyword, AtomicInteger continueIndex) {
boolean retVal;
// It can only be a keyword if there is room in the input buffer for it.
if (index + keyword.length() > inputBuffer.length) {
retVal = false;
} else {
retVal = false;
String candidateString = new String(inputBuffer, index, keyword.length());
if (candidateString.compareTo(keyword) == 0) {
// Make sure the following byte is the expected terminator.
if ((index + keyword.length() < inputBuffer.length)
&& inputBuffer[index + keyword.length()] == terminatorByte) {
// Great. Now we need to make sure that we run into a '$'
// character before we run into the eol sequence.
int tempIndex = index + keyword.length();
boolean continueFlag = true;
while (continueFlag && (tempIndex < inputBuffer.length)) {
if (inputBuffer[tempIndex] == keywordPrefix) {
retVal = true;
continueIndex.set(tempIndex + 1);
break;
}
boolean matchEOLFlag = true;
for (int i = 0; (i < eol.length) && (tempIndex + i < inputBuffer.length); i++) {
if (inputBuffer[tempIndex + i] != eol[i]) {
matchEOLFlag = false;
break;
}
}
if (matchEOLFlag) {
continueFlag = false;
}
tempIndex++;
}
}
}
}
return retVal;
}
private boolean isExpandedLogKeyword(byte[] inputBuffer, int index, String keyword, AtomicInteger continueIndex, boolean binaryFileFlag) {
boolean retVal = false;
if (isExpandedKeyword(inputBuffer, index, keyword, continueIndex)) {
// Need to look for the trailing Endlog keyword.
for (int i = continueIndex.get(); i < inputBuffer.length; i++) {
// Check for the keyword prefix character
if (inputBuffer[i] == keywordPrefix) {
if (isKeyword(inputBuffer, i + 1, endlogKeyword, keywordPrefix, binaryFileFlag)) {
retVal = true;
continueIndex.set(i + 2 + endlogKeyword.length());
break;
}
}
}
}
return retVal;
}
private boolean isExpandedLogXKeyword(byte[] inputBuffer, int index, String keyword, AtomicInteger x, AtomicInteger continueIndex, boolean binaryFileFlag) {
boolean retVal;
// It can only be a keyword if there is room in the input buffer for it.
if (index + keyword.length() > inputBuffer.length) {
retVal = false;
} else {
try {
retVal = false;
String candidateString = new String(inputBuffer, index, keyword.length());
if (candidateString.compareTo(keyword) == 0) {
retVal = false;
// Look through the buffer for the ':' character. Assume they are
// going to limit their revision count to less than 99999.
int i;
int bufferIndex = index + keyword.length();
for (i = 0; i + bufferIndex < inputBuffer.length && i < MAXIMUM_CHARACTERS_FOR_LOGX_REVISION_COUNT; i++) {
if (inputBuffer[i + bufferIndex] == terminatorByte) {
retVal = true;
break;
}
}
if (retVal) {
String revCount = new String(inputBuffer, index + keyword.length(), i);
int revCountInt = Integer.parseInt(revCount);
x.set(revCountInt);
String logXKeyword = keyword + revCount;
retVal = isExpandedLogKeyword(inputBuffer, index, logXKeyword, continueIndex, binaryFileFlag);
} else {
retVal = false;
}
}
} catch (NumberFormatException e) {
retVal = false;
}
}
return retVal;
}
private boolean isExpandedCopyrightKeyword(byte[] inputBuffer, int index, String keyword, AtomicInteger continueIndex) {
boolean retVal;
// It can only be a keyword if there is room in the input buffer for it.
if (index + keyword.length() > inputBuffer.length) {
retVal = false;
} else {
retVal = false;
String candidateString = new String(inputBuffer, index, keyword.length());
if (candidateString.compareTo(keyword) == 0) {
// Make sure the following bytes are the expected ones.
if ((index + keyword.length() < inputBuffer.length + 1)
&& (inputBuffer[index + keyword.length()] == ' ')) {
// Great. Now we need to make sure that we run into a '$'
// character before we run into the eol sequence.
int tempIndex = index + keyword.length();
boolean continueFlag = true;
while (continueFlag && (tempIndex < inputBuffer.length)) {
if (inputBuffer[tempIndex] == keywordPrefix) {
retVal = true;
continueIndex.set(tempIndex + 1);
break;
}
boolean matchEOLFlag = true;
for (int i = 0; (i < eol.length) && (tempIndex + i < inputBuffer.length); i++) {
if (inputBuffer[tempIndex + i] != eol[i]) {
matchEOLFlag = false;
break;
}
}
if (matchEOLFlag) {
continueFlag = false;
}
tempIndex++;
}
}
}
}
return retVal;
}
private boolean isExpandedCommentKeyword(byte[] inputBuffer, int index, String keyword, AtomicReference<String> checkInComment, AtomicInteger continueIndex) {
boolean retVal = false;
if (isExpandedKeyword(inputBuffer, index, keyword, continueIndex)) {
String commentString = new String(inputBuffer, index + keyword.length() + EXTRACT_COMMENT_START_INDEX_OFFSET, continueIndex.get() - index - keyword.length()
- EXTRACT_COMMENT_ADJUST_LENGTH);
String existingComment = checkInComment.get();
if ((existingComment != null) && (existingComment.length() > 0)) {
checkInComment.set(existingComment + "; " + commentString);
} else {
checkInComment.set(commentString);
}
retVal = true;
}
return retVal;
}
private void writeContractedKeyword(String keyword, final OutputStream outStream) throws java.io.IOException {
outStream.write(keywordPrefix); // write the leading '$'
outStream.write(keyword.getBytes()); // write the keyword
outStream.write(keywordPrefix); // write the trailing '$'
}
private void writeContractedLogXKeyword(String keyword, final OutputStream outStream, int x) throws java.io.IOException {
outStream.write(keywordPrefix); // write the leading '$'
outStream.write(keyword.getBytes()); // write the keyword
outStream.write(Integer.toString(x).getBytes()); // write the X
outStream.write(keywordPrefix); // write the trailing '$'
}
/**
* Get the version keyword.
* @return the version keyword.
*/
public String getVersionKeyword() {
return versionKeyword;
}
private boolean expandHeaderKeyword(RevisionHeader revisionHeader, LogFileHeaderInfo headerInfo,
byte[] inputBuffer, int useIndex, KeywordExpansionContext keywordExpansionContext) throws IOException {
String dateTime;
String headerString;
File outputFile = keywordExpansionContext.getOutputFile();
String workfileName = outputFile.getName();
OutputStream outStream = keywordExpansionContext.getOutStream();
if (keywordExpansionContext.getBinaryFileFlag()) {
headerString = "Bogus Header String";
} else {
dateTime = dateFormat.format(revisionHeader.getCheckInDate()) + " "
+ timeFormat.format(revisionHeader.getCheckInDate());
headerString = workfileName + " " + revisionKeyword + ":" + revisionHeader.getRevisionString() + " " + dateTime + " " + headerInfo.getOwner();
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(headerKeyword, headerString, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(headerKeyword, headerString, outStream);
}
return true;
}
private boolean expandHeaderPathKeyword(RevisionHeader revisionHeader, LogFileHeaderInfo headerInfo,
byte[] inputBuffer, int useIndex, KeywordExpansionContext keywordExpansionContext) throws IOException {
String dateTime;
String headerString;
String workfileName;
String appendedPath = keywordExpansionContext.getAppendedPath();
LogfileInfo logfileInfo = keywordExpansionContext.getLogfileInfo();
OutputStream outStream = keywordExpansionContext.getOutStream();
if (appendedPath.length() > 0) {
workfileName = appendedPath + "/" + logfileInfo.getShortWorkfileName();
} else {
workfileName = logfileInfo.getShortWorkfileName();
}
workfileName = formatFilename(workfileName);
if (keywordExpansionContext.getBinaryFileFlag()) {
headerString = "Bogus Header String";
} else {
dateTime = dateFormat.format(revisionHeader.getCheckInDate()) + " "
+ timeFormat.format(revisionHeader.getCheckInDate());
headerString = workfileName + " " + revisionKeyword + ":" + revisionHeader.getRevisionString() + " " + dateTime + " " + headerInfo.getOwner();
}
if (keywordExpansionContext.getBinaryFileFlag()) {
writeBinaryKeyword(headerPathKeyword, headerString, outStream, inputBuffer, useIndex, keywordExpansionContext);
} else {
writeKeyword(headerPathKeyword, headerString, outStream);
}
return true;
}
}