mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'master' of git.labs.intellij.net:idea/community
This commit is contained in:
@@ -15,33 +15,21 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.javadoc;
|
||||
|
||||
import com.intellij.codeInsight.documentation.AbstractExternalFilter;
|
||||
import com.intellij.codeInsight.documentation.DocumentationManager;
|
||||
import com.intellij.codeInsight.documentation.PlatformDocumentationUtil;
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.io.UrlConnectionUtil;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -53,85 +41,19 @@ import java.util.regex.Pattern;
|
||||
* To change this template use Options | File Templates.
|
||||
*/
|
||||
|
||||
public class JavaDocExternalFilter {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.javadoc.JavaDocExternalFilter");
|
||||
|
||||
public class JavaDocExternalFilter extends AbstractExternalFilter {
|
||||
private final Project myProject;
|
||||
private final PsiManager myManager;
|
||||
|
||||
protected static @NonNls final Pattern ourHTMLsuffix = Pattern.compile("[.][hH][tT][mM][lL]?");
|
||||
protected static @NonNls final Pattern ourParentFolderprefix = Pattern.compile("^[.][.]/");
|
||||
protected static @NonNls final Pattern ourAnchorsuffix = Pattern.compile("#(.*)$");
|
||||
protected static @NonNls final Pattern ourHTMLFilesuffix = Pattern.compile("/([^/]*[.][hH][tT][mM][lL]?)$");
|
||||
private static @NonNls final Pattern ourHREFselector = Pattern.compile("<A.*?HREF=\"([^>\"]*)\"", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
|
||||
private static @NonNls final Pattern ourAnnihilator = Pattern.compile("/[^/^.]*/[.][.]/");
|
||||
private static @NonNls final Pattern ourIMGselector = Pattern.compile("<IMG[ \\t\\n\\r\\f]+SRC=\"([^>]*)\"", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
|
||||
private static @NonNls final Pattern ourMethodHeading = Pattern.compile("<H3>(.+?)</H3>", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
|
||||
protected static @NonNls final String DOC_ELEMENT_PROTOCOL = "doc_element://";
|
||||
protected static @NonNls final String PSI_ELEMENT_PROTOCOL = "psi_element://";
|
||||
private static @NonNls final String JAR_PROTOCOL = "jar:";
|
||||
private static @NonNls final String DOC_START_MARKER = "Generated by javadoc";
|
||||
@NonNls private static final String HR = "<HR>";
|
||||
@NonNls private static final String P = "<P>";
|
||||
@NonNls private static final String DL = "<DL>";
|
||||
@NonNls protected static final String H2 = "</H2>";
|
||||
@NonNls protected static final String HTML_CLOSE = "</HTML>";
|
||||
@NonNls protected static final String HTML = "<HTML>";
|
||||
@NonNls private static final String BR = "<BR>";
|
||||
@NonNls private static final String DT = "<DT>";
|
||||
|
||||
protected static abstract class RefConvertor {
|
||||
private final Pattern mySelector;
|
||||
|
||||
public RefConvertor(Pattern selector) {
|
||||
mySelector = selector;
|
||||
}
|
||||
|
||||
protected abstract String convertReference(String root, String href);
|
||||
|
||||
public String refFilter(final String root, String read) {
|
||||
String toMatch = read.toUpperCase();
|
||||
StringBuffer ready = new StringBuffer();
|
||||
int prev = 0;
|
||||
Matcher matcher = mySelector.matcher(toMatch);
|
||||
|
||||
while (matcher.find()) {
|
||||
String before = read.substring(prev, matcher.start(1) - 1); // Before reference
|
||||
final String href = read.substring(matcher.start(1), matcher.end(1)); // The URL
|
||||
prev = matcher.end(1) + 1;
|
||||
ready.append(before);
|
||||
ready.append("\"");
|
||||
ready.append(ApplicationManager.getApplication().runReadAction(
|
||||
new Computable<String>() {
|
||||
public String compute() {
|
||||
return convertReference(root, href);
|
||||
}
|
||||
}
|
||||
));
|
||||
ready.append("\"");
|
||||
}
|
||||
|
||||
ready.append(read.substring(prev, read.length()));
|
||||
|
||||
return ready.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected final RefConvertor myIMGConvertor = new RefConvertor(ourIMGselector) {
|
||||
protected String convertReference(String root, String href) {
|
||||
if (StringUtil.startsWithChar(href, '#')) {
|
||||
return DOC_ELEMENT_PROTOCOL + root + href;
|
||||
}
|
||||
|
||||
if (Comparing.strEqual(VirtualFileManager.extractProtocol(root), LocalFileSystem.PROTOCOL)) {
|
||||
final String path = VirtualFileManager.extractPath(root);
|
||||
if (!path.startsWith("/")) {//skip host for local file system files (format - file://host_name/path)
|
||||
root = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, "/" + path);
|
||||
}
|
||||
}
|
||||
return ourHTMLFilesuffix.matcher(root).replaceAll("/") + href;
|
||||
}
|
||||
};
|
||||
|
||||
private final RefConvertor[] myReferenceConvertors = new RefConvertor[]{
|
||||
new RefConvertor(ourHREFselector) {
|
||||
@@ -155,7 +77,7 @@ public class JavaDocExternalFilter {
|
||||
final String classRef = ourAnchorsuffix.matcher(elementRef).replaceAll("");
|
||||
|
||||
return
|
||||
(JavaPsiFacade.getInstance(myManager.getProject()).findClass(classRef, GlobalSearchScope.allScope(myProject)) != null)
|
||||
(JavaPsiFacade.getInstance(myProject).findClass(classRef, GlobalSearchScope.allScope(myProject)) != null)
|
||||
? PSI_ELEMENT_PROTOCOL + elementRef
|
||||
: DOC_ELEMENT_PROTOCOL + doAnnihilate(nakedRoot + href);
|
||||
}
|
||||
@@ -166,112 +88,9 @@ public class JavaDocExternalFilter {
|
||||
|
||||
public JavaDocExternalFilter(Project project) {
|
||||
myProject = project;
|
||||
myManager = PsiManager.getInstance(myProject);
|
||||
}
|
||||
|
||||
protected static String doAnnihilate(String path) {
|
||||
int len = path.length();
|
||||
|
||||
do {
|
||||
path = ourAnnihilator.matcher(path).replaceAll("/");
|
||||
}
|
||||
while (len > (len = path.length()));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private interface Waiter{
|
||||
void sayYes();
|
||||
|
||||
boolean runMe();
|
||||
}
|
||||
|
||||
public static boolean isJavaDocURL(final String url) throws Exception {
|
||||
final Waiter waiter = new Waiter(){
|
||||
Boolean key = Boolean.FALSE;
|
||||
final Object LOCK = new Object();
|
||||
|
||||
public void sayYes(){
|
||||
key = Boolean.TRUE;
|
||||
synchronized (LOCK) {
|
||||
LOCK.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean runMe(){
|
||||
try {
|
||||
synchronized (LOCK) {
|
||||
LOCK.wait(600);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return key.booleanValue();
|
||||
}
|
||||
};
|
||||
|
||||
final boolean[] fail = new boolean[1];
|
||||
final Exception [] ex = new Exception[1];
|
||||
final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
|
||||
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
|
||||
public void run() {
|
||||
Reader stream = null;
|
||||
try {
|
||||
stream = getReaderByUrl(url, httpConfigurable, ProgressManager.getInstance().getProgressIndicator());
|
||||
}
|
||||
catch (IOException e) {
|
||||
ex[0] = e;
|
||||
}
|
||||
if (stream == null) {
|
||||
fail[0] = true;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(stream);
|
||||
int lookUp = 6;
|
||||
|
||||
while (lookUp > 0) {
|
||||
if (reader.readLine().indexOf(DOC_START_MARKER) != -1) {
|
||||
waiter.sayYes();
|
||||
}
|
||||
|
||||
lookUp--;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (final Exception e) {
|
||||
ex[0] = e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (ex[0] != null) {
|
||||
throw ex[0];
|
||||
}
|
||||
return !fail[0] && waiter.runMe();
|
||||
}
|
||||
|
||||
private String correctRefs(String root, String read) {
|
||||
String result = read;
|
||||
|
||||
for (RefConvertor myReferenceConvertor : getRefConvertors()) {
|
||||
result = myReferenceConvertor.refFilter(root, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RefConvertor[] getRefConvertors() {
|
||||
return myReferenceConvertors;
|
||||
}
|
||||
@@ -281,74 +100,13 @@ public class JavaDocExternalFilter {
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
text = JavaDocUtil.fixupText(text);
|
||||
|
||||
text = PlatformDocumentationUtil.fixupText(text);
|
||||
return text;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static Reader getReaderByUrl(final String surl, final HttpConfigurable httpConfigurable, final ProgressIndicator pi) throws IOException {
|
||||
if (surl.startsWith(JAR_PROTOCOL)) {
|
||||
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(BrowserUtil.getDocURL(surl));
|
||||
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new StringReader(VfsUtil.loadText(file));
|
||||
}
|
||||
|
||||
URL url = BrowserUtil.getURL(surl);
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
httpConfigurable.prepareURL(url.toString());
|
||||
final URLConnection urlConnection = url.openConnection();
|
||||
final String contentEncoding = urlConnection.getContentEncoding();
|
||||
final InputStream inputStream =
|
||||
pi != null ? UrlConnectionUtil.getConnectionInputStreamWithException(urlConnection, pi) : urlConnection.getInputStream();
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
return contentEncoding != null ? new InputStreamReader(inputStream, contentEncoding) : new InputStreamReader(inputStream);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"HardCodedStringLiteral"})
|
||||
public String getExternalDocInfo(final String surl) throws Exception {
|
||||
if (surl == null) return null;
|
||||
if (MyJavadocFetcher.isFree()) {
|
||||
final MyJavadocFetcher fetcher = new MyJavadocFetcher(surl, new MyDocBuilder() {
|
||||
public void buildFromStream(String surl, Reader input, StringBuffer result) throws IOException {
|
||||
doBuildFromStream(surl, input, result);
|
||||
}
|
||||
});
|
||||
final Future<?> fetcherFuture = ApplicationManager.getApplication().executeOnPooledThread(fetcher);
|
||||
try {
|
||||
fetcherFuture.get();
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
final Exception exception = fetcher.getException();
|
||||
if (exception != null) {
|
||||
fetcher.cleanup();
|
||||
throw exception;
|
||||
}
|
||||
|
||||
final String docText = correctRefs(ourAnchorsuffix.matcher(surl).replaceAll(""), fetcher.getData());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Filtered JavaDoc: " + docText + "\n");
|
||||
}
|
||||
return JavaDocUtil.fixupText(docText);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getExternalDocInfoForElement(final String docURL, final PsiElement element) throws Exception {
|
||||
String externalDoc = getExternalDocInfo(docURL);
|
||||
String externalDoc = super.getExternalDocInfoForElement(docURL, element);
|
||||
if (externalDoc != null) {
|
||||
if (element instanceof PsiMethod) {
|
||||
final String className = ApplicationManager.getApplication().runReadAction(
|
||||
@@ -368,154 +126,4 @@ public class JavaDocExternalFilter {
|
||||
return externalDoc;
|
||||
}
|
||||
|
||||
protected void doBuildFromStream(String surl, Reader input, StringBuffer data) throws IOException {
|
||||
final BufferedReader buf = new BufferedReader(input);
|
||||
Matcher anchorMatcher = ourAnchorsuffix.matcher(surl);
|
||||
@NonNls String startSection = "<!-- ======== START OF CLASS DATA ======== -->";
|
||||
@NonNls String endSection = "SUMMARY ========";
|
||||
@NonNls String greatestEndSection = "<!-- ========= END OF CLASS DATA ========= -->";
|
||||
boolean isClassDoc = true;
|
||||
|
||||
if (anchorMatcher.find()) {
|
||||
isClassDoc = false;
|
||||
startSection = "<A NAME=\"" + anchorMatcher.group(1).toUpperCase() + "\"";
|
||||
endSection = "<A NAME=";
|
||||
}
|
||||
|
||||
data.append(HTML);
|
||||
|
||||
String read;
|
||||
|
||||
do {
|
||||
read = buf.readLine();
|
||||
}
|
||||
while (read != null && read.toUpperCase().indexOf(startSection) == -1);
|
||||
|
||||
|
||||
if (read == null) {
|
||||
data.delete(0, data.length());
|
||||
return;
|
||||
}
|
||||
|
||||
appendLine(data, read);
|
||||
|
||||
if (isClassDoc) {
|
||||
boolean skip = false;
|
||||
|
||||
while (((read = buf.readLine()) != null) && !read.toUpperCase().trim().equals(DL)) {
|
||||
if (read.toUpperCase().indexOf(H2) != -1) { // read=class name in <H2>
|
||||
data.append(H2);
|
||||
skip = true;
|
||||
}
|
||||
else if (!skip) {
|
||||
appendLine(data, read);
|
||||
}
|
||||
}
|
||||
|
||||
data.append(DL);
|
||||
|
||||
StringBuffer classDetails = new StringBuffer();
|
||||
|
||||
while (((read = buf.readLine()) != null) && !read.toUpperCase().equals(HR) && !read.toUpperCase().equals(P)) {
|
||||
appendLine(classDetails, read);
|
||||
}
|
||||
|
||||
while (((read = buf.readLine()) != null) && !read.toUpperCase().equals(P) && !read.toUpperCase().equals(HR)) {
|
||||
appendLine(data, read.replaceAll(DT, DT + BR));
|
||||
}
|
||||
|
||||
data.append(classDetails);
|
||||
data.append(P);
|
||||
}
|
||||
|
||||
while (((read = buf.readLine()) != null) && read.indexOf(endSection) == -1 && read.indexOf(greatestEndSection) == -1) {
|
||||
if (read.toUpperCase().indexOf(HR) == -1) {
|
||||
appendLine(data, read);
|
||||
}
|
||||
}
|
||||
|
||||
data.append(HTML_CLOSE);
|
||||
}
|
||||
|
||||
private static void appendLine(final StringBuffer buffer, final String read) {
|
||||
buffer.append(read);
|
||||
buffer.append("\n");
|
||||
}
|
||||
|
||||
private interface MyDocBuilder {
|
||||
void buildFromStream(String surl, Reader input, StringBuffer result) throws IOException;
|
||||
}
|
||||
|
||||
private static class MyJavadocFetcher implements Runnable {
|
||||
private static boolean ourFree = true;
|
||||
private final StringBuffer data = new StringBuffer();
|
||||
private final String surl;
|
||||
private final MyDocBuilder myBuilder;
|
||||
private final Exception [] myExceptions = new Exception[1];
|
||||
private final HttpConfigurable myHttpConfigurable;
|
||||
|
||||
public MyJavadocFetcher(final String surl, MyDocBuilder builder) {
|
||||
this.surl = surl;
|
||||
myBuilder = builder;
|
||||
ourFree = false;
|
||||
myHttpConfigurable = HttpConfigurable.getInstance();
|
||||
}
|
||||
|
||||
public static boolean isFree() {
|
||||
return ourFree;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
if (surl == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reader stream = null;
|
||||
try {
|
||||
stream = getReaderByUrl(surl, myHttpConfigurable, new ProgressIndicatorBase());
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
return;
|
||||
}
|
||||
catch (IOException e) {
|
||||
myExceptions[0] = e;
|
||||
}
|
||||
|
||||
if (stream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
myBuilder.buildFromStream(surl, stream, data);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
myExceptions[0] = e;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
stream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
myExceptions[0] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
ourFree = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return myExceptions[0];
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
myExceptions[0] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,19 +27,14 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
public class JavaDocUtil {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.javadoc.JavaDocUtil");
|
||||
|
||||
private static final @NonNls Pattern ourTypePattern = Pattern.compile("[ ]+[^ ^\\[^\\]]");
|
||||
private static final @NonNls Pattern ourLtFixupPattern = Pattern.compile("<([^/^\\w^!])");
|
||||
private static final @NonNls Pattern ourToQuote = Pattern.compile("[\\\\\\.\\^\\$\\?\\*\\+\\|\\)\\}\\]\\{\\(\\[]");
|
||||
private static final @NonNls String LT_ENTITY = "<";
|
||||
|
||||
private JavaDocUtil() {
|
||||
}
|
||||
@@ -350,41 +345,6 @@ public class JavaDocUtil {
|
||||
return memberText.substring(0, parenthIndex + 1) + buffer.toString() + ")";
|
||||
}
|
||||
|
||||
private static String quote(String x) {
|
||||
if (ourToQuote.matcher(x).find()) {
|
||||
return "\\" + x;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
public static String fixupText(String docText) {
|
||||
Matcher fixupMatcher = ourLtFixupPattern.matcher(docText);
|
||||
LinkedList<String> secondSymbols = new LinkedList<String>();
|
||||
|
||||
while (fixupMatcher.find()) {
|
||||
String s = fixupMatcher.group(1);
|
||||
|
||||
//[db] that's workaround to avoid internal bug
|
||||
if (!s.equals("\\") && !secondSymbols.contains(s)) {
|
||||
secondSymbols.addFirst(s);
|
||||
}
|
||||
}
|
||||
|
||||
for (String s : secondSymbols) {
|
||||
String pattern = "<" + quote(s);
|
||||
|
||||
try {
|
||||
docText = Pattern.compile(pattern).matcher(docText).replaceAll(LT_ENTITY + pattern);
|
||||
}
|
||||
catch (PatternSyntaxException e) {
|
||||
LOG.error("Pattern syntax exception on " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
return docText;
|
||||
}
|
||||
|
||||
public static PsiClassType[] getImplementsList(PsiClass aClass) {
|
||||
if (aClass instanceof PsiAnonymousClass) {
|
||||
return new PsiClassType[]{((PsiAnonymousClass)aClass).getBaseClassType()};
|
||||
@@ -405,4 +365,4 @@ public class JavaDocUtil {
|
||||
return list == null ? PsiClassType.EMPTY_ARRAY : list.getReferencedTypes();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.intellij.lang.java;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.documentation.PlatformDocumentationUtil;
|
||||
import com.intellij.codeInsight.editorActions.CodeDocumentationUtil;
|
||||
import com.intellij.codeInsight.javadoc.JavaDocExternalFilter;
|
||||
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator;
|
||||
@@ -36,9 +37,7 @@ import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem;
|
||||
import com.intellij.openapi.vfs.ex.http.HttpFileSystem;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.beanProperties.BeanPropertyElement;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
|
||||
@@ -639,7 +638,7 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
|
||||
}
|
||||
if (module != null) {
|
||||
String[] javadocPaths = ModuleRootManager.getInstance(module).getRootUrls(JavadocOrderRootType.getInstance());
|
||||
final List<String> httpRoots = getHttpRoots(javadocPaths, relPath);
|
||||
final List<String> httpRoots = PlatformDocumentationUtil.getHttpRoots(javadocPaths, relPath);
|
||||
// if found nothing and the file is from library classes, fall back to order entries
|
||||
if (httpRoots != null || !fileIndex.isInLibraryClasses(virtualFile)) {
|
||||
return httpRoots;
|
||||
@@ -649,33 +648,12 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
|
||||
final List<OrderEntry> orderEntries = fileIndex.getOrderEntriesForFile(virtualFile);
|
||||
for (OrderEntry orderEntry : orderEntries) {
|
||||
final String[] files = orderEntry.getUrls(JavadocOrderRootType.getInstance());
|
||||
final List<String> httpRoot = getHttpRoots(files, relPath);
|
||||
final List<String> httpRoot = PlatformDocumentationUtil.getHttpRoots(files, relPath);
|
||||
if (httpRoot != null) return httpRoot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static List<String> getHttpRoots(final String[] roots, String relPath) {
|
||||
final ArrayList<String> result = new ArrayList<String>();
|
||||
for (String root : roots) {
|
||||
final VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(root);
|
||||
if (virtualFile != null) {
|
||||
if (virtualFile.getFileSystem() instanceof HttpFileSystem) {
|
||||
String url = virtualFile.getUrl();
|
||||
if (!url.endsWith("/")) url += "/";
|
||||
result.add(url + relPath);
|
||||
}
|
||||
else {
|
||||
VirtualFile file = virtualFile.findFileByRelativePath(relPath);
|
||||
if (file != null) result.add(file.getUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.isEmpty() ? null : result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static List<String> findUrlForPackage(PsiPackage aPackage) {
|
||||
String qName = aPackage.getQualifiedName();
|
||||
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.codeInsight.documentation;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.io.UrlConnectionUtil;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: db
|
||||
* Date: May 2, 2003
|
||||
* Time: 8:35:34 PM
|
||||
* To change this template use Options | File Templates.
|
||||
*/
|
||||
|
||||
public abstract class AbstractExternalFilter {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.javadoc.JavaDocExternalFilter");
|
||||
|
||||
protected static @NonNls final Pattern ourAnchorsuffix = Pattern.compile("#(.*)$");
|
||||
protected static @NonNls final Pattern ourHTMLFilesuffix = Pattern.compile("/([^/]*[.][hH][tT][mM][lL]?)$");
|
||||
private static @NonNls final Pattern ourAnnihilator = Pattern.compile("/[^/^.]*/[.][.]/");
|
||||
private static @NonNls final Pattern ourIMGselector = Pattern.compile("<IMG[ \\t\\n\\r\\f]+SRC=\"([^>]*)\"", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
|
||||
protected static @NonNls final String DOC_ELEMENT_PROTOCOL = "doc_element://";
|
||||
protected static @NonNls final String PSI_ELEMENT_PROTOCOL = "psi_element://";
|
||||
private static @NonNls final String JAR_PROTOCOL = "jar:";
|
||||
@NonNls private static final String HR = "<HR>";
|
||||
@NonNls private static final String P = "<P>";
|
||||
@NonNls private static final String DL = "<DL>";
|
||||
@NonNls protected static final String H2 = "</H2>";
|
||||
@NonNls protected static final String HTML_CLOSE = "</HTML>";
|
||||
@NonNls protected static final String HTML = "<HTML>";
|
||||
@NonNls private static final String BR = "<BR>";
|
||||
@NonNls private static final String DT = "<DT>";
|
||||
|
||||
protected static abstract class RefConvertor {
|
||||
private final Pattern mySelector;
|
||||
|
||||
public RefConvertor(Pattern selector) {
|
||||
mySelector = selector;
|
||||
}
|
||||
|
||||
protected abstract String convertReference(String root, String href);
|
||||
|
||||
public String refFilter(final String root, String read) {
|
||||
String toMatch = read.toUpperCase();
|
||||
StringBuffer ready = new StringBuffer();
|
||||
int prev = 0;
|
||||
Matcher matcher = mySelector.matcher(toMatch);
|
||||
|
||||
while (matcher.find()) {
|
||||
String before = read.substring(prev, matcher.start(1) - 1); // Before reference
|
||||
final String href = read.substring(matcher.start(1), matcher.end(1)); // The URL
|
||||
prev = matcher.end(1) + 1;
|
||||
ready.append(before);
|
||||
ready.append("\"");
|
||||
ready.append(ApplicationManager.getApplication().runReadAction(
|
||||
new Computable<String>() {
|
||||
public String compute() {
|
||||
return convertReference(root, href);
|
||||
}
|
||||
}
|
||||
));
|
||||
ready.append("\"");
|
||||
}
|
||||
|
||||
ready.append(read.substring(prev, read.length()));
|
||||
|
||||
return ready.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected final RefConvertor myIMGConvertor = new RefConvertor(ourIMGselector) {
|
||||
protected String convertReference(String root, String href) {
|
||||
if (StringUtil.startsWithChar(href, '#')) {
|
||||
return DOC_ELEMENT_PROTOCOL + root + href;
|
||||
}
|
||||
|
||||
if (Comparing.strEqual(VirtualFileManager.extractProtocol(root), LocalFileSystem.PROTOCOL)) {
|
||||
final String path = VirtualFileManager.extractPath(root);
|
||||
if (!path.startsWith("/")) {//skip host for local file system files (format - file://host_name/path)
|
||||
root = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, "/" + path);
|
||||
}
|
||||
}
|
||||
return ourHTMLFilesuffix.matcher(root).replaceAll("/") + href;
|
||||
}
|
||||
};
|
||||
|
||||
protected static String doAnnihilate(String path) {
|
||||
int len = path.length();
|
||||
|
||||
do {
|
||||
path = ourAnnihilator.matcher(path).replaceAll("/");
|
||||
}
|
||||
while (len > (len = path.length()));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private String correctRefs(String root, String read) {
|
||||
String result = read;
|
||||
|
||||
for (RefConvertor myReferenceConvertor : getRefConvertors()) {
|
||||
result = myReferenceConvertor.refFilter(root, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract RefConvertor[] getRefConvertors();
|
||||
|
||||
@Nullable
|
||||
private static Reader getReaderByUrl(final String surl, final HttpConfigurable httpConfigurable, final ProgressIndicator pi) throws IOException {
|
||||
if (surl.startsWith(JAR_PROTOCOL)) {
|
||||
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(BrowserUtil.getDocURL(surl));
|
||||
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new StringReader(VfsUtil.loadText(file));
|
||||
}
|
||||
|
||||
URL url = BrowserUtil.getURL(surl);
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
httpConfigurable.prepareURL(url.toString());
|
||||
final URLConnection urlConnection = url.openConnection();
|
||||
final String contentEncoding = urlConnection.getContentEncoding();
|
||||
final InputStream inputStream =
|
||||
pi != null ? UrlConnectionUtil.getConnectionInputStreamWithException(urlConnection, pi) : urlConnection.getInputStream();
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
return contentEncoding != null ? new InputStreamReader(inputStream, contentEncoding) : new InputStreamReader(inputStream);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"HardCodedStringLiteral"})
|
||||
public String getExternalDocInfo(final String surl) throws Exception {
|
||||
if (surl == null) return null;
|
||||
if (MyJavadocFetcher.isFree()) {
|
||||
final MyJavadocFetcher fetcher = new MyJavadocFetcher(surl, new MyDocBuilder() {
|
||||
public void buildFromStream(String surl, Reader input, StringBuffer result) throws IOException {
|
||||
doBuildFromStream(surl, input, result);
|
||||
}
|
||||
});
|
||||
final Future<?> fetcherFuture = ApplicationManager.getApplication().executeOnPooledThread(fetcher);
|
||||
try {
|
||||
fetcherFuture.get();
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
final Exception exception = fetcher.getException();
|
||||
if (exception != null) {
|
||||
fetcher.cleanup();
|
||||
throw exception;
|
||||
}
|
||||
|
||||
final String docText = correctRefs(ourAnchorsuffix.matcher(surl).replaceAll(""), fetcher.getData());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Filtered JavaDoc: " + docText + "\n");
|
||||
}
|
||||
return PlatformDocumentationUtil.fixupText(docText);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getExternalDocInfoForElement(final String docURL, final PsiElement element) throws Exception {
|
||||
return getExternalDocInfo(docURL);
|
||||
}
|
||||
|
||||
protected void doBuildFromStream(String surl, Reader input, StringBuffer data) throws IOException {
|
||||
final BufferedReader buf = new BufferedReader(input);
|
||||
Matcher anchorMatcher = ourAnchorsuffix.matcher(surl);
|
||||
@NonNls String startSection = "<!-- ======== START OF CLASS DATA ======== -->";
|
||||
@NonNls String endSection = "SUMMARY ========";
|
||||
@NonNls String greatestEndSection = "<!-- ========= END OF CLASS DATA ========= -->";
|
||||
boolean isClassDoc = true;
|
||||
|
||||
if (anchorMatcher.find()) {
|
||||
isClassDoc = false;
|
||||
startSection = "<A NAME=\"" + anchorMatcher.group(1).toUpperCase() + "\"";
|
||||
endSection = "<A NAME=";
|
||||
}
|
||||
|
||||
data.append(HTML);
|
||||
|
||||
String read;
|
||||
|
||||
do {
|
||||
read = buf.readLine();
|
||||
}
|
||||
while (read != null && read.toUpperCase().indexOf(startSection) == -1);
|
||||
|
||||
|
||||
if (read == null) {
|
||||
data.delete(0, data.length());
|
||||
return;
|
||||
}
|
||||
|
||||
appendLine(data, read);
|
||||
|
||||
if (isClassDoc) {
|
||||
boolean skip = false;
|
||||
|
||||
while (((read = buf.readLine()) != null) && !read.toUpperCase().trim().equals(DL)) {
|
||||
if (read.toUpperCase().indexOf(H2) != -1) { // read=class name in <H2>
|
||||
data.append(H2);
|
||||
skip = true;
|
||||
}
|
||||
else if (!skip) {
|
||||
appendLine(data, read);
|
||||
}
|
||||
}
|
||||
|
||||
data.append(DL);
|
||||
|
||||
StringBuffer classDetails = new StringBuffer();
|
||||
|
||||
while (((read = buf.readLine()) != null) && !read.toUpperCase().equals(HR) && !read.toUpperCase().equals(P)) {
|
||||
appendLine(classDetails, read);
|
||||
}
|
||||
|
||||
while (((read = buf.readLine()) != null) && !read.toUpperCase().equals(P) && !read.toUpperCase().equals(HR)) {
|
||||
appendLine(data, read.replaceAll(DT, DT + BR));
|
||||
}
|
||||
|
||||
data.append(classDetails);
|
||||
data.append(P);
|
||||
}
|
||||
|
||||
while (((read = buf.readLine()) != null) && read.indexOf(endSection) == -1 && read.indexOf(greatestEndSection) == -1) {
|
||||
if (read.toUpperCase().indexOf(HR) == -1) {
|
||||
appendLine(data, read);
|
||||
}
|
||||
}
|
||||
|
||||
data.append(HTML_CLOSE);
|
||||
}
|
||||
|
||||
private static void appendLine(final StringBuffer buffer, final String read) {
|
||||
buffer.append(read);
|
||||
buffer.append("\n");
|
||||
}
|
||||
|
||||
private interface MyDocBuilder {
|
||||
void buildFromStream(String surl, Reader input, StringBuffer result) throws IOException;
|
||||
}
|
||||
|
||||
private static class MyJavadocFetcher implements Runnable {
|
||||
private static boolean ourFree = true;
|
||||
private final StringBuffer data = new StringBuffer();
|
||||
private final String surl;
|
||||
private final MyDocBuilder myBuilder;
|
||||
private final Exception [] myExceptions = new Exception[1];
|
||||
private final HttpConfigurable myHttpConfigurable;
|
||||
|
||||
public MyJavadocFetcher(final String surl, MyDocBuilder builder) {
|
||||
this.surl = surl;
|
||||
myBuilder = builder;
|
||||
ourFree = false;
|
||||
myHttpConfigurable = HttpConfigurable.getInstance();
|
||||
}
|
||||
|
||||
public static boolean isFree() {
|
||||
return ourFree;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
if (surl == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reader stream = null;
|
||||
try {
|
||||
stream = getReaderByUrl(surl, myHttpConfigurable, new ProgressIndicatorBase());
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
return;
|
||||
}
|
||||
catch (IOException e) {
|
||||
myExceptions[0] = e;
|
||||
}
|
||||
|
||||
if (stream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
myBuilder.buildFromStream(surl, stream, data);
|
||||
}
|
||||
catch (final IOException e) {
|
||||
myExceptions[0] = e;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
stream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
myExceptions[0] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
ourFree = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return myExceptions[0];
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
myExceptions[0] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.codeInsight.documentation;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.ex.http.HttpFileSystem;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
public class PlatformDocumentationUtil {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.documentation.PlatformDocumentationUtil");
|
||||
|
||||
private static final @NonNls Pattern ourLtFixupPattern = Pattern.compile("<([^/^\\w^!])");
|
||||
private static final @NonNls Pattern ourToQuote = Pattern.compile("[\\\\\\.\\^\\$\\?\\*\\+\\|\\)\\}\\]\\{\\(\\[]");
|
||||
private static final @NonNls String LT_ENTITY = "<";
|
||||
|
||||
@Nullable
|
||||
public static List<String> getHttpRoots(final String[] roots, String relPath) {
|
||||
final ArrayList<String> result = new ArrayList<String>();
|
||||
for (String root : roots) {
|
||||
final VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(root);
|
||||
if (virtualFile != null) {
|
||||
if (virtualFile.getFileSystem() instanceof HttpFileSystem) {
|
||||
String url = virtualFile.getUrl();
|
||||
if (!url.endsWith("/")) url += "/";
|
||||
result.add(url + relPath);
|
||||
}
|
||||
else {
|
||||
VirtualFile file = virtualFile.findFileByRelativePath(relPath);
|
||||
if (file != null) result.add(file.getUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.isEmpty() ? null : result;
|
||||
}
|
||||
|
||||
private static String quote(String x) {
|
||||
if (ourToQuote.matcher(x).find()) {
|
||||
return "\\" + x;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
public static String fixupText(String docText) {
|
||||
Matcher fixupMatcher = ourLtFixupPattern.matcher(docText);
|
||||
LinkedList<String> secondSymbols = new LinkedList<String>();
|
||||
|
||||
while (fixupMatcher.find()) {
|
||||
String s = fixupMatcher.group(1);
|
||||
|
||||
//[db] that's workaround to avoid internal bug
|
||||
if (!s.equals("\\") && !secondSymbols.contains(s)) {
|
||||
secondSymbols.addFirst(s);
|
||||
}
|
||||
}
|
||||
|
||||
for (String s : secondSymbols) {
|
||||
String pattern = "<" + quote(s);
|
||||
|
||||
try {
|
||||
docText = Pattern.compile(pattern).matcher(docText).replaceAll(LT_ENTITY + pattern);
|
||||
}
|
||||
catch (PatternSyntaxException e) {
|
||||
LOG.error("Pattern syntax exception on " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
return docText;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user