filters: AbstractFileHyperlinkFilter as a convenient base class for file hyperlink...
authorSergey Simonchik <sergey.simonchik@jetbrains.com>
Fri, 16 Sep 2016 11:30:29 +0000 (14:30 +0300)
committerSergey Simonchik <sergey.simonchik@jetbrains.com>
Fri, 16 Sep 2016 11:30:29 +0000 (14:30 +0300)
platform/lang-impl/src/com/intellij/execution/filters/AbstractFileHyperlinkFilter.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/filters/FileHyperlinkParsedData.java
platform/lang-impl/src/com/intellij/execution/filters/FileHyperlinkRawData.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/filters/FileHyperlinkRawDataFinder.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/filters/PatternBasedFileHyperlinkFilter.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/filters/PatternBasedFileHyperlinkRawDataFinder.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/filters/PatternHyperlinkFormat.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/filters/PatternHyperlinkPart.java [new file with mode: 0644]

diff --git a/platform/lang-impl/src/com/intellij/execution/filters/AbstractFileHyperlinkFilter.java b/platform/lang-impl/src/com/intellij/execution/filters/AbstractFileHyperlinkFilter.java
new file mode 100644 (file)
index 0000000..910f9f3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.filters;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.List;
+
+public abstract class AbstractFileHyperlinkFilter implements Filter {
+  private static final Logger LOG = Logger.getInstance(AbstractFileHyperlinkFilter.class);
+
+  private final Project myProject;
+  private final ProjectFileIndex myFileIndex;
+  private final String myBaseDir;
+
+  public AbstractFileHyperlinkFilter(@NotNull Project project, @Nullable String baseDir) {
+    myProject = project;
+    myFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
+    myBaseDir = baseDir;
+  }
+
+  @Nullable
+  @Override
+  public final Result applyFilter(String line, int entireLength) {
+    List<FileHyperlinkRawData> links;
+    try {
+      links = parse(line);
+    }
+    catch (RuntimeException e) {
+      LOG.error("Failed to process '" + line + "'", e);
+      return null;
+    }
+    List<Filter.ResultItem> items = ContainerUtil.newArrayList();
+    for (FileHyperlinkRawData link : links) {
+      VirtualFile file = findFile(link.getFilePath());
+      if (file != null) {
+        OpenFileHyperlinkInfo info = new OpenFileHyperlinkInfo(myProject,
+                                                               file,
+                                                               link.getDocumentLine(),
+                                                               link.getDocumentColumn());
+        boolean grayedHyperLink = isGrayedHyperlink(file);
+        items.add(new Filter.ResultItem(link.getHyperlinkStartInd(), link.getHyperlinkEndInd(), info, grayedHyperLink));
+      }
+    }
+    return items.isEmpty() ? null : new Result(items);
+  }
+
+  private boolean isGrayedHyperlink(@NotNull VirtualFile file) {
+    return !myFileIndex.isInContent(file) || myFileIndex.isInLibrary(file);
+  }
+
+  @NotNull
+  public abstract List<FileHyperlinkRawData> parse(@NotNull String line);
+
+  @Nullable
+  public VirtualFile findFile(@NotNull String filePath) {
+    File file = findIoFile(filePath);
+    if (file != null) {
+      return LocalFileSystem.getInstance().findFileByIoFile(file);
+    }
+    return null;
+  }
+
+  @Nullable
+  private File findIoFile(@NotNull String filePath) {
+    File file = new File(filePath);
+    if (file.isFile() && file.isAbsolute()) {
+      return file;
+    }
+    if (myBaseDir != null) {
+      file = new File(myBaseDir, filePath);
+      if (file.isFile()) {
+        return file;
+      }
+    }
+    return null;
+  }
+}
index bab166e77b792ed5382df276fcae2f2e32f6af35..45a28537123245f63ac31c6a5e42f9753ba5c5d9 100644 (file)
@@ -2,6 +2,9 @@ package com.intellij.execution.filters;
 
 import org.jetbrains.annotations.NotNull;
 
