summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2013-11-25 13:02:20 -0500
committerJames Moger <james.moger@gitblit.com>2013-12-03 10:36:19 -0500
commitc6f3d01c29bb67156b8154bfe5780537b0ef43ac (patch)
tree9af2cdf8bc2137d937d48ef78ecd0971e9f7e7a6
parentb6976fc0a4b1fd07e1e72fdd06597a0ffb9d48ef (diff)
downloadgitblit-c6f3d01c29bb67156b8154bfe5780537b0ef43ac.tar.gz
gitblit-c6f3d01c29bb67156b8154bfe5780537b0ef43ac.zip
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
-rw-r--r--.classpath3
-rw-r--r--.gitbugtraq7
-rw-r--r--build.moxie3
-rw-r--r--gitblit.iml13
-rw-r--r--releases.moxie2
-rw-r--r--src/main/bugtraq/LICENSE43
-rw-r--r--src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java240
-rw-r--r--src/main/bugtraq/com/syntevo/bugtraq/BugtraqEntry.java61
-rw-r--r--src/main/bugtraq/com/syntevo/bugtraq/BugtraqException.java49
-rw-r--r--src/main/bugtraq/com/syntevo/bugtraq/BugtraqFormatter.java118
-rw-r--r--src/main/bugtraq/com/syntevo/bugtraq/BugtraqParser.java151
-rw-r--r--src/main/bugtraq/com/syntevo/bugtraq/BugtraqParserIssueId.java61
-rw-r--r--src/main/java/com/gitblit/servlet/SyndicationServlet.java2
-rw-r--r--src/main/java/com/gitblit/utils/MessageProcessor.java59
-rw-r--r--src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/CommitPage.java6
-rw-r--r--src/main/java/com/gitblit/wicket/pages/RepositoryPage.java2
-rw-r--r--src/main/resources/gitblit.css4
-rw-r--r--src/test/bugtraq/com/syntevo/bugtraq/BugtraqFormatterTest.java170
-rw-r--r--src/test/bugtraq/com/syntevo/bugtraq/BugtraqParserTest.java121
20 files changed, 1104 insertions, 13 deletions
diff --git a/.classpath b/.classpath
index 9dfa1930..b9367bfa 100644
--- a/.classpath
+++ b/.classpath
@@ -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" />
diff --git a/.gitbugtraq b/.gitbugtraq
new file mode 100644
index 00000000..9a2670c7
--- /dev/null
+++ b/.gitbugtraq
@@ -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]+)"
diff --git a/build.moxie b/build.moxie
index 44f4cf80..22fdfeac 100644
--- a/build.moxie
+++ b/build.moxie
@@ -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
diff --git a/gitblit.iml b/gitblit.iml
index 8c95ac68..f1daa9bd 100644
--- a/gitblit.iml
+++ b/gitblit.iml
@@ -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" />
@@ -56,6 +58,17 @@
</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>
<root url="jar://$MODULE_DIR$/ext/jcommander-1.17.jar!/" />
diff --git a/releases.moxie b/releases.moxie
index 3310cb10..9f559af1 100644
--- a/releases.moxie
+++ b/releases.moxie
@@ -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
}
#
diff --git a/src/main/bugtraq/LICENSE b/src/main/bugtraq/LICENSE
new file mode 100644
index 00000000..550aacc5
--- /dev/null
+++ b/src/main/bugtraq/LICENSE
@@ -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.
diff --git a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java
new file mode 100644
index 00000000..1512a9b0
--- /dev/null
+++ b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java
@@ -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;
+ }
+} \ No newline at end of file
diff --git a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqEntry.java b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqEntry.java
new file mode 100644
index 00000000..7ecd8bf8
--- /dev/null
+++ b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqEntry.java
@@ -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;
+ }
+} \ No newline at end of file
diff --git a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqException.java b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqException.java
new file mode 100644
index 00000000..fdc8dae6
--- /dev/null
+++ b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqException.java
@@ -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);
+ }
+} \ No newline at end of file
diff --git a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqFormatter.java b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqFormatter.java
new file mode 100644
index 00000000..978cd8c9
--- /dev/null
+++ b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqFormatter.java
@@ -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;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqParser.java b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqParser.java
new file mode 100644
index 00000000..8619c4c5
--- /dev/null
+++ b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqParser.java
@@ -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;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqParserIssueId.java b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqParserIssueId.java
new file mode 100644
index 00000000..fddb4dfc
--- /dev/null
+++ b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqParserIssueId.java
@@ -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;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/gitblit/servlet/SyndicationServlet.java b/src/main/java/com/gitblit/servlet/SyndicationServlet.java
index 739ee2d9..a35efa83 100644
--- a/src/main/java/com/gitblit/servlet/SyndicationServlet.java
+++ b/src/main/java/com/gitblit/servlet/SyndicationServlet.java
@@ -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;
diff --git a/src/main/java/com/gitblit/utils/MessageProcessor.java b/src/main/java/com/gitblit/utils/MessageProcessor.java
index 58493de4..087187f5 100644
--- a/src/main/java/com/gitblit/utils/MessageProcessor.java
+++ b/src/main/java/com/gitblit/utils/MessageProcessor.java
@@ -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));
+ }
+ }
}
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
index 5d14bfec..f3f61753 100644
--- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -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));
}
};
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.java b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
index 59422332..0998c714 100644
--- a/src/main/java/com/gitblit/wicket/pages/CommitPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
@@ -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))
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
index a108f922..dc0233b5 100644
--- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -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:
diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css
index c07f8bf8..58c0aed5 100644
--- a/src/main/resources/gitblit.css
+++ b/src/main/resources/gitblit.css
@@ -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;
diff --git a/src/test/bugtraq/com/syntevo/bugtraq/BugtraqFormatterTest.java b/src/test/bugtraq/com/syntevo/bugtraq/BugtraqFormatterTest.java
new file mode 100644
index 00000000..54f0e428
--- /dev/null
+++ b/src/test/bugtraq/com/syntevo/bugtraq/BugtraqFormatterTest.java
@@ -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);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/test/bugtraq/com/syntevo/bugtraq/BugtraqParserTest.java b/src/test/bugtraq/com/syntevo/bugtraq/BugtraqParserTest.java
new file mode 100644
index 00000000..424fd1c0
--- /dev/null
+++ b/src/test/bugtraq/com/syntevo/bugtraq/BugtraqParserTest.java
@@ -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());
+ }
+ }
+} \ No newline at end of file