| Dave Borowitz | 9de6595 | 2012-08-13 16:09:45 -0700 | [diff] [blame] | 1 | // 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 | |
| 15 | package com.google.gitiles; |
| 16 | |
| 17 | import static com.google.common.base.Preconditions.checkNotNull; |
| 18 | |
| 19 | import com.google.common.base.Joiner; |
| 20 | import com.google.common.collect.ImmutableMap; |
| 21 | import com.google.common.collect.Lists; |
| 22 | |
| 23 | import java.util.List; |
| 24 | import java.util.Map; |
| 25 | import java.util.regex.Matcher; |
| 26 | import java.util.regex.Pattern; |
| 27 | |
| 28 | import javax.servlet.http.HttpServletRequest; |
| 29 | |
| 30 | /** Linkifier for blocks of text such as commit message descriptions. */ |
| 31 | public 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 | } |