blob: a9673b454ed3353df02203ece95ceab4e2ef4850 [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gitiles;
16
17import static com.google.common.base.Preconditions.checkNotNull;
18
19import com.google.common.base.Joiner;
20import com.google.common.collect.ImmutableMap;
21import com.google.common.collect.Lists;
22
23import java.util.List;
24import java.util.Map;
25import java.util.regex.Matcher;
26import java.util.regex.Pattern;
27
28import javax.servlet.http.HttpServletRequest;
29
30/** Linkifier for blocks of text such as commit message descriptions. */
31public class Linkifier {
32 private static final Pattern LINK_PATTERN;
33
34 static {
35 // HTTP URL regex adapted from com.google.gwtexpui.safehtml.client.SafeHtml.
Dave Borowitz5871ac82013-03-21 12:14:28 -070036 String part = "(?:" +
37 "[a-zA-Z0-9$_.+!*',%;:@=?#/~<>-]" +
38 "|&(?!lt;|gt;)" +
39 ")";
Dave Borowitz9de65952012-08-13 16:09:45 -070040 String httpUrl = "https?://" +
41 part + "{2,}" +
42 "(?:[(]" + part + "*" + "[)])*" +
43 part + "*";
44 String changeId = "\\bI[0-9a-f]{8,40}\\b";
45 LINK_PATTERN = Pattern.compile(Joiner.on("|").join(
46 "(" + httpUrl + ")",
47 "(" + changeId + ")"));
48 }
49
50 private final GitilesUrls urls;
51
52 public Linkifier(GitilesUrls urls) {
53 this.urls = checkNotNull(urls, "urls");
54 }
55
56 public List<Map<String, String>> linkify(HttpServletRequest req, String message) {
57 String baseGerritUrl = urls.getBaseGerritUrl(req);
58 List<Map<String, String>> parsed = Lists.newArrayList();
59 Matcher m = LINK_PATTERN.matcher(message);
60 int last = 0;
61 while (m.find()) {
62 addText(parsed, message.substring(last, m.start()));
63 if (m.group(1) != null) {
64 // Bare URL.
65 parsed.add(link(m.group(1), m.group(1)));
66 } else if (m.group(2) != null) {
67 if (baseGerritUrl != null) {
68 // Gerrit Change-Id.
69 parsed.add(link(m.group(2), baseGerritUrl + "#/q/" + m.group(2) + ",n,z"));
70 } else {
71 addText(parsed, m.group(2));
72 }
73 }
74 last = m.end();
75 }
76 addText(parsed, message.substring(last));
77 return parsed;
78 }
79
80 private static Map<String, String> link(String text, String url) {
81 return ImmutableMap.of("text", text, "url", url);
82 }
83
84 private static void addText(List<Map<String, String>> parts, String text) {
85 if (text.isEmpty()) {
86 return;
87 }
88 if (parts.isEmpty()) {
89 parts.add(ImmutableMap.of("text", text));
90 } else {
91 Map<String, String> old = parts.get(parts.size() - 1);
92 if (!old.containsKey("url")) {
93 parts.set(parts.size() - 1, ImmutableMap.of("text", old.get("text") + text));
94 } else {
95 parts.add(ImmutableMap.of("text", text));
96 }
97 }
98 }
99}