+/**
+ * @deprecated use {@link FileHyperlinkRawData} instead
+ */
 public class FileHyperlinkParsedData {
 
   private final String myFilePath;
diff --git a/platform/lang-impl/src/com/intellij/execution/filters/FileHyperlinkRawData.java b/platform/lang-impl/src/com/intellij/execution/filters/FileHyperlinkRawData.java
new file mode 100644 (file)
index 0000000..f6ffc64
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.filters;
+
+import org.jetbrains.annotations.NotNull;
+
+public class FileHyperlinkRawData {
+
+  private final String myFilePath;
+  private final int myDocumentLine;
+  private final int myDocumentColumn;
+  private final int myHyperlinkStartInd;
+  private final int myHyperlinkEndInd;
+
+  public FileHyperlinkRawData(@NotNull String filePath,
+                              int documentLine,
+                              int documentColumn,
+                              int hyperlinkStartInd,
+                              int hyperlinkEndInd) {
+    myFilePath = filePath;
+    myDocumentLine = documentLine;
+    myDocumentColumn = documentColumn;
+    myHyperlinkStartInd = hyperlinkStartInd;
+    myHyperlinkEndInd = hyperlinkEndInd;
+  }
+
+  @NotNull
+  public String getFilePath() {
+    return myFilePath;
+  }
+
+  public int getDocumentLine() {
+    return myDocumentLine;
+  }
+
+  public int getDocumentColumn() {
+    return myDocumentColumn;
+  }
+
+  public int getHyperlinkStartInd() {
+    return myHyperlinkStartInd;
+  }
+
+  public int getHyperlinkEndInd() {
+    return myHyperlinkEndInd;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    FileHyperlinkRawData data = (FileHyperlinkRawData)o;
+
+    return myDocumentLine == data.myDocumentLine &&
+           myDocumentColumn == data.myDocumentColumn &&
+           myHyperlinkStartInd == data.myHyperlinkStartInd &&
+           myHyperlinkEndInd == data.myHyperlinkEndInd &&
+           myFilePath.equals(data.myFilePath);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = myFilePath.hashCode();
+    result = 31 * result + myDocumentLine;
+    result = 31 * result + myDocumentColumn;
+    result = 31 * result + myHyperlinkStartInd;
+    result = 31 * result + myHyperlinkEndInd;
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "filePath=" + myFilePath +
+           ", line=" + myDocumentLine +
+           ", column=" + myDocumentColumn +
+           ", hyperlinkStartOffset=" + myHyperlinkStartInd +
+           ", hyperlinkEndOffset=" + myHyperlinkEndInd;
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/execution/filters/FileHyperlinkRawDataFinder.java b/platform/lang-impl/src/com/intellij/execution/filters/FileHyperlinkRawDataFinder.java
new file mode 100644 (file)
index 0000000..c0fcf34
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.filters;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public interface FileHyperlinkRawDataFinder {
+  @NotNull
+  List<FileHyperlinkRawData> find(@NotNull String line);
+}
diff --git a/platform/lang-impl/src/com/intellij/execution/filters/PatternBasedFileHyperlinkFilter.java b/platform/lang-impl/src/com/intellij/execution/filters/PatternBasedFileHyperlinkFilter.java
new file mode 100644 (file)
index 0000000..ddd3a31
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.filters;
+
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class PatternBasedFileHyperlinkFilter extends AbstractFileHyperlinkFilter implements DumbAware {
+
+  private final PatternBasedFileHyperlinkRawDataFinder myFinder;
+
+  public PatternBasedFileHyperlinkFilter(@NotNull Project project,
+                                         @Nullable String baseDir,
+                                         @NotNull PatternBasedFileHyperlinkRawDataFinder finder) {
+    super(project, baseDir);
+    myFinder = finder;
+  }
+
+  @NotNull
+  @Override
+  public List<FileHyperlinkRawData> parse(@NotNull String line) {
+    return myFinder.find(line);
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/execution/filters/PatternBasedFileHyperlinkRawDataFinder.java b/platform/lang-impl/src/com/intellij/execution/filters/PatternBasedFileHyperlinkRawDataFinder.java
new file mode 100644 (file)
index 0000000..5724708
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.filters;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+
+public class PatternBasedFileHyperlinkRawDataFinder implements FileHyperlinkRawDataFinder {
+  private static final int UNKNOWN = -2;
+
+  private final PatternHyperlinkFormat[] myLinkFormats;
+
+  public PatternBasedFileHyperlinkRawDataFinder(@NotNull PatternHyperlinkFormat[] linkFormats) {
+    myLinkFormats = linkFormats;
+  }
+
+  @NotNull
+  @Override
+  public List<FileHyperlinkRawData> find(@NotNull String line) {
+    Pair<Matcher, PatternHyperlinkFormat> pair = findMatcher(line);
+    if (pair == null) {
+      return Collections.emptyList();
+    }
+    Matcher matcher = pair.first;
+    PatternHyperlinkFormat format = pair.second;
+    PatternHyperlinkPart[] linkParts = format.getLinkParts();
+    int groupCount = matcher.groupCount();
+    if (groupCount > linkParts.length) {
+      return Collections.emptyList();
+    }
+    String path = null;
+    int lineNumber = -1, columnNumber = -1;
+    int hyperlinkStartInd = -1, hyperlinkEndInd = -1;
+    boolean hyperlinkFreezed = false;
+    for (int i = 1; i <= groupCount; i++) {
+      String value = matcher.group(i);
+      PatternHyperlinkPart part = linkParts[i - 1];
+      if (part == PatternHyperlinkPart.HYPERLINK) {
+        hyperlinkStartInd = matcher.start(i);
+        hyperlinkEndInd = matcher.end(i);
+        hyperlinkFreezed = true;
+      }
+      else if (part == PatternHyperlinkPart.PATH) {
+        path = value;
+        if (!hyperlinkFreezed) {
+          hyperlinkStartInd = matcher.start(i);
+          hyperlinkEndInd = matcher.end(i);
+        }
+      }
+      else if (part == PatternHyperlinkPart.LINE) {
+        value = StringUtil.trimStart(value, ":");
+        lineNumber = StringUtil.parseInt(value, UNKNOWN);
+        hyperlinkEndInd = tryExtendHyperlinkEnd(hyperlinkFreezed, hyperlinkEndInd, matcher.start(i), matcher.end(i));
+      }
+      else if (part == PatternHyperlinkPart.COLUMN) {
+        value = StringUtil.trimStart(value, ":");
+        columnNumber = StringUtil.parseInt(value, UNKNOWN);
+        hyperlinkEndInd = tryExtendHyperlinkEnd(hyperlinkFreezed, hyperlinkEndInd, matcher.start(i), matcher.end(i));
+      }
+    }
+    if (path == null || lineNumber == UNKNOWN || columnNumber == UNKNOWN || hyperlinkStartInd == -1) {
+      return Collections.emptyList();
+    }
+    if (!format.isZeroBasedLine()) {
+      lineNumber--;
+    }
+    if (!format.isZeroBasedColumn()) {
+      columnNumber--;
+    }
+    FileHyperlinkRawData data = new FileHyperlinkRawData(path, lineNumber, columnNumber,
+                                                         hyperlinkStartInd, hyperlinkEndInd);
+    return Collections.singletonList(data);
+  }
+
+  private static int tryExtendHyperlinkEnd(boolean hyperlinkFreezed, int hyperlinkEndInd,
+                                           int groupStartInd, int groupEndInd) {
+    if (!hyperlinkFreezed && (hyperlinkEndInd == groupStartInd || hyperlinkEndInd + 1 == groupStartInd)) {
+      return groupEndInd;
+    }
+    return hyperlinkEndInd;
+  }
+
+  @Nullable
+  private Pair<Matcher, PatternHyperlinkFormat> findMatcher(@NotNull String line) {
+    for (PatternHyperlinkFormat linkFormat : myLinkFormats) {
+      Matcher matcher = linkFormat.getPattern().matcher(line);
+      if (matcher.matches()) {
+        return Pair.create(matcher, linkFormat);
+      }
+    }
+    return null;
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/execution/filters/PatternHyperlinkFormat.java b/platform/lang-impl/src/com/intellij/execution/filters/PatternHyperlinkFormat.java
new file mode 100644 (file)
index 0000000..63713fb
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.filters;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.regex.Pattern;
+
+public class PatternHyperlinkFormat {
+  private final Pattern myPattern;
+  private final boolean myZeroBasedLine;
+  private final boolean myZeroBasedColumn;
+  private final PatternHyperlinkPart[] myLinkParts;
+
+  public PatternHyperlinkFormat(@NotNull Pattern pattern,
+                                boolean zeroBasedLine,
+                                boolean zeroBasedColumn,
+                                @NotNull PatternHyperlinkPart... linkParts) {
+    myPattern = pattern;
+    myZeroBasedLine = zeroBasedLine;
+    myZeroBasedColumn = zeroBasedColumn;
+    myLinkParts = linkParts;
+  }
+
+  @NotNull
+  public Pattern getPattern() {
+    return myPattern;
+  }
+
+  public boolean isZeroBasedLine() {
+    return myZeroBasedLine;
+  }
+
+  public boolean isZeroBasedColumn() {
+    return myZeroBasedColumn;
+  }
+
+  @NotNull
+  public PatternHyperlinkPart[] getLinkParts() {
+    return myLinkParts;
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/execution/filters/PatternHyperlinkPart.java b/platform/lang-impl/src/com/intellij/execution/filters/PatternHyperlinkPart.java
new file mode 100644 (file)
index 0000000..a305c4a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.filters;
+
+public enum PatternHyperlinkPart {
+  HYPERLINK, PATH, LINE, COLUMN
+}