250N/A/*
250N/A * CDDL HEADER START
250N/A *
250N/A * The contents of this file are subject to the terms of the
250N/A * Common Development and Distribution License (the "License").
250N/A * You may not use this file except in compliance with the License.
250N/A *
250N/A * See LICENSE.txt included in this distribution for the specific
250N/A * language governing permissions and limitations under the License.
250N/A *
250N/A * When distributing Covered Code, include this CDDL HEADER in each
250N/A * file and include the License file at LICENSE.txt.
250N/A * If applicable, add the following below this CDDL HEADER, with the
250N/A * fields enclosed by brackets "[]" replaced with your own identifying
250N/A * information: Portions Copyright [yyyy] [name of copyright owner]
250N/A *
250N/A * CDDL HEADER END
250N/A */
250N/A
250N/A/*
1285N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
250N/A */
250N/Apackage org.opensolaris.opengrok.history;
250N/A
250N/Aimport java.io.BufferedReader;
626N/Aimport java.io.ByteArrayInputStream;
250N/Aimport java.io.File;
250N/Aimport java.io.IOException;
250N/Aimport java.io.InputStream;
250N/Aimport java.io.InputStreamReader;
749N/Aimport java.text.DateFormat;
250N/Aimport java.text.ParseException;
250N/Aimport java.util.ArrayList;
250N/Aimport java.util.Date;
837N/Aimport java.util.List;
427N/Aimport java.util.logging.Level;
1327N/Aimport java.util.logging.Logger;
1327N/A
1470N/Aimport org.opensolaris.opengrok.configuration.Configuration;
250N/Aimport org.opensolaris.opengrok.configuration.RuntimeEnvironment;
626N/Aimport org.opensolaris.opengrok.util.Executor;
250N/A
250N/A/**
250N/A * Parse a stream of Bazaar log comments.
250N/A */
770N/Aclass BazaarHistoryParser implements Executor.StreamHandler {
1470N/A private static final Logger logger =
1470N/A Logger.getLogger(BazaarHistoryParser.class.getName());
626N/A private String myDir;
860N/A private List<HistoryEntry> entries = new ArrayList<HistoryEntry>(); //NOPMD
860N/A private BazaarRepository repository=new BazaarRepository(); //NOPMD
749N/A
837N/A BazaarHistoryParser(BazaarRepository repository) {
837N/A this.repository = repository;
837N/A myDir = repository.getDirectoryName() + File.separator;
837N/A }
250N/A
837N/A History parse(File file, String sinceRevision) throws HistoryException {
1016N/A try {
1016N/A Executor executor = repository.getHistoryLogExecutor(file, sinceRevision);
1016N/A int status = executor.exec(true, this);
347N/A
1016N/A if (status != 0) {
1327N/A throw new HistoryException("Failed to get history for '" +
1327N/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
837N/A // If a changeset to start from is specified, remove that changeset
837N/A // from the list, since only the ones following it should be returned.
837N/A // Also check that the specified changeset was found, otherwise throw
837N/A // an exception.
837N/A if (sinceRevision != null) {
967N/A repository.removeAndVerifyOldestChangeset(entries, sinceRevision);
837N/A }
837N/A
837N/A return new History(entries);
250N/A }
626N/A
1470N/A private static final String LOG_SEPARATOR =
1470N/A "------------------------------------------------------------";
1285N/A /**
626N/A * Process the output from the log command and insert the HistoryEntries
626N/A * into the history field.
626N/A *
626N/A * @param input The output from the process
626N/A * @throws java.io.IOException If an error occurs while reading the stream
626N/A */
815N/A @Override
626N/A public void processStream(InputStream input) throws IOException {
749N/A DateFormat df = repository.getDateFormat();
1470N/A Configuration cfg = RuntimeEnvironment.getConfig();
626N/A
626N/A BufferedReader in = new BufferedReader(new InputStreamReader(input));
626N/A String s;
626N/A
626N/A HistoryEntry entry = null;
626N/A int state = 0;
626N/A while ((s = in.readLine()) != null) {
1470N/A if (LOG_SEPARATOR.equals(s)) {
631N/A if (entry != null && state > 2) {
626N/A entries.add(entry);
626N/A }
626N/A entry = new HistoryEntry();
626N/A entry.setActive(true);
626N/A state = 0;
626N/A continue;
626N/A }
1470N/A if (entry == null) {
1470N/A continue;
1470N/A }
626N/A
626N/A switch (state) {
626N/A case 0:
1285N/A // First, go on until revno is found.
1285N/A if (s.startsWith("revno:")) {
837N/A String rev[] = s.substring("revno:".length()).trim().split(" ");
837N/A entry.setRevision(rev[0]);
626N/A ++state;
626N/A }
626N/A break;
626N/A case 1:
1285N/A // Then, look for committer.
1285N/A if (s.startsWith("committer:")) {
626N/A entry.setAuthor(s.substring("committer:".length()).trim());
626N/A ++state;
626N/A }
626N/A break;
626N/A case 2:
1285N/A // And then, look for timestamp.
1285N/A if (s.startsWith("timestamp:")) {
626N/A try {
1470N/A Date date =
1470N/A df.parse(s.substring("timestamp:".length()).trim());
626N/A entry.setDate(date);
626N/A } catch (ParseException e) {
1327N/A logger.warning("Failed to parse history timestamp "
1327N/A + s + ": " + e.getMessage());
1327N/A logger.log(Level.FINE, "processStream", e);
626N/A }
626N/A ++state;
626N/A }
626N/A break;
626N/A case 3:
1285N/A // Expect the commit message to follow immediately after
1285N/A // the timestamp, and that everything up to the list of
1285N/A // modified, added and removed files is part of the commit
1285N/A // message.
1470N/A if (s.startsWith("modified:") || s.startsWith("added:")
1470N/A || s.startsWith("removed:"))
1470N/A {
1285N/A ++state;
1285N/A } else if (s.startsWith(" ")) {
1285N/A // Commit messages returned by bzr log -v are prefixed
1285N/A // with two blanks.
1285N/A entry.appendMessage(s.substring(2));
626N/A }
626N/A break;
626N/A case 4:
1285N/A // Finally, store the list of modified, added and removed
1285N/A // files. (Except the labels.)
1470N/A if (!(s.startsWith("modified:") || s.startsWith("added:")
1470N/A || s.startsWith("removed:")))
1470N/A {
1285N/A // The list of files is prefixed with blanks.
1285N/A s = s.trim();
1285N/A
626N/A int idx = s.indexOf(" => ");
626N/A if (idx != -1) {
626N/A s = s.substring(idx + 4);
626N/A }
626N/A
626N/A File f = new File(myDir, s);
1470N/A String name = cfg.getPathRelativeToSourceRoot(f, 0);
626N/A entry.addFile(name);
626N/A }
626N/A break;
626N/A default:
1327N/A logger.warning("Unknown parser state " + state);
626N/A break;
626N/A }
626N/A }
626N/A
631N/A if (entry != null && state > 2) {
626N/A entries.add(entry);
626N/A }
626N/A }
1190N/A
626N/A /**
626N/A * Parse the given string.
1190N/A *
626N/A * @param buffer The string to be parsed
626N/A * @return The parsed history
626N/A * @throws IOException if we fail to parse the buffer
626N/A */
770N/A History parse(String buffer) throws IOException {
626N/A myDir = File.separator;
626N/A processStream(new ByteArrayInputStream(buffer.getBytes("UTF-8")));
837N/A return new History(entries);
626N/A }
250N/A}