Browse Source

Add support for per-repository bugtraq configuration

Imported the reference implementation contributed by syntevo which
is used in their SmartGit product.  You may create a bugtraq config
section inf your .git/config file OR you may add a .gitbugtraq file
to the root of your repository.

Example:

    [bugtraq "issues"]
        url = http://code.google.com/p/gitblit/issues/detail?id=%BUGID%
        logRegex = "[Ii]ssue[-#:\\s]{1}\\d+"
        logRegex1 = "\\d+"
    [bugtraq "[pullrequests"]
        url = "https://github.com/gitblit/gitblit/pull/%BUGID%"
        logRegex = "(?:pull request|pull|pr)\\s*[-#]?([0-9]+)"

Change-Id: Iaba305bf4280d08cc4d1abf533c2f1365470a43f
tags/v1.4.0
James Moger 10 years ago
parent
commit
c6f3d01c29

+ 3
- 0
.classpath View File

@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java" />
<classpathentry kind="src" path="src/main/bugtraq" />
<classpathentry kind="src" path="src/test/java" output="bin/test-classes" />
<classpathentry kind="src" path="src/test/bugtraq" output="bin/test-classes" />
<classpathentry kind="src" path="src/main/resources" />
<classpathentry kind="lib" path="ext/dagger-1.1.0.jar" sourcepath="ext/src/dagger-1.1.0.jar" />
<classpathentry kind="lib" path="ext/javax.inject-1.jar" sourcepath="ext/src/javax.inject-1.jar" />
<classpathentry kind="lib" path="ext/dagger-compiler-1.1.0.jar" sourcepath="ext/src/dagger-compiler-1.1.0.jar" />
<classpathentry kind="lib" path="ext/javawriter-2.1.1.jar" sourcepath="ext/src/javawriter-2.1.1.jar" />
<classpathentry kind="lib" path="ext/annotations-12.0.jar" sourcepath="ext/src/annotations-12.0.jar" />
<classpathentry kind="lib" path="ext/jcommander-1.17.jar" sourcepath="ext/src/jcommander-1.17.jar" />
<classpathentry kind="lib" path="ext/log4j-1.2.17.jar" sourcepath="ext/src/log4j-1.2.17.jar" />
<classpathentry kind="lib" path="ext/slf4j-api-1.6.6.jar" sourcepath="ext/src/slf4j-api-1.6.6.jar" />

+ 7
- 0
.gitbugtraq View File

@@ -0,0 +1,7 @@
[bugtraq "issues"]
url = http://code.google.com/p/gitblit/issues/detail?id=%BUGID%
logRegex = "[Ii]ssue[-#:\\s]{1}\\d+"
logRegex1 = "\\d+"
[bugtraq "[pullrequests"]
url = "https://github.com/gitblit/gitblit/pull/%BUGID%"
logRegex = "(?:pull request|pull|pr)\\s*[-#]?([0-9]+)"

+ 3
- 0
build.moxie View File

@@ -57,8 +57,10 @@ mainclass: com.gitblit.GitBlitServer
# a scope to each directory.
sourceDirectories:
- compile 'src/main/java'
- compile 'src/main/bugtraq'
- compile 'src/main/dagger' apt
- test 'src/test/java'
- test 'src/test/bugtraq'
# Moxie supports one site-scoped directory for mx:doc
- site 'src/site'

@@ -125,6 +127,7 @@ dependencies:
- compile 'com.squareup.dagger:dagger:1.1.0' :war apt
- compile 'com.squareup.dagger:dagger-compiler:1.1.0' :war optional apt
# Standard dependencies
- compile 'com.intellij:annotations:12.0' :war
- compile 'com.beust:jcommander:1.17' :fedclient :authority
- compile 'log4j:log4j:1.2.17' :war :fedclient :authority
- compile 'org.slf4j:slf4j-api:1.6.6' :war :fedclient :authority

+ 13
- 0
gitblit.iml View File

@@ -6,8 +6,10 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/bugtraq" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/dagger" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/bugtraq" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
@@ -55,6 +57,17 @@
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="annotations-12.0.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/annotations-12.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/ext/src/annotations-12.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jcommander-1.17.jar">
<CLASSES>

