blob: 017fbb9943d1337f28c5e6e90292bb1431d7ff7f [file] [log] [blame]
package com.googlesource.gerrit.plugins.hooks.util;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.hooks.its.ItsName;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class IssueExtractor {
private static final Logger log = LoggerFactory.getLogger(
IssueExtractor.class);
private final Config gerritConfig;
private final String itsName;
private final CommitMessageFetcher commitMessageFetcher;
@Inject
IssueExtractor(@GerritServerConfig Config gerritConfig,
@ItsName String itsName, CommitMessageFetcher commitMessageFetcher) {
this.gerritConfig = gerritConfig;
this.itsName = itsName;
this.commitMessageFetcher = commitMessageFetcher;
}
/**
* Gets issue ids from a string.
*
* @param haystack String to extract issue ids from
* @return array of {@link Strings}. Each String being a found issue id.
*/
public String[] getIssueIds(String haystack) {
Pattern pattern = getPattern();
if (pattern == null) return new String[] {};
log.debug("Matching '" + haystack + "' against " + pattern.pattern());
Set<String> issues = Sets.newHashSet();
Matcher matcher = pattern.matcher(haystack);
while (matcher.find()) {
int groupIdx = Math.min(matcher.groupCount(), 1);
issues.add(matcher.group(groupIdx));
}
return issues.toArray(new String[issues.size()]);
}
/**
* Gets the regular expression used to identify issue ids.
* @return the regular expression, or {@code null}, if there is no pattern
* to match issue ids.
*/
public Pattern getPattern() {
Pattern ret = null;
String match = gerritConfig.getString("commentLink", itsName, "match");
if (match != null) {
ret = Pattern.compile(match);
}
return ret;
}
/**
* Helper funcion for {@link #getIssueIds(String, String)}.
* <p>
* Adds a text's issues for a given occurrence to the map returned by
* {@link #getIssueIds(String, String)}.
*
* @param text The text to extract issues from.
* @param occurrence The occurrence the issues get added at in {@code map}.
* @param map The map that the issues should get added to.
*/
private void addIssuesOccurrence(String text, String occurrence, Map<String,Set<String>> map) {
for (String issue : getIssueIds(text)) {
Set<String> occurrences = map.get(issue);
if (occurrences == null) {
occurrences = Sets.newLinkedHashSet();
map.put(issue, occurrences);
}
occurrences.add(occurrence);
}
}
/**
* Gets issues for a commit.
*
* @param projectName The project to fetch {@code commitId} from.
* @param commitId The commit id to fetch issues for.
* @return A mapping, whose keys are issue ids and whose values is a set of
* places where the issue occurs. Each issue occurs at least in
* "somewhere". Issues from the first line get tagged with an occurrence
* "subject". Issues in the last block get tagged with "footer". Issues
* occurring between "subject" and "footer" get tagged with "body".
*/
public Map<String,Set<String>> getIssueIds(String projectName,
String commitId) {
Map<String,Set<String>> ret = Maps.newHashMap();
String commitMessage = commitMessageFetcher.fetchGuarded(projectName,
commitId);
addIssuesOccurrence(commitMessage, "somewhere", ret);
String[] lines = commitMessage.split("\n");
if (lines.length > 0) {
// Parsing for "subject"
addIssuesOccurrence(lines[0], "subject", ret);
// Determining footer line numbers
int currentLine = lines.length-1;
while (currentLine >=0 && lines[currentLine].isEmpty()) {
currentLine--;
}
int footerEnd = currentLine + 1;
while (currentLine >=0 && !lines[currentLine].isEmpty()) {
currentLine--;
}
int footerStart = currentLine + 1;
if (footerStart == 0) {
// The first block of non-blank lines is not considered a footer, so
// we adjust that.
footerStart = -1;
}
// Parsing for "body", and "footer"
String body = null;
String footer = null;
if (footerStart == -1) {
// No footer could be found. So all lines after the first one (that's
// the subject) is the body.
//body = String[] templateParameters =
// Arrays.copyOfRange(allParameters, 1, allParameters.length);
if (lines.length > 0) {
body = StringUtils.join(lines, "\n", 1, lines.length);
}
} else {
body = StringUtils.join(lines, "\n", 1, footerStart - 1);
footer = StringUtils.join(lines, "\n", footerStart, footerEnd);
}
if (body != null) {
addIssuesOccurrence(body, "body", ret);
}
if (footer != null) {
addIssuesOccurrence(footer, "footer", ret);
}
}
return ret;
}
}