// 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.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
//import java.util.stream.Stream;
/**
* Implement the QVCS keyword manager using Java 8 lambda expressions, and java.util.stream() classes. Note that this implementation is meant to be used only for text files. It
* will not work for binary files. For now , we also do not support the Log or LogX keywords. The keyword delimiter character is hard-coded to be '$', and all the other keyword
* strings are hard-coded as well. In other words, this is really just a proof-of-concept implementation.
*
*/
public class UsingLambdaKeywordManager implements KeywordManagerInterface {
// Create our logger object
private static final Logger LOGGER = Logger.getLogger("com.qumasoft.qvcslib");
private static final String AUTHOR_KEYWORD = "Author";
private static final String DATE_KEYWORD = "Date";
private static final String FILENAME_KEYWORD = "Filename";
private static final String FILEPATH_KEYWORD = "FilePath";
private static final String HEADER_KEYWORD = "Header";
private static final String HEADERPATH_KEYWORD = "HeaderPath";
private static final String LABEL_KEYWORD = "Label";
private static final String LOGFILE_KEYWORD = "Logfile";
private static final String OWNER_KEYWORD = "Owner";
private static final String PROJECT_KEYWORD = "Project";
private static final String REVISION_KEYWORD = "Revision";
private static final String VER_KEYWORD = "VER";
private static final String VERSION_KEYWORD = "Version";
private static final String NO_LABEL_LABEL = "NONE";
private static final String MULTIPLE_LABELS = "MULTIPLE LABELS";
@Override
public void expandKeywords(FileInputStream inStream, KeywordExpansionContext keywordExpansionContext) throws IOException, QVCSException {
expandKeywords(inStream, keywordExpansionContext);
}
@Override
public void expandKeywords(byte[] inBuffer, KeywordExpansionContext keywordExpansionContext) throws IOException, QVCSException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inBuffer);
expandKeywords(byteArrayInputStream, keywordExpansionContext);
}
private void expandKeywords(InputStream inputStream, KeywordExpansionContext keywordExpansionContext) throws IOException, QVCSException {
OutputStream outputStream = keywordExpansionContext.getOutStream();
LogfileInfo logfileInfo = keywordExpansionContext.getLogfileInfo();
File outputFile = keywordExpansionContext.getOutputFile();
int revisionIndex = keywordExpansionContext.getRevisionIndex();
String labelString = keywordExpansionContext.getLabelString();
String appendedPath = keywordExpansionContext.getAppendedPath();
AbstractProjectProperties projectProperties = keywordExpansionContext.getProjectProperties();
if (logfileInfo.getLogFileHeaderInfo().getLogFileHeader().attributes().getIsBinaryfile()) {
// We do not support binary files.
// TODO -- just copy the input stream to the output stream.
LOGGER.log(Level.INFO, "Binary files not supported.");
throw new QVCSRuntimeException("Binary files not supported.");
} else {
ExpansionContext expansionContext = new ExpansionContext(logfileInfo, outputFile, revisionIndex, labelString, appendedPath, projectProperties);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
// Stream<String> lines = bufferedReader.lines();
// The expanded output will go in the expandedLines ArrayList.
List<String> expandedLines = new ArrayList<>();
// TODO -- uncomment when checkstyle is JDK8 enabled.
// // Expand the lines.
// lines.forEachOrdered(contractedLine -> expandLine(contractedLine, expandedLines, expansionContext));
//
// // Write the expanded lines to the output file...
// PrintWriter printWriter = new PrintWriter(outputStream);
// expandedLines.stream().forEachOrdered(expandedLine -> printWriter.println(expandedLine));
// printWriter.flush();
}
}
@Override
public void contractKeywords(FileInputStream inStream, OutputStream outStream, AtomicReference<String> checkInComment, AbstractProjectProperties projectProperties,
boolean binaryFileFlag) throws IOException, QVCSException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
private void expandLine(String contractedLine, List<String> expandedLines, ExpansionContext expansionContext) {
StringBuilder expandedLine = new StringBuilder();
String[] keywordCandidates = contractedLine.split("\\$");
boolean lineEndsWithDollar = contractedLine.endsWith("$");
if (keywordCandidates.length > 1) {
int segmentIndex = 0;
int maxSegmentIndex = keywordCandidates.length - 1;
for (String segment : keywordCandidates) {
expandKeywordCandidate(segment, expandedLine, expansionContext, segmentIndex == maxSegmentIndex, lineEndsWithDollar);
segmentIndex++;
}
expandedLines.add(expandedLine.toString());
} else {
expandedLines.add(contractedLine);
}
}
/**
* Expand a candidate keyword. If the input segment has no keywords, then the expanded string will be the same as the input string.
*
* @param segment the string that is a keyword candidate. It <i>might</i> be a keyword.
* @param expandedLine the expansion of the input keyword candidate. This will be the expanded keyword, or it will be same as the input segment (if the input segment is not a
* keyword).
* @param expansionContext a collection of information useful for expanding keywords.
* @param isLastSegment flag that is true if the input segment is the final segment of the split input line.
* @param lineEndsWithDollar flag that is true if the line from which the segment has been extracted ends with the '$' character.
*/
private void expandKeywordCandidate(String segment, StringBuilder expandedLine, ExpansionContext expansionContext, boolean isLastSegment, boolean lineEndsWithDollar) {
RevisionHeader revisionHeader;
DateFormat dateFormat;
DateFormat timeFormat;
String dateTime;
StringBuilder workfileName;
LogFileHeaderInfo headerInfo;
switch (segment) {
case AUTHOR_KEYWORD:
AccessList modifierList;
modifierList = new AccessList(expansionContext.getLogfileInfo().getLogFileHeaderInfo().getModifierList());
revisionHeader = expansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(expansionContext.getRevisionIndex());
String author;
author = modifierList.indexToUser(revisionHeader.getCreatorIndex());
expandedLine.append(AUTHOR_KEYWORD).append(": ").append(author).append(" $");
break;
case DATE_KEYWORD:
dateFormat = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy");
timeFormat = new SimpleDateFormat("h:mm:ss aaa");
revisionHeader = expansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(expansionContext.getRevisionIndex());
dateTime = dateFormat.format(revisionHeader.getCheckInDate()) + " "
+ timeFormat.format(revisionHeader.getCheckInDate());
expandedLine.append(DATE_KEYWORD).append(": ").append(dateTime).append(" $");
break;
case FILENAME_KEYWORD:
String canonicalFilename = null;
try {
canonicalFilename = expansionContext.getOutputFile().getCanonicalPath();
} catch (IOException ex) {
Logger.getLogger(UsingLambdaKeywordManager.class.getName()).log(Level.SEVERE, null, ex);
}
expandedLine.append(FILENAME_KEYWORD).append(": ").append(canonicalFilename).append(" $");
break;
case FILEPATH_KEYWORD:
String fileName;
if (expansionContext.getAppendedPath().length() > 0) {
fileName = expansionContext.getAppendedPath() + "/" + expansionContext.getLogfileInfo().getShortWorkfileName();
} else {
fileName = expansionContext.getLogfileInfo().getShortWorkfileName();
}
expandedLine.append(FILEPATH_KEYWORD).append(": ").append(fileName).append(" $");
break;
case HEADER_KEYWORD:
revisionHeader = expansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(expansionContext.getRevisionIndex());
dateFormat = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy");
timeFormat = new SimpleDateFormat("h:mm:ss aaa");
dateTime = dateFormat.format(revisionHeader.getCheckInDate()) + " "
+ timeFormat.format(revisionHeader.getCheckInDate());
headerInfo = expansionContext.getLogfileInfo().getLogFileHeaderInfo();
StringBuilder headerStringBuilder = new StringBuilder(expansionContext.getOutputFile().getName());
headerStringBuilder.append(" ")
.append(REVISION_KEYWORD)
.append(": ")
.append(revisionHeader.getRevisionString())
.append(" ")
.append(dateTime)
.append(" ")
.append(headerInfo.getOwner());
expandedLine.append(HEADER_KEYWORD).append(": ").append(headerStringBuilder).append(" $");
break;
case HEADERPATH_KEYWORD:
StringBuilder headerString = new StringBuilder();
workfileName = new StringBuilder();
revisionHeader = expansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(expansionContext.getRevisionIndex());
headerInfo = expansionContext.getLogfileInfo().getLogFileHeaderInfo();
dateFormat = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy");
timeFormat = new SimpleDateFormat("h:mm:ss aaa");
dateTime = dateFormat.format(revisionHeader.getCheckInDate()) + " "
+ timeFormat.format(revisionHeader.getCheckInDate());
if (expansionContext.getAppendedPath().length() > 0) {
workfileName.append(expansionContext.getAppendedPath()).append("/").append(expansionContext.getLogfileInfo().getShortWorkfileName());
} else {
workfileName.append(expansionContext.getLogfileInfo().getShortWorkfileName());
}
headerString.append(workfileName.toString())
.append(" ")
.append(REVISION_KEYWORD)
.append(": ")
.append(revisionHeader.getRevisionString())
.append(" ")
.append(dateTime)
.append(" ")
.append(headerInfo.getOwner());
expandedLine.append(HEADERPATH_KEYWORD).append(": ").append(headerString).append(" $");
break;
case LABEL_KEYWORD:
LabelInfo[] labelInfo = expansionContext.getLogfileInfo().getLogFileHeaderInfo().getLabelInfo();
revisionHeader = expansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(expansionContext.getRevisionIndex());
String useLabelString = deduceLabelString(labelInfo, revisionHeader, expansionContext.getLabelString());
expandedLine.append(LABEL_KEYWORD).append(": ").append(useLabelString).append(" $");
break;
case LOGFILE_KEYWORD:
break;
case OWNER_KEYWORD:
break;
case PROJECT_KEYWORD:
break;
case REVISION_KEYWORD:
revisionHeader = expansionContext.getLogfileInfo().getRevisionInformation().getRevisionHeader(expansionContext.getRevisionIndex());
String revisionString = revisionHeader.getRevisionString();
expandedLine.append(REVISION_KEYWORD).append(": ").append(revisionString).append(" $");
break;
case VER_KEYWORD:
break;
case VERSION_KEYWORD:
break;
case "":
expandedLine.append("$");
break;
default:
if (isLastSegment) {
if (lineEndsWithDollar) {
expandedLine.append(segment).append("$");
} else {
expandedLine.append(segment);
}
} else {
expandedLine.append(segment).append("$");
}
break;
}
}
private String deduceLabelString(LabelInfo[] labelInfos, RevisionHeader revisionHeader, String inputLabelString) {
String labelString = NO_LABEL_LABEL;
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 = MULTIPLE_LABELS;
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;
}
/**
* An immutable class for aggregating information that we need in order to expand QVCS keywords.
*/
private static class ExpansionContext {
private final LogfileInfo logfileInfo;
private final File outputFile;
private final int revisionIndex;
private final String labelString;
private final String appendedPath;
private final AbstractProjectProperties projectProperties;
public ExpansionContext(LogfileInfo inputLogfileInfo, File inputOutputFile, int inputRevisionIndex, String inputLabelString, String inputAppendedPath,
AbstractProjectProperties inputProjectProperties) {
this.logfileInfo = inputLogfileInfo;
this.outputFile = inputOutputFile;
this.revisionIndex = inputRevisionIndex;
this.labelString = inputLabelString;
this.appendedPath = inputAppendedPath;
this.projectProperties = inputProjectProperties;
}
/**
* @return the logfileInfo
*/
public LogfileInfo getLogfileInfo() {
return logfileInfo;
}
/**
* @return the revisionIndex
*/
public int getRevisionIndex() {
return revisionIndex;
}
/**
* @return the labelString
*/
public String getLabelString() {
return labelString;
}
/**
* @return the appendedPath
*/
public String getAppendedPath() {
return appendedPath;
}
/**
* @return the projectProperties
*/
public AbstractProjectProperties getProjectProperties() {
return projectProperties;
}
/**
* @return the outputFile
*/
public File getOutputFile() {
return outputFile;
}
}
}