Skip to content

Commit 0b8c5fb

Browse files
committed
Merge pull request #671 from michailf/master
Support for Surround SCM history
2 parents 80c87d7 + 1a50186 commit 0b8c5fb

File tree

4 files changed

+702
-0
lines changed

4 files changed

+702
-0
lines changed

src/org/opensolaris/opengrok/history/RepositoryFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public final class RepositoryFactory {
5353
new RCSRepository(),
5454
new CVSRepository(),
5555
new RepoRepository(),
56+
new SSCMRepository(),
5657
};
5758

5859
private RepositoryFactory() {
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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+
package org.opensolaris.opengrok.history;
21+
22+
import java.io.BufferedReader;
23+
import java.io.ByteArrayInputStream;
24+
import java.io.File;
25+
import java.io.IOException;
26+
import java.io.InputStream;
27+
import java.io.InputStreamReader;
28+
import java.text.DateFormat;
29+
import java.text.ParseException;
30+
import java.util.ArrayList;
31+
import java.util.logging.Level;
32+
import java.util.regex.Matcher;
33+
import java.util.regex.Pattern;
34+
import org.opensolaris.opengrok.OpenGrokLogger;
35+
import org.opensolaris.opengrok.util.Executor;
36+
37+
/**
38+
*
39+
* @author michailf
40+
*/
41+
public class SSCMHistoryParser implements Executor.StreamHandler {
42+
43+
private final SSCMRepository repository;
44+
45+
SSCMHistoryParser(SSCMRepository repository) {
46+
this.repository = repository;
47+
}
48+
49+
private static final String ACTION_PATTERN = "[a-z][a-z ]+";
50+
private static final String USER_PATTERN = "\\w+";
51+
private static final String VERSION_PATTERN = "\\d+";
52+
private static final String TIME_PATTERN = "\\d{1,2}/\\d{1,2}/\\d{4} \\d{1,2}:\\d{2} [AP]M";
53+
private static final String COMMENT_START_PATTERN = "Comments - ";
54+
// ^([a-z][a-z ]+)(?:\[(.*?)\])?\s+(\w+)\s+(\d+)\s+(\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{2} [AP]M)$\s*(?:Comments - )?
55+
private static final Pattern HISTORY_PATTERN = Pattern.compile("^(" + ACTION_PATTERN + ")(?:\\[(.*?)\\])?\\s+(" + USER_PATTERN + ")\\s+(" + VERSION_PATTERN + ")\\s+(" + TIME_PATTERN + ")$\\s*(?:" + COMMENT_START_PATTERN + ")?",
56+
Pattern.MULTILINE);
57+
58+
private static final String NEWLINE = System.getProperty("line.separator");
59+
60+
private History history;
61+
62+
/**
63+
* Process the output from the history command and insert the HistoryEntries
64+
* into the history field.
65+
*
66+
* @param input The output from the process
67+
* @throws java.io.IOException If an error occurs while reading the stream
68+
*/
69+
@Override
70+
public void processStream(InputStream input) throws IOException {
71+
DateFormat df = repository.getDateFormat();
72+
history = new History();
73+
74+
BufferedReader in = new BufferedReader(new InputStreamReader(input));
75+
StringBuilder total = new StringBuilder(input.available());
76+
String line;
77+
while ((line = in.readLine()) != null) {
78+
total.append(line).append(NEWLINE);
79+
}
80+
81+
ArrayList<HistoryEntry> entries = new ArrayList<>();
82+
HistoryEntry entry = null;
83+
int prevEntryEnd = 0;
84+
85+
long revisionCounter = 0;
86+
Matcher matcher = HISTORY_PATTERN.matcher(total);
87+
while (matcher.find()) {
88+
if (entry != null) {
89+
if (matcher.start() != prevEntryEnd) {
90+
// Get the comment and reduce all double new lines to single
91+
// add a space as well for better formatting in RSS feeds.
92+
entry.appendMessage(total.substring(prevEntryEnd, matcher.start()).replaceAll("(\\r?\\n){2}", " $1").trim());
93+
}
94+
entries.add(0, entry);
95+
entry = null;
96+
}
97+
String revision = matcher.group(4);
98+
String author = matcher.group(3);
99+
String context = matcher.group(2);
100+
String date = matcher.group(5);
101+
102+
long currentRevision = 0;
103+
try {
104+
currentRevision = Long.parseLong(revision);
105+
} catch (NumberFormatException ex) {
106+
OpenGrokLogger.getLogger().log(Level.WARNING, "Failed to parse revision: '" + revision + "'", ex);
107+
}
108+
// We're only interested in history entries that change file content
109+
if (revisionCounter < currentRevision) {
110+
revisionCounter = currentRevision;
111+
112+
entry = new HistoryEntry();
113+
// Add context of action to message. Helps when branch name is used
114+
// as indicator of why promote was made.
115+
if (context != null)
116+
entry.appendMessage("[" + context + "] ");
117+
entry.setAuthor(author);
118+
entry.setRevision(revision);
119+
try {
120+
entry.setDate(df.parse(date));
121+
} catch (ParseException ex) {
122+
OpenGrokLogger.getLogger().log(Level.WARNING, "Failed to parse date: '" + date + "'", ex);
123+
}
124+
entry.setActive(true);
125+
}
126+
prevEntryEnd = matcher.end();
127+
}
128+
129+
if (entry != null) {
130+
if (total.length() != prevEntryEnd) {
131+
// Get the comment and reduce all double new lines to single
132+
// add a space as well for better formatting in RSS feeds.
133+
entry.appendMessage(total.substring(prevEntryEnd).replaceAll("(\\r?\\n){2}", " $1").trim());
134+
}
135+
entries.add(0, entry);
136+
}
137+
history.setHistoryEntries(entries);
138+
}
139+
140+
History parse(File file, String sinceRevision) throws HistoryException {
141+
try {
142+
Executor executor = repository.getHistoryLogExecutor(file, sinceRevision);
143+
int status = executor.exec(true, this);
144+
145+
if (status != 0) {
146+
throw new HistoryException("Failed to get history for: \"" +
147+
file.getAbsolutePath() + "\" Exit code: " + status);
148+
}
149+
} catch (IOException e) {
150+
throw new HistoryException("Failed to get history for: \"" +
151+
file.getAbsolutePath() + "\"", e);
152+
}
153+
154+
return history;
155+
}
156+
157+
/**
158+
* Parse the given string.
159+
*
160+
* @param buffer The string to be parsed
161+
* @return The parsed history
162+
* @throws IOException if we fail to parse the buffer
163+
*/
164+
History parse(String buffer) throws IOException {
165+
processStream(new ByteArrayInputStream(buffer.getBytes("UTF-8")));
166+
return history;
167+
}
168+
}

0 commit comments

Comments
 (0)