diff options
author | James Moger <james.moger@gitblit.com> | 2013-11-25 13:02:20 -0500 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2013-12-03 10:36:19 -0500 |
commit | c6f3d01c29bb67156b8154bfe5780537b0ef43ac (patch) | |
tree | 9af2cdf8bc2137d937d48ef78ecd0971e9f7e7a6 | |
parent | b6976fc0a4b1fd07e1e72fdd06597a0ffb9d48ef (diff) | |
download | gitblit-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
20 files changed, 1104 insertions, 13 deletions
@@ -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 |