blob: 735e6f5c8725b9d4ebcdc6144044aef247c40a1c [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.
36 String part = "[a-zA-Z0-9$_.+!*',%;:@=?#/~<>-]";
37 String httpUrl = "https?://" +
38 part + "{2,}" +
39 "(?:[(]" + part + "*" + "[)])*" +
40 part + "*";
41 String changeId = "\\bI[0-9a-f]{8,40}\\b";
42 LINK_PATTERN = Pattern.compile(Joiner.on("|").join(
43 "(" + httpUrl + ")",
44 "(" + changeId + ")"));
45 }
46
47 private final GitilesUrls urls;
48
49 public Linkifier(GitilesUrls urls) {
50 this.urls = checkNotNull(urls, "urls");
51 }
52
53 public List<Map<String, String>> linkify(HttpServletRequest req, String message) {
54 String baseGerritUrl = urls.getBaseGerritUrl(req);
55 List<Map<String, String>> parsed = Lists.newArrayList();
56 Matcher m = LINK_PATTERN.matcher(message);
57 int last = 0;
58 while (m.find()) {
59 addText(parsed, message.substring(last, m.start()));
60 if (m.group(1) != null) {
61 // Bare URL.
62 parsed.add(link(m.group(1), m.group(1)));
63 } else if (m.group(2) != null) {
64 if (baseGerritUrl != null) {
65 // Gerrit Change-Id.
66 parsed.add(link(m.group(2), baseGerritUrl + "#/q/" + m.group(2) + ",n,z"));
67 } else {
68 addText(parsed, m.group(2));
69 }
70 }
71 last = m.end();
72 }
73 addText(parsed, message.substring(last));
74 return parsed;
75 }
76
77 private static Map<String, String> link(String text, String url) {
78 return ImmutableMap.of("text", text, "url", url);
79 }
80
81 private static void addText(List<Map<String, String>> parts, String text) {
82 if (text.isEmpty()) {
83 return;
84 }
85 if (parts.isEmpty()) {
86 parts.add(ImmutableMap.of("text", text));
87 } else {
88 Map<String, String> old = parts.get(parts.size() - 1);
89 if (!old.containsKey("url")) {
90 parts.set(parts.size() - 1, ImmutableMap.of("text", old.get("text") + text));
91 } else {
92 parts.add(ImmutableMap.of("text", text));
93 }
94 }
95 }
96}