b5458fc19353553e6dd8b2ba41164e0ba0b30e31
[idea/community.git] / platform / vcs-api / src / com / intellij / openapi / vcs / IssueNavigationConfiguration.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.openapi.vcs;
18
19 import com.intellij.lifecycle.PeriodicalTasksCloser;
20 import com.intellij.openapi.components.PersistentStateComponent;
21 import com.intellij.openapi.components.State;
22 import com.intellij.openapi.components.Storage;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.SimpleModificationTracker;
26 import com.intellij.openapi.util.TextRange;
27 import com.intellij.util.io.URLUtil;
28 import com.intellij.util.xmlb.XmlSerializerUtil;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 /**
38  * @author yole
39  */
40 @State(name = "IssueNavigationConfiguration", storages = @Storage("vcs.xml"))
41 public class IssueNavigationConfiguration extends SimpleModificationTracker
42   implements PersistentStateComponent<IssueNavigationConfiguration> {
43   private static final Logger LOG = Logger.getInstance(IssueNavigationConfiguration.class);
44
45   public static IssueNavigationConfiguration getInstance(Project project) {
46     return PeriodicalTasksCloser.getInstance().safeGetService(project, IssueNavigationConfiguration.class);
47   }
48
49   private List<IssueNavigationLink> myLinks = new ArrayList<>();
50
51   public List<IssueNavigationLink> getLinks() {
52     return myLinks;
53   }
54
55   public void setLinks(final List<IssueNavigationLink> links) {
56     myLinks = new ArrayList<>(links);
57     incModificationCount();
58   }
59
60   public IssueNavigationConfiguration getState() {
61     return this;
62   }
63
64   public void loadState(IssueNavigationConfiguration state) {
65     XmlSerializerUtil.copyBean(state, this);
66   }
67
68   public static class LinkMatch implements Comparable {
69     private final TextRange myRange;
70     private final String myTargetUrl;
71
72     public LinkMatch(final TextRange range, final String targetUrl) {
73       myRange = range;
74       myTargetUrl = targetUrl;
75     }
76
77     public TextRange getRange() {
78       return myRange;
79     }
80
81     public String getTargetUrl() {
82       return myTargetUrl;
83     }
84
85     public int compareTo(Object o) {
86       if (!(o instanceof LinkMatch)) {
87         return 0;
88       }
89       return myRange.getStartOffset() - ((LinkMatch)o).getRange().getStartOffset();
90     }
91   }
92
93   public List<LinkMatch> findIssueLinks(CharSequence text) {
94     final List<LinkMatch> result = new ArrayList<>();
95     for (IssueNavigationLink link : myLinks) {
96       Pattern issuePattern = link.getIssuePattern();
97       Matcher m = issuePattern.matcher(text);
98       while (m.find()) {
99         try {
100           String replacement = issuePattern.matcher(m.group(0)).replaceFirst(link.getLinkRegexp());
101           addMatch(result, new TextRange(m.start(), m.end()), replacement);
102         }
103         catch (Exception e) {
104           LOG.debug("Malformed regex replacement. IssueLink: " + link + "; text: " + text, e);
105         }
106       }
107     }
108     Matcher m = URLUtil.URL_PATTERN.matcher(text);
109     while (m.find()) {
110       addMatch(result, new TextRange(m.start(), m.end()), m.group());
111     }
112     Collections.sort(result);
113     return result;
114   }
115
116   private static void addMatch(final List<LinkMatch> result, final TextRange range, final String replacement) {
117     for (Iterator<LinkMatch> iterator = result.iterator(); iterator.hasNext(); ) {
118       LinkMatch oldMatch = iterator.next();
119       if (range.contains(oldMatch.getRange())) {
120         iterator.remove();
121       }
122       else if (oldMatch.getRange().contains(range)) {
123         return;
124       }
125     }
126     result.add(new LinkMatch(range, replacement));
127   }
128 }