xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperHistoryParser.java (revision 0dd20075c64240d25935b16ff042a8a9af403e3a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2017, James Service <jas2701@googlemail.com>.
22  * Portions Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
23  * Portions Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
24  */
25 package org.opengrok.indexer.history;
26 
27 import java.io.BufferedReader;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.TreeSet;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38 
39 import org.opengrok.indexer.logger.LoggerFactory;
40 import org.opengrok.indexer.util.Executor;
41 
42 /**
43  * BitKeeperHistoryParser handles parsing the output of `bk log` into a history object.
44  *
45  * @author James Service  {@literal <jas2701@googlemail.com>}
46  */
47 class BitKeeperHistoryParser implements Executor.StreamHandler {
48 
49     private static final Logger LOGGER = LoggerFactory.getLogger(BitKeeperHistoryParser.class);
50 
51     /**
52      * Parses dates.
53      */
54     private final SimpleDateFormat dateFormat;
55     /**
56      * Store entries processed from executor output.
57      */
58     private final List<HistoryEntry> entries = new ArrayList<>();
59     /**
60      * Store renamed files processed from executor output.
61      */
62     private final Set<String> renamedFiles = new TreeSet<>();
63 
64     /**
65      * Constructor to construct the thing to be constructed.
66      *
67      * @param datePattern a simple date format string
68      */
BitKeeperHistoryParser(String datePattern)69     BitKeeperHistoryParser(String datePattern) {
70         dateFormat = new SimpleDateFormat(datePattern);
71     }
72 
73     /**
74      * Returns the history that has been created.
75      *
76      * @return history a history object
77      */
getHistory()78     History getHistory() {
79         return new History(entries, new ArrayList<>(renamedFiles));
80     }
81 
82     /**
83      * Process the output of a `bk log` command.
84      *
85      * Each input line should be in the following format:
86      * either
87      *   D FILE\tREVISION\tDATE\tUSER(\tRENAMED_FROM)?
88      * or
89      *   C COMMIT_MESSAGE_LINE
90      *
91      * BitKeeper always outputs the file name relative to its root directory, which is nice for us since that's what
92      * History expects.
93      *
94      * @param input the executor input stream
95      * @throws IOException if the stream reader throws an IOException
96      */
97     @Override
processStream(InputStream input)98     public void processStream(InputStream input) throws IOException {
99         HistoryEntry entry = null;
100 
101         final BufferedReader in = new BufferedReader(new InputStreamReader(input));
102         for (String line = in.readLine(); line != null; line = in.readLine()) {
103             if (line.startsWith("D ")) {
104                 if (entry != null) {
105                     entries.add(entry);
106                     entry = null;
107                 }
108 
109                 final String[] fields = line.substring(2).split("\t");
110                 final HistoryEntry newEntry = new HistoryEntry();
111                 try {
112                     if (fields[0].equals("ChangeSet")) {
113                         continue;
114                     }
115                     newEntry.addFile(fields[0].intern());
116                     newEntry.setRevision(fields[1]);
117                     newEntry.setDate(dateFormat.parse(fields[2]));
118                     newEntry.setAuthor(fields[3]);
119                     newEntry.setActive(true);
120                 } catch (final Exception e) {
121                     LOGGER.log(Level.SEVERE, "Error: malformed BitKeeper log output {0}", line);
122                     continue;
123                 }
124 
125                 entry = newEntry;
126                 if (fields.length == 5) {
127                     renamedFiles.add(fields[4]);
128                 }
129             } else if (line.startsWith("C ") && (entry != null)) {
130                 final String messageLine = line.substring(2);
131                 entry.appendMessage(messageLine);
132             }
133         }
134 
135         if (entry != null) {
136             entries.add(entry);
137         }
138     }
139 }
140