/* * 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 */ 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 entries = new ArrayList(); 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(); } } }