/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* See LICENSE.txt included in this distribution for the specific
* language governing permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
*/
/* Portions Copyright 2008 Peter Bray */
package org.opensolaris.opengrok.history;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.opensolaris.opengrok.util.IOUtils;
import org.opensolaris.opengrok.util.StringUtils;
/**
* A History Parser for Razor
*
* @author Peter Bray <Peter.Darren.Bray@gmail.com>
*/
class RazorHistoryParser {
private static final Logger logger =
Logger.getLogger(RazorHistoryParser.class.getName());
private RazorRepository repository = new RazorRepository();
private static final Pattern ACTION_TYPE_PATTERN =
Pattern.compile("^(INTRODUCE|CHECK-OUT|CHECK-IN|UN-CHECK-OUT|RENAME"
+ "|EDIT_PROPS|ALTERED|CHECK-POINT|REVERT|INTRODUCE_AND_EDIT|BRANCH"
+ "|BUMP|MERGE-CHECK-IN|PROMOTE)"
+ "\\s+(\\S*)\\s+([\\.0-9]+)?\\s+(\\S*)\\s+(\\S*)\\s*$");
private static final Pattern ADDITIONAL_INFO_PATTERN =
Pattern.compile("^##(TITLE|NOTES|AUDIT|ISSUE):\\s+(.*)\\s*$");
private static final boolean DUMP_HISTORY_ENTRY_ADDITIONS = false;
History parse(File file, Repository repos) throws HistoryException {
try {
return parseFile(file, repos);
} catch (IOException ioe) {
throw new HistoryException(ioe);
}
}
private History parseFile(File file, Repository repos)
throws IOException {
repository = (RazorRepository) repos;
File mappedFile = repository.getRazorHistoryFileFor(file);
logger.log(Level.FINE, "Mapping ''{0}'' to ''{1}''",
new String[] { file.getPath(), mappedFile.getPath() });
if (!mappedFile.exists()) {
logger.warning("History File Mapping is NON-EXISTENT ("
+ mappedFile.getAbsolutePath() + ")");
return null;
}
if (mappedFile.isDirectory()) {
logger.warning("History File Mapping is a DIRECTORY ("
+ mappedFile.getAbsolutePath() + ")");
return null;
}
FileReader contents = new FileReader(mappedFile.getAbsoluteFile());
try {
return parseContents(new BufferedReader(contents));
} finally {
IOUtils.close(contents);
}
}
protected History parseContents(BufferedReader contents) throws IOException {
DateFormat df = repository.getDateFormat();
String line;
ArrayList<HistoryEntry> entries = new ArrayList<HistoryEntry>();
HistoryEntry entry = null;
boolean lastWasTitle = true;
Matcher actionMatcher = ACTION_TYPE_PATTERN.matcher("");
Matcher infoMatcher = ADDITIONAL_INFO_PATTERN.matcher("");
while ((line = contents.readLine()) != null) {
logger.log(Level.FINE, "Processing [{0}]", line);
if (StringUtils.isOnlyWhitespace(line)) {
if (entry != null && entry.getDate() != null) {
entries.add(entry);
dumpEntry(entry);
}
entry = null;
continue;
} else if (entry != null) {
infoMatcher.reset(line);
if (infoMatcher.find()) {
String infoType = infoMatcher.group(1);
String details = infoMatcher.group(2);
if ("TITLE".equals(infoType)) {
logger.log(Level.FINE, "Setting Message ''{0}''", details);
entry.setMessage(details);
lastWasTitle = true;
} else if ("ISSUE".equals(infoType)) {
logger.log(Level.FINE, "Adding CR ''{0}''", details);
entry.addChangeRequest(details);
} else {
logger.log(Level.FINE, "Ignoring Info Type Line [{0}]", line);
}
} else if (!line.startsWith("##") && line.charAt(0) == '#') {
logger.log(Level.FINE, "Seen Comment [{0}]", line);
if (lastWasTitle) {
entry.appendMessage("");
lastWasTitle = false;
}
entry.appendMessage(line.substring(1));
} else {
logger.warning("Expecting addlInfo and got [" + line + "]");
}
} else {
actionMatcher.reset(line);
if (actionMatcher.find()) {
entry = new HistoryEntry();
String actionType = actionMatcher.group(1);
String userName = actionMatcher.group(2);
String revision = actionMatcher.group(3);
String state = actionMatcher.group(4);
String dateTime = actionMatcher.group(5);
logger.log(Level.FINE, "New History Event Seen: actionType "
+ "= {0}, userName = {1}, revision = {2}, state = {3}, "
+ "dateTime = {4}", new String[] { actionType, userName,
revision, state, dateTime });
if (actionType.startsWith("INTRODUCE")
|| actionType.contains("CHECK-IN")
|| "CHECK-POINT".equals(actionType)
|| "REVERT".equals(actionType))
{
entry.setAuthor(userName);
entry.setRevision(revision);
entry.setActive("Active".equals(state));
Date date = null;
try {
date = df.parse(dateTime);
} catch (ParseException pe) {
logger.warning("Could not parse date " + dateTime
+ ": " + pe.getMessage());
logger.log(Level.FINE, "parseContents", pe);
}
entry.setDate(date);
}
} else {
logger.warning("Expecting actionType and got [" + line + "]");
}
}
}
if (entry != null && entry.getDate() != null) {
entries.add(entry);
dumpEntry(entry);
}
History history = new History();
history.setHistoryEntries(entries);
return history;
}
private static void dumpEntry(HistoryEntry entry) {
if (DUMP_HISTORY_ENTRY_ADDITIONS) {
entry.dump();
}
}
}