don't cancel search on ProcessCanceledExceptions from too long regexp processing
[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.progress.ProcessCanceledException;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.SimpleModificationTracker;
27 import com.intellij.openapi.util.TextRange;
28 import com.intellij.util.io.URLUtil;
29 import com.intellij.util.xmlb.XmlSerializerUtil;
30
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37
38 /**
39  * @author yole
40  */
41 @State(name = "IssueNavigationConfiguration", storages = @Storage("vcs.xml"))
42 public class IssueNavigationConfiguration extends SimpleModificationTracker
43   implements PersistentStateComponent<IssueNavigationConfiguration> {
44   private static final Logger LOG = Logger.getInstance(IssueNavigationConfiguration.class);
45
46   public static IssueNavigationConfiguration getInstance(Project project) {
47     return PeriodicalTasksCloser.getInstance().safeGetService(project, IssueNavigationConfiguration.class);
48   }
49
50   private List<IssueNavigationLink> myLinks = new ArrayList<>();
51
52   public List<IssueNavigationLink> getLinks() {
53     return myLinks;
54   }
55
56   public void setLinks(final List<IssueNavigationLink> links) {
57     myLinks = new ArrayList<>(links);
58     incModificationCount();
59   }
60
61   public IssueNavigationConfiguration getState() {
62     return this;
63   }
64
65   public void loadState(IssueNavigationConfiguration state) {
66     XmlSerializerUtil.copyBean(state, this);
67   }
68
69   public static class LinkMatch implements Comparable {
70     private final TextRange myRange;
71     private final String myTargetUrl;
72
73     public LinkMatch(final TextRange range, final String targetUrl) {
74       myRange = range;
75       myTargetUrl = targetUrl;
76     }
77
78     public TextRange getRange() {
79       return myRange;
80     }
81
82     public String getTargetUrl() {
83       return myTargetUrl;
84     }
85
86     public int compareTo(Object o) {
87       if (!(o instanceof LinkMatch)) {
88         return 0;
89       }
90       return myRange.getStartOffset() - ((LinkMatch)o).getRange().getStartOffset();
91     }
92   }
93
94   public List<LinkMatch> findIssueLinks(CharSequence text) {
95     final List<LinkMatch> result = new ArrayList<>();
96     try {
97       for (IssueNavigationLink link : myLinks) {
98         Pattern issuePattern = link.getIssuePattern();
99         Matcher m = issuePattern.matcher(text);
100         while (m.find()) {
101           try {
102             String replacement = issuePattern.matcher(m.group(0)).replaceFirst(link.getLinkRegexp());
103             addMatch(result, new TextRange(m.start(), m.end()), replacement);
104           }
105           catch (Exception e) {
106             LOG.debug("Malformed regex replacement. IssueLink: " + link + "; text: " + text, e);
107           }
108         }
109       }
110       Matcher m = URLUtil.URL_PATTERN.matcher(text);
111       while (m.find()) {
112         addMatch(result, new TextRange(m.start(), m.end()), m.group());
113       }
114     }
115     catch (ProcessCanceledException e) {
116       //skip too long processing completely
117     }
118     Collections.sort(result);
119     return result;
120   }
121
122   private static void addMatch(final List<LinkMatch> result, final TextRange range, final String replacement) {
123     for (Iterator<LinkMatch> iterator = result.iterator(); iterator.hasNext(); ) {
124       LinkMatch oldMatch = iterator.next();
125       if (range.contains(oldMatch.getRange())) {
126         iterator.remove();
127       }
128       else if (oldMatch.getRange().contains(range)) {
129         return;
130       }
131     }
132     result.add(new LinkMatch(range, replacement));
133   }
134 }