+ 2
- 0
releases.moxie View File

@@ -53,6 +53,7 @@ r20: {
- Added a normalized diffstat display to the commit, commitdiff, and compare pages
- Added GO setting to automatically redirect all http requests to the secure https connector
- Automatically display common repository root documents as tabs on the docs page
- Support bugtraq configuration in collaboration with syntevo
dependencyChanges:
- updated to Jetty 8.1.13
- updated to JGit 3.1.0
@@ -85,6 +86,7 @@ r20: {
- fpeters.fae
- David Ostrovsky
- Alex Lewis
- Marc Strapetz
}

#

+ 43
- 0
src/main/bugtraq/LICENSE View File

@@ -0,0 +1,43 @@
Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

o Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

o Neither the name of syntevo GmbH nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Third Parties
-------------

The following third parties have rights on parts of the SOFTWARE:

- IDEA annotations.jar, copyright by JetBrains, Inc.

- JGit, copyright by various authors (http://repo.or.cz/w/jgit.git and
http://jgit.org).
The corresponding license agreement can be found at
http://repo.or.cz/w/jgit.git/blob/HEAD:/LICENSE.

- JUnit, copyright by www.hamcrest.org.
The corresponding license agreement can be found at
in subdirectory lib/JUNIT-LICENSE.

+ 240
- 0
src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java View File

@@ -0,0 +1,240 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public final class BugtraqConfig {

// Constants ==============================================================

private static final String DOT_GIT_BUGTRAQ = ".gitbugtraq";

private static final String BUGTRAQ = "bugtraq";

private static final String URL = "url";
private static final String ENABLED = "enabled";
private static final String LOG_REGEX = "logRegex";

// Static =================================================================

@Nullable
public static BugtraqConfig read(@NotNull Repository repository) throws IOException, ConfigInvalidException {
final Config baseConfig = getBaseConfig(repository);
final Set<String> allNames = new HashSet<String>();
final Config config = repository.getConfig();
allNames.addAll(config.getSubsections(BUGTRAQ));
if (baseConfig != null) {
allNames.addAll(baseConfig.getSubsections(BUGTRAQ));
}

final List<BugtraqEntry> entries = new ArrayList<BugtraqEntry>();
for (String name : allNames) {
final String url = getString(name, URL, config, baseConfig);
final String enabled = getString(name, ENABLED, config, baseConfig);
if (enabled != null && !"true".equals(enabled)) {
continue;
}

final String logIdRegex = getString(name, LOG_REGEX, config, baseConfig);
if (url == null || logIdRegex == null) {
return null;
}

final List<String> logIdRegexs = new ArrayList<String>();
logIdRegexs.add(logIdRegex);

for (int index = 1; index < Integer.MAX_VALUE; index++) {
final String logIdRegexN = getString(name, LOG_REGEX + index, config, baseConfig);
if (logIdRegexN == null) {
break;
}

logIdRegexs.add(logIdRegexN);
}

entries.add(new BugtraqEntry(url, logIdRegexs));
}

if (entries.isEmpty()) {
return null;
}

return new BugtraqConfig(entries);
}

// Fields =================================================================

@NotNull
private final List<BugtraqEntry> entries;

// Setup ==================================================================

BugtraqConfig(@NotNull List<BugtraqEntry> entries) {
this.entries = entries;
}

// Accessing ==============================================================

@NotNull
public List<BugtraqEntry> getEntries() {
return Collections.unmodifiableList(entries);
}

// Utils ==================================================================

@Nullable
private static Config getBaseConfig(Repository repository) throws IOException, ConfigInvalidException {
final Config baseConfig;
if (repository.isBare()) {
// read bugtraq config directly from the repository
String content = null;
String head = repository.getFullBranch();
RevWalk rw = new RevWalk(repository);
TreeWalk tw = new TreeWalk(repository);
tw.setFilter(PathFilterGroup.createFromStrings(DOT_GIT_BUGTRAQ));
try {
ObjectId headId = repository.resolve(head);
RevCommit commit = rw.parseCommit(headId);
RevTree tree = commit.getTree();
tw.reset(tree);
while (tw.next()) {
if (tw.isSubtree()) {
tw.enterSubtree();
continue;
}
ObjectId entid = tw.getObjectId(0);
FileMode entmode = tw.getFileMode(0);
if (entmode == FileMode.REGULAR_FILE) {
RevObject ro = rw.lookupAny(entid, entmode.getObjectType());
rw.parseBody(ro);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectLoader ldr = repository.open(ro.getId(), Constants.OBJ_BLOB);
byte[] tmp = new byte[4096];
InputStream in = ldr.openStream();
int n;
while ((n = in.read(tmp)) > 0) {
os.write(tmp, 0, n);
}
in.close();
content = new String(os.toByteArray(), commit.getEncoding());
}
}
} finally {
rw.dispose();
tw.release();
}

if (content == null) {
// config not found
baseConfig = null;
} else {
// parse the config
Config cfg = new Config();
cfg.fromText(content);
baseConfig = new StoredConfig(cfg) {
@Override
public void save() throws IOException {
}

@Override
public void load() throws IOException, ConfigInvalidException {
}
};
}
} else {
// read bugtraq config from work tree
final File baseFile = new File(repository.getWorkTree(), DOT_GIT_BUGTRAQ);
if (baseFile.isFile()) {
FileBasedConfig fileConfig = new FileBasedConfig(baseFile, repository.getFS());
fileConfig.load();
baseConfig = fileConfig;
}
else {
baseConfig = null;
}
}
return baseConfig;
}

@Nullable
private static String getString(@NotNull String subsection, @NotNull String key, @NotNull Config config, @Nullable Config baseConfig) {
final String value = config.getString(BUGTRAQ, subsection, key);
if (value != null) {
return trimMaybeNull(value);
}

if (baseConfig != null) {
return trimMaybeNull(baseConfig.getString(BUGTRAQ, subsection, key));
}

return value;
}

@Nullable
private static String trimMaybeNull(@Nullable String string) {
if (string == null) {
return null;
}

string = string.trim();
if (string.length() == 0) {
return null;
}

return string;
}
}

+ 61
- 0
src/main/bugtraq/com/syntevo/bugtraq/BugtraqEntry.java View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

import java.util.*;

import org.jetbrains.annotations.*;

final class BugtraqEntry {

// Fields =================================================================

private final String url;
private final BugtraqParser parser;

// Setup ==================================================================

public BugtraqEntry(@NotNull String url, @NotNull List<String> logIdRegexs) throws BugtraqException {
this.url = url;
this.parser = BugtraqParser.createInstance(logIdRegexs);
}

// Accessing ==============================================================

@NotNull
public String getUrl() {
return url;
}

@NotNull
public BugtraqParser getParser() {
return parser;
}
}

+ 49
- 0
src/main/bugtraq/com/syntevo/bugtraq/BugtraqException.java View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

import java.io.*;

public class BugtraqException extends IOException {

// Setup ==================================================================

public BugtraqException(String message) {
super(message);
}

public BugtraqException(String message, Throwable cause) {
super(message, cause);
}

public BugtraqException(Throwable cause) {
super(cause);
}
}

+ 118
- 0
src/main/bugtraq/com/syntevo/bugtraq/BugtraqFormatter.java View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

import java.util.*;

import org.jetbrains.annotations.*;

public final class BugtraqFormatter {

// Fields =================================================================

private final BugtraqConfig config;

// Setup ==================================================================

public BugtraqFormatter(@NotNull BugtraqConfig config) {
this.config = config;
}

// Accessing ==============================================================

public void formatLogMessage(@NotNull String message, @NotNull OutputHandler outputHandler) {
final SortedSet<IssueId> allIds = new TreeSet<IssueId>(new Comparator<IssueId>() {
@Override
public int compare(IssueId o1, IssueId o2) {
final int from1 = o1.id.getFrom();
final int from2 = o2.id.getFrom();
return from1 > from2 ? +1 : from1 < from2 ? -1 : 0;
}
});

for (BugtraqEntry entry : config.getEntries()) {
final List<BugtraqParserIssueId> ids = entry.getParser().parse(message);
if (ids == null) {
continue;
}

for (BugtraqParserIssueId id : ids) {
allIds.add(new IssueId(entry, id));
}
}

int lastIdEnd = -1;
for (IssueId issueId : allIds) {
final BugtraqParserIssueId id = issueId.id;
if (id.getFrom() <= lastIdEnd) {
continue;
}

appendText(message.substring(lastIdEnd + 1, id.getFrom()), outputHandler);

final String linkText = message.substring(id.getFrom(), id.getTo() + 1);
final String target = issueId.entry.getUrl().replace("%BUGID%", id.getId());
outputHandler.appendLink(linkText, target);
lastIdEnd = id.getTo();
}

if (lastIdEnd - 1 < message.length()) {
appendText(message.substring(lastIdEnd + 1, message.length()), outputHandler);
}
}

// Utils ==================================================================

private static void appendText(@NotNull String message, @NotNull OutputHandler outputHandler) {
if (message.length() == 0) {
return;
}

outputHandler.appendText(message);
}

// Inner Classes ==========================================================

public interface OutputHandler {
void appendText(@NotNull String text);

void appendLink(@NotNull String name, @NotNull String target);
}
private static class IssueId {
private final BugtraqEntry entry;
private final BugtraqParserIssueId id;

private IssueId(BugtraqEntry entry, BugtraqParserIssueId id) {
this.entry = entry;
this.id = id;
}
}
}

+ 151
- 0
src/main/bugtraq/com/syntevo/bugtraq/BugtraqParser.java View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

import java.util.*;
import java.util.regex.*;

import org.jetbrains.annotations.*;

final class BugtraqParser {

// Static =================================================================

@NotNull
public static BugtraqParser createInstance(@NotNull List<String> regexs) throws BugtraqException {
try {
return new BugtraqParser(regexs);
}
catch (PatternSyntaxException ex) {
throw new BugtraqException(ex);
}
}

// Fields =================================================================

private final List<Pattern> patterns;

// Setup ==================================================================

private BugtraqParser(List<String> regexs) {
this.patterns = new ArrayList<Pattern>();

for (String regex : regexs) {
patterns.add(compilePatternSafe(regex));
}
}

// Accessing ==============================================================

@Nullable
public List<BugtraqParserIssueId> parse(@NotNull String message) {
List<Part> parts = new ArrayList<Part>();
parts.add(new Part(message, 0, message.length() - 1));

boolean firstMatch = false;

for (Pattern pattern : patterns) {
final List<Part> newParts = new ArrayList<Part>();
for (Part part : parts) {
final Matcher matcher = pattern.matcher(part.text);
while (matcher.find()) {
firstMatch = true;
if (matcher.groupCount() == 0) {
addNewPart(part, matcher, 0, newParts);
}
else {
addNewPart(part, matcher, 1, newParts);
}
}
}

parts = newParts;
if (parts.isEmpty()) {
parts = null;
break;
}
}

if (!firstMatch) {
return null;
}

if (parts == null) {
parts = new ArrayList<Part>();
}

final List<BugtraqParserIssueId> ids = new ArrayList<BugtraqParserIssueId>();
for (Part part : parts) {
final BugtraqParserIssueId id = new BugtraqParserIssueId(part.from, part.to, part.text);
if (ids.size() > 0) {
final BugtraqParserIssueId lastId = ids.get(ids.size() - 1);
if (id.getFrom() <= lastId.getTo()) {
continue;
}
}

ids.add(id);
}

return ids;
}

// Utils ==================================================================

private static void addNewPart(Part part, Matcher matcher, int group, List<Part> newParts) {
final int textStart = matcher.start(group) + part.from;
final int textEnd = matcher.end(group) - 1 + part.from;
if (textEnd < 0) {
return;
}

final Part newPart = new Part(matcher.group(group), textStart, textEnd);
newParts.add(newPart);
}

private static Pattern compilePatternSafe(String pattern) throws PatternSyntaxException {
return Pattern.compile(pattern);
}

// Inner Classes ==========================================================

private static class Part {

private final int from;
private final int to;
private final String text;

public Part(String text, int from, int to) {
this.text = text;
this.from = from;
this.to = to;
}
}
}

+ 61
- 0
src/main/bugtraq/com/syntevo/bugtraq/BugtraqParserIssueId.java View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

final class BugtraqParserIssueId {

// Fields =================================================================

private final int from;
private final int to;
private final String id;

// Setup ==================================================================

public BugtraqParserIssueId(int from, int to, String id) {
this.id = id;
this.from = from;
this.to = to;
}

// Accessing ==============================================================

public int getFrom() {
return from;
}

public int getTo() {
return to;
}

public String getId() {
return id;
}
}

+ 1
- 1
src/main/java/com/gitblit/servlet/SyndicationServlet.java View File

@@ -273,7 +273,7 @@ public class SyndicationServlet extends HttpServlet {
StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());
entry.published = commit.getCommitterIdent().getWhen();
entry.contentType = "text/html";
String message = processor.processCommitMessage(model, commit.getFullMessage());
String message = processor.processCommitMessage(repository, model, commit.getFullMessage());
entry.content = message;
entry.repository = model.name;
entry.branch = objectId;

+ 52
- 7
src/main/java/com/gitblit/utils/MessageProcessor.java View File

@@ -15,17 +15,24 @@
*/
package com.gitblit.utils;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.models.RepositoryModel;
import com.syntevo.bugtraq.BugtraqConfig;
import com.syntevo.bugtraq.BugtraqFormatter;
import com.syntevo.bugtraq.BugtraqFormatter.OutputHandler;

public class MessageProcessor {

@@ -44,14 +51,15 @@ public class MessageProcessor {
* This method uses the preferred renderer to transform the commit message.
*
* @param repository
* @param model
* @param text
* @return html version of the commit message
*/
public String processCommitMessage(RepositoryModel repository, String text) {
switch (repository.commitMessageRenderer) {
public String processCommitMessage(Repository repository, RepositoryModel model, String text) {
switch (model.commitMessageRenderer) {
case MARKDOWN:
try {
String prepared = processCommitMessageRegex(repository.name, text);
String prepared = processCommitMessageRegex(repository, model.name, text);
return MarkdownUtils.transformMarkdown(prepared);
} catch (Exception e) {
logger.error("Failed to render commit message as markdown", e);
@@ -62,7 +70,7 @@ public class MessageProcessor {
break;
}

return processPlainCommitMessage(repository.name, text);
return processPlainCommitMessage(repository, model.name, text);
}

/**
@@ -71,13 +79,14 @@ public class MessageProcessor {
*
* This method assumes the commit message is plain text.
*
* @param repository
* @param repositoryName
* @param text
* @return html version of the commit message
*/
public String processPlainCommitMessage(String repositoryName, String text) {
public String processPlainCommitMessage(Repository repository, String repositoryName, String text) {
String html = StringUtils.escapeForHtml(text, false);
html = processCommitMessageRegex(repositoryName, html);
html = processCommitMessageRegex(repository, repositoryName, html);
return StringUtils.breakLinesForHtml(html);

}
@@ -86,11 +95,12 @@ public class MessageProcessor {
* Apply globally or per-repository specified regex substitutions to the
* commit message.
*
* @param repository
* @param repositoryName
* @param text
* @return the processed commit message
*/
protected String processCommitMessageRegex(String repositoryName, String text) {
protected String processCommitMessageRegex(Repository repository, String repositoryName, String text) {
Map<String, String> map = new HashMap<String, String>();
// global regex keys
if (settings.getBoolean(Keys.regex.global, false)) {
@@ -121,6 +131,41 @@ public class MessageProcessor {
+ definition);
}
}

try {
// parse bugtraq repo config
BugtraqConfig config = BugtraqConfig.read(repository);
if (config != null) {
BugtraqFormatter formatter = new BugtraqFormatter(config);
StringBuilder sb = new StringBuilder();
formatter.formatLogMessage(text, new BugtraqOutputHandler(sb));
text = sb.toString();
}
} catch (IOException e) {
logger.error(MessageFormat.format("Bugtraq config for {0} is invalid!", repositoryName), e);
} catch (ConfigInvalidException e) {
logger.error(MessageFormat.format("Bugtraq config for {0} is invalid!", repositoryName), e);
}

return text;
}

private class BugtraqOutputHandler implements OutputHandler {

final StringBuilder sb;

BugtraqOutputHandler(StringBuilder sb) {
this.sb = sb;
}

@Override
public void appendText(String text) {
sb.append(text);
}

@Override
public void appendLink(String name, String target) {
sb.append(MessageFormat.format("<a class=\"bugtraq\" href=\"{1}\">{0}</a>", name, target));
}
}
}

+ 1
- 1
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java View File

@@ -107,7 +107,7 @@ public class CommitDiffPage extends RepositoryPage {
item.add(new GravatarImage("noteAuthorAvatar", entry.notesRef.getAuthorIdent()));
item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef
.getAuthorIdent().getWhen(), getTimeZone(), getTimeUtils()));
item.add(new Label("noteContent", messageProcessor().processPlainCommitMessage(repositoryName,
item.add(new Label("noteContent", messageProcessor().processPlainCommitMessage(getRepository(), repositoryName,
entry.content)).setEscapeModelStrings(false));
}
};

+ 3
- 3
src/main/java/com/gitblit/wicket/pages/CommitPage.java View File

@@ -135,7 +135,7 @@ public class CommitPage extends RepositoryPage {
item.add(new GravatarImage("noteAuthorAvatar", entry.notesRef.getAuthorIdent()));
item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef
.getAuthorIdent().getWhen(), getTimeZone(), getTimeUtils()));
item.add(new Label("noteContent", messageProcessor().processPlainCommitMessage(repositoryName,
item.add(new Label("noteContent", messageProcessor().processPlainCommitMessage(getRepository(), repositoryName,
entry.content)).setEscapeModelStrings(false));
}
};
@@ -198,11 +198,11 @@ public class CommitPage extends RepositoryPage {
.newPathParameter(repositoryName, entry.commitId, path)));
}
// quick links
if (entry.isSubmodule()) {
item.add(new ExternalLink("raw", "").setEnabled(false));
// submodule
item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils
.newPathParameter(repositoryName, entry.commitId, entry.path))

+ 1
- 1
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java View File

@@ -516,7 +516,7 @@ public abstract class RepositoryPage extends RootPage {
protected void addFullText(String wicketId, String text) {
RepositoryModel model = getRepositoryModel();
String content = messageProcessor().processCommitMessage(model, text);
String content = messageProcessor().processCommitMessage(r, model, text);
String html;
switch (model.commitMessageRenderer) {
case MARKDOWN:

+ 4
- 0
src/main/resources/gitblit.css View File

@@ -34,6 +34,10 @@ a.btn i {
vertical-align: text-bottom;
}
a.bugtraq {
font-weight: bold;
}
[class^="icon-"], [class*=" icon-"] i {
/* override for a links that look like bootstrap buttons */
vertical-align: text-bottom;

+ 170
- 0
src/test/bugtraq/com/syntevo/bugtraq/BugtraqFormatterTest.java View File

@@ -0,0 +1,170 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

import junit.framework.*;

import java.util.*;

import org.jetbrains.annotations.*;

public class BugtraqFormatterTest extends TestCase {

// Accessing ==============================================================

public void testSimple() throws BugtraqException {
final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/%BUGID%", "JRA-\\d+"));
doTest(formatter, "JRA-7399: Email subject formatting", l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(": Email subject formatting"));
doTest(formatter, " JRA-7399, JRA-7398: Email subject formatting", t(" "), l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(", "), l("JRA-7398", "https://jira.atlassian.com/browse/JRA-7398"), t(": Email subject formatting"));
doTest(formatter, "Fixed JRA-7399", t("Fixed "), l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"));
}

public void testTwoNonIntersectingConfigurations() throws BugtraqException {
final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/%BUGID%", "JRA-\\d+"),
createEntry("https://issues.apache.org/jira/browse/%BUGID%", "VELOCITY-\\d+"));
doTest(formatter, "JRA-7399, VELOCITY-847: fix", l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(", "), l("VELOCITY-847", "https://issues.apache.org/jira/browse/VELOCITY-847"), t(": fix"));
doTest(formatter, " JRA-7399: fix/VELOCITY-847", t(" "), l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(": fix/"), l("VELOCITY-847", "https://issues.apache.org/jira/browse/VELOCITY-847"));
doTest(formatter, "JRA-7399VELOCITY-847", l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), l("VELOCITY-847", "https://issues.apache.org/jira/browse/VELOCITY-847"));
}

public void testTwoIntersectingConfigurations() throws BugtraqException {
final BugtraqFormatter formatter = createFormatter(createEntry("https://host1/%BUGID%", "A[AB]"),
createEntry("https://host2/%BUGID%", "BA[A]?"));
doTest(formatter, "AA: fix", l("AA", "https://host1/AA"), t(": fix"));
doTest(formatter, "AB: fix", l("AB", "https://host1/AB"), t(": fix"));
doTest(formatter, "BA: fix", l("BA", "https://host2/BA"), t(": fix"));
doTest(formatter, "BAA: fix", l("BAA", "https://host2/BAA"), t(": fix"));
doTest(formatter, "BAAA: fix", l("BAA", "https://host2/BAA"), t("A: fix"));
doTest(formatter, "BAAAA: fix", l("BAA", "https://host2/BAA"), l("AA", "https://host1/AA"), t(": fix"));
doTest(formatter, "BAAAAA: fix", l("BAA", "https://host2/BAA"), l("AA", "https://host1/AA"), t("A: fix"));
doTest(formatter, "BAAABA: fix", l("BAA", "https://host2/BAA"), l("AB", "https://host1/AB"), t("A: fix"));
doTest(formatter, "BAAABAA: fix", l("BAA", "https://host2/BAA"), l("AB", "https://host1/AB"), l("AA", "https://host1/AA"), t(": fix"));
doTest(formatter, "BAAB: fix", l("BAA", "https://host2/BAA"), t("B: fix"));
doTest(formatter, "BAAAB: fix", l("BAA", "https://host2/BAA"), l("AB", "https://host1/AB"), t(": fix"));
doTest(formatter, "BAABBA: fix", l("BAA", "https://host2/BAA"), t("B"), l("BA", "https://host2/BA"), t(": fix"));
}

// Utils ==================================================================

private BugtraqFormatter createFormatter(BugtraqEntry ... entries) {
return new BugtraqFormatter(new BugtraqConfig(Arrays.asList(entries)));
}
private BugtraqEntry createEntry(String url, String ... logRegexs) throws BugtraqException {
return new BugtraqEntry(url, Arrays.asList(logRegexs));
}
private Text t(String text) {
return new Text(text);
}
private Link l(String text, String url) {
return new Link(text, url);
}
private void doTest(BugtraqFormatter formatter, String message, Atom ... expectedAtoms) {
final List<Atom> actualAtoms = new ArrayList<Atom>();
formatter.formatLogMessage(message, new BugtraqFormatter.OutputHandler() {
@Override
public void appendText(@NotNull String text) {
actualAtoms.add(t(text));
}

@Override
public void appendLink(@NotNull String name, @NotNull String target) {
actualAtoms.add(l(name, target));
}
});
assertEquals(Arrays.asList(expectedAtoms), actualAtoms);
}

// Inner Classes ==========================================================

private static interface Atom {
}
private static class Text implements Atom {
private final String text;

private Text(String text) {
this.text = text;
}

@Override
public String toString() {
return text;
}

@Override
public int hashCode() {
return text.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
return text.equals(((Text)obj).text);
}
}
private static class Link implements Atom {
private final String text;
private final String url;

private Link(String text, String url) {
this.text = text;
this.url = url;
}

@Override
public String toString() {
return "(" + text + "," + url + ")";
}

@Override
public int hashCode() {
return text.hashCode() ^ url.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
return text.equals(((Link)obj).text)
&& url.equals(((Link)obj).url);
}
}
}

+ 121
- 0
src/test/bugtraq/com/syntevo/bugtraq/BugtraqParserTest.java View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;

import junit.framework.*;

import java.util.*;

public class BugtraqParserTest extends TestCase {

// Accessing ==============================================================

public void testSimple1() throws BugtraqException {
final BugtraqParser parser = createParser("\\d");
assertNull(parser.parse(""));
doTest("1", parser, id(0, 0, "1"));
doTest("1 2 3", parser, id(0, 0, "1"), id(2, 2, "2"), id(4, 4, "3"));
}

public void testSimple2() throws BugtraqException {
final BugtraqParser parser = createParser("(\\d)");
assertNull(parser.parse(""));
doTest("1", parser, id(0, 0, "1"));
doTest("1 2 3", parser, id(0, 0, "1"), id(2, 2, "2"), id(4, 4, "3"));
}

public void testSimple3() throws BugtraqException {
final BugtraqParser parser = createParser("(SG-\\d)");
assertNull(parser.parse(""));
doTest("SG-1", parser, id(0, 3, "SG-1"));
doTest("SG-1 SG-2 SG-3", parser, id(0, 3, "SG-1"), id(5, 8, "SG-2"), id(10, 13, "SG-3"));
}

public void testSimple4() throws BugtraqException {
final BugtraqParser parser = createParser("SG-(\\d)");
assertNull(parser.parse(""));
doTest("SG-1", parser, id(3, 3, "1"));
doTest("SG-1 SG-2 SG-3", parser, id(3, 3, "1"), id(8, 8, "2"), id(13, 13, "3"));
}

public void testTwoLevel1() throws BugtraqException {
final BugtraqParser parser = createParser("(SG-\\d)", "\\d");
doTest("SG-1", parser, id(3, 3, "1"));
doTest("SG-1 SG-2 SG-3", parser, id(3, 3, "1"), id(8, 8, "2"), id(13, 13, "3"));
}

public void testTwoLevel2() throws BugtraqException {
final BugtraqParser parser = createParser("xSG-\\dx", "\\d");
doTest("SG-1 xSG-2x SG-3", parser, id(9, 9, "2"));
}

public void testTwoLevel3() throws BugtraqException {
final BugtraqParser parser = createParser("[Ii]ssues?:?((\\s*(,|and)?\\s*#\\d+)+)", "\\d");
doTest("Issues #3, #4 and #5: Git Bugtraq Configuration options (see #12)", parser, id(8, 8, "3"), id(12, 12, "4"), id(19, 19, "5"));
}

public void testThreeLevel() throws BugtraqException {
final BugtraqParser parser = createParser("[ab]\\d[cd]", "a\\dc|b\\dd", "\\d");
doTest("a1c a2d b3c b4d", parser, id(1, 1, "1"), id(13, 13, "4"));
}

public void testFogBugz() throws BugtraqException {
final BugtraqParser parser = createParser("(?:Bug[zs]?\\s*IDs?\\s*|Cases?)[#:; ]+((\\d+[ ,:;#]*)+)", "\\d");
doTest("Bug IDs: 3, 4, 5", parser, id(9, 9, "3"), id(12, 12, "4"), id(15, 15, "5"));
}

public void testFogBugzInvalid() throws BugtraqException {
final BugtraqParser parser = createParser("Bug[zs]?\\s*IDs?\\s*|Cases?[#:; ]+((\\d+[ ,:;#]*)+)", "\\d");
doTest("Bug IDs: 3, 4, 5", parser);
}

// Utils ==================================================================

private BugtraqParser createParser(String ... regexs) throws BugtraqException {
return BugtraqParser.createInstance(Arrays.asList(regexs));
}
private BugtraqParserIssueId id(int from, int to, String id) {
return new BugtraqParserIssueId(from, to, id);
}
private void doTest(String message, BugtraqParser parser, BugtraqParserIssueId... expectedIds) {
final List<BugtraqParserIssueId> actualIds = parser.parse(message);
assertEquals(expectedIds.length, actualIds.size());
for (int index = 0; index < expectedIds.length; index++) {
final BugtraqParserIssueId expectedId = expectedIds[index];
final BugtraqParserIssueId actualId = actualIds.get(index);
assertEquals(expectedId.getFrom(), actualId.getFrom());
assertEquals(expectedId.getTo(), actualId.getTo());
assertEquals(expectedId.getId(), actualId.getId());
}
}
}

Loading…
Cancel
Save