289N/A/*
289N/A * CDDL HEADER START
289N/A *
289N/A * The contents of this file are subject to the terms of the
289N/A * Common Development and Distribution License (the "License").
289N/A * You may not use this file except in compliance with the License.
289N/A *
289N/A * See LICENSE.txt included in this distribution for the specific
289N/A * language governing permissions and limitations under the License.
289N/A *
289N/A * When distributing Covered Code, include this CDDL HEADER in each
289N/A * file and include the License file at LICENSE.txt.
289N/A * If applicable, add the following below this CDDL HEADER, with the
289N/A * fields enclosed by brackets "[]" replaced with your own identifying
289N/A * information: Portions Copyright [yyyy] [name of copyright owner]
289N/A *
289N/A * CDDL HEADER END
289N/A */
289N/A
289N/A/*
1072N/A * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
289N/A */
289N/Apackage org.opensolaris.opengrok.history;
289N/A
289N/Aimport java.io.BufferedReader;
625N/Aimport java.io.ByteArrayInputStream;
289N/Aimport java.io.File;
1016N/Aimport java.io.FileNotFoundException;
289N/Aimport java.io.IOException;
289N/Aimport java.io.InputStream;
749N/Aimport java.text.DateFormat;
289N/Aimport java.text.ParseException;
289N/Aimport java.util.ArrayList;
1482N/Aimport java.util.Map;
459N/Aimport java.util.logging.Level;
1327N/Aimport java.util.logging.Logger;
1327N/A
1470N/Aimport org.opensolaris.opengrok.configuration.Configuration;
289N/Aimport org.opensolaris.opengrok.configuration.RuntimeEnvironment;
625N/Aimport org.opensolaris.opengrok.util.Executor;
1207N/Aimport org.opensolaris.opengrok.util.IOUtils;
614N/Aimport org.opensolaris.opengrok.util.StringUtils;
289N/A
289N/A/**
289N/A * Parse a stream of Git log comments.
289N/A */
770N/Aclass GitHistoryParser implements Executor.StreamHandler {
1470N/A private static final Logger logger =
1470N/A Logger.getLogger(GitHistoryParser.class.getName());
289N/A private enum ParseState {
1134N/A
289N/A HEADER, MESSAGE, FILES
1181N/A }
625N/A private String myDir;
625N/A private History history;
1134N/A private GitRepository repository = new GitRepository();
1482N/A private static final String SVN_ID_PREFIX = " git-svn-id: ";
1133N/A
1134N/A /**
625N/A * Process the output from the log command and insert the HistoryEntries
625N/A * into the history field.
625N/A *
625N/A * @param input The output from the process
625N/A * @throws java.io.IOException If an error occurs while reading the stream
612N/A */
815N/A @Override
625N/A public void processStream(InputStream input) throws IOException {
1207N/A BufferedReader in = null;
1207N/A try {
1207N/A in = new BufferedReader(repository.newLogReader(input));
1207N/A process(in);
1207N/A } finally {
1207N/A IOUtils.close(in);
1207N/A }
1207N/A }
1207N/A
1207N/A private void process(BufferedReader in) throws IOException {
1470N/A Configuration cfg = RuntimeEnvironment.getConfig();
749N/A DateFormat df = repository.getDateFormat();
612N/A ArrayList<HistoryEntry> entries = new ArrayList<HistoryEntry>();
1483N/A String aDate = null;
1066N/A
625N/A history = new History();
612N/A HistoryEntry entry = null;
1482N/A String uuid = repository.getSvnUUID();
1482N/A if (uuid != null) {
1482N/A uuid = "@" + uuid;
1482N/A }
612N/A ParseState state = ParseState.HEADER;
612N/A String s = in.readLine();
612N/A while (s != null) {
612N/A if (state == ParseState.HEADER) {
612N/A
612N/A if (s.startsWith("commit")) {
612N/A if (entry != null) {
612N/A entries.add(entry);
612N/A }
612N/A entry = new HistoryEntry();
612N/A entry.setActive(true);
612N/A String commit = s.substring("commit".length()).trim();
612N/A entry.setRevision(commit);
612N/A } else if (s.startsWith("Author:") && entry != null) {
1482N/A s = s.substring("Author:".length()).trim();
1482N/A // strip off the uuid if this is a git-svn repo. Per default
1482N/A // git generates for unknown authors:
1482N/A // ^Author: ${user} <${user}@${uuid}>
1482N/A if (uuid != null
1482N/A && s.startsWith(uuid, s.length() - uuid.length() - 1))
1482N/A {
1482N/A s = s.substring(0,
1482N/A s.lastIndexOf('<', s.length() - uuid.length() -1) -1);
1482N/A }
1482N/A entry.setAuthor(s);
612N/A } else if (s.startsWith("AuthorDate:") && entry != null) {
1483N/A // TBD: Perhaps we should add this to the history entry as well
1483N/A aDate = s.substring("AuthorDate:".length()).trim();
1483N/A } else if (s.startsWith("CommitDate:") && entry != null) {
612N/A String dateString =
1483N/A s.substring("CommitDate:".length()).trim();
612N/A try {
612N/A entry.setDate(df.parse(dateString));
1483N/A aDate = null;
612N/A } catch (ParseException pe) {
1483N/A logger.warning("Failed to parse commit date " + s
1327N/A + ": " + pe.getMessage());
1327N/A logger.log(Level.FINE, "process", pe);
612N/A }
622N/A } else if (StringUtils.isOnlyWhitespace(s)) {
612N/A // We are done reading the heading, start to read the message
612N/A state = ParseState.MESSAGE;
1133N/A
1470N/A // The current line is empty - the message starts on the
1470N/A // next line (to be parsed below).
612N/A s = in.readLine();
612N/A }
612N/A
612N/A }
612N/A if (state == ParseState.MESSAGE) {
614N/A if ((s.length() == 0) || Character.isWhitespace(s.charAt(0))) {
612N/A if (entry != null) {
1482N/A int i = 0, k;
1482N/A if (uuid != null && s.startsWith(SVN_ID_PREFIX)) {
1482N/A i = s.indexOf('@', SVN_ID_PREFIX.length()+4) + 1;
1482N/A k = s.indexOf(' ', i);
1482N/A if (i > 0 && k > 0) {
1482N/A s = s.substring(i, k);
1482N/A entry.setOldRevision(s);
1482N/A } else {
1482N/A i = 0;
1482N/A }
1482N/A }
1482N/A if (i == 0) {
1482N/A entry.appendMessage(s);
1482N/A }
612N/A }
612N/A } else {
612N/A // This is the list of files after the message - add them
612N/A state = ParseState.FILES;
612N/A }
612N/A }
612N/A if (state == ParseState.FILES) {
614N/A if (StringUtils.isOnlyWhitespace(s) || s.startsWith("commit")) {
612N/A state = ParseState.HEADER;
612N/A continue; // Parse this line again - do not read a new line
1183N/A }
1183N/A if (entry != null) {
1183N/A try {
1183N/A File f = new File(myDir, s);
1470N/A entry.addFile(cfg.getPathRelativeToSourceRoot(f, 0));
1183N/A } catch (FileNotFoundException e) { //NOPMD
1183N/A // If the file is not located under the source root,
1183N/A // ignore it (bug #11664).
612N/A }
612N/A }
612N/A }
612N/A s = in.readLine();
612N/A }
612N/A
612N/A if (entry != null) {
1483N/A if (aDate != null) {
1483N/A // no commitDate found? Strange! Use authorDate than.
1483N/A try {
1483N/A entry.setDate(df.parse(aDate));
1483N/A } catch (ParseException pe) {
1483N/A logger.warning("Failed to parse author date " + s
1483N/A + ": " + pe.getMessage());
1483N/A logger.log(Level.FINE, "process", pe);
1483N/A }
1483N/A }
612N/A entries.add(entry);
612N/A }
612N/A
612N/A history.setHistoryEntries(entries);
615N/A }
615N/A
625N/A /**
625N/A * Parse the history for the specified file.
625N/A *
625N/A * @param file the file to parse history for
625N/A * @param repos Pointer to the SubversionReporitory
625N/A * @return object representing the file's history
625N/A */
1470N/A History parse(File file, Repository repos, String sinceRevision)
1470N/A throws HistoryException
1470N/A {
1134N/A myDir = repos.getDirectoryName() + File.separator;
749N/A repository = (GitRepository) repos;
1016N/A try {
1470N/A Executor executor =
1470N/A repository.getHistoryLogExecutor(file, sinceRevision);
1016N/A int status = executor.exec(true, this);
1133N/A
1016N/A if (status != 0) {
1470N/A throw new HistoryException("Failed to get history for '"
1470N/A + file.getAbsolutePath() + "' - Exit code " + status);
1016N/A }
1016N/A } catch (IOException e) {
1327N/A throw new HistoryException("Failed to get history for '" +
1327N/A file.getAbsolutePath() + "'", e);
347N/A }
347N/A
289N/A return history;
289N/A }
1133N/A
625N/A /**
625N/A * Parse the given string.
1133N/A *
625N/A * @param buffer The string to be parsed
625N/A * @return The parsed history
625N/A * @throws IOException if we fail to parse the buffer
625N/A */
770N/A History parse(String buffer) throws IOException {
1470N/A myDir = RuntimeEnvironment.getConfig().getSourceRoot();
625N/A processStream(new ByteArrayInputStream(buffer.getBytes("UTF-8")));
625N/A return history;
625N/A }
289N/A}