From 87887ae8b78ae5f3de0f096431487292ea88ee38 Mon Sep 17 00:00:00 2001 From: "Kirill.Safonov" Date: Mon, 7 Feb 2011 17:19:09 +0300 Subject: [PATCH] Javadoc fetcher: extract language independent part to platform and use it in Flex plugin, remove dead code --- .../javadoc/JavaDocExternalFilter.java | 412 +----------------- .../codeInsight/javadoc/JavaDocUtil.java | 42 +- .../lang/java/JavaDocumentationProvider.java | 28 +- .../documentation/AbstractExternalFilter.java | 359 +++++++++++++++ .../PlatformDocumentationUtil.java | 95 ++++ 5 files changed, 468 insertions(+), 468 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java index 7d2494cdf827..d28cda96e8ed 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java @@ -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("\"]*)\"", Pattern.CASE_INSENSITIVE|Pattern.DOTALL); - private static @NonNls final Pattern ourAnnihilator = Pattern.compile("/[^/^.]*/[.][.]/"); - private static @NonNls final Pattern ourIMGselector = Pattern.compile("]*)\"", Pattern.CASE_INSENSITIVE|Pattern.DOTALL); private static @NonNls final Pattern ourMethodHeading = Pattern.compile("

(.+?)

", 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 = "
"; - @NonNls private static final String P = "

"; - @NonNls private static final String DL = "

"; @NonNls protected static final String H2 = ""; @NonNls protected static final String HTML_CLOSE = ""; @NonNls protected static final String HTML = ""; - @NonNls private static final String BR = "
"; - @NonNls private static final String 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() { - 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 = ""; - @NonNls String endSection = "SUMMARY ========"; - @NonNls String greatestEndSection = ""; - boolean isClassDoc = true; - - if (anchorMatcher.find()) { - isClassDoc = false; - startSection = " - 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; - } - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java index 38e71b754c3a..bd8d2901c153 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocUtil.java @@ -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 secondSymbols = new LinkedList(); - - 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(); } -} \ No newline at end of file +} diff --git a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java index 657da24e9af0..6328cd086867 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaDocumentationProvider.java @@ -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 httpRoots = getHttpRoots(javadocPaths, relPath); + final List 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 orderEntries = fileIndex.getOrderEntriesForFile(virtualFile); for (OrderEntry orderEntry : orderEntries) { final String[] files = orderEntry.getUrls(JavadocOrderRootType.getInstance()); - final List httpRoot = getHttpRoots(files, relPath); + final List httpRoot = PlatformDocumentationUtil.getHttpRoots(files, relPath); if (httpRoot != null) return httpRoot; } return null; } - @Nullable - public static List getHttpRoots(final String[] roots, String relPath) { - final ArrayList result = new ArrayList(); - 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 findUrlForPackage(PsiPackage aPackage) { String qName = aPackage.getQualifiedName(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java new file mode 100644 index 000000000000..b8d6f346bf7d --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java @@ -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("]*)\"", 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 = "
"; + @NonNls private static final String P = "

"; + @NonNls private static final String DL = "

"; + @NonNls protected static final String H2 = ""; + @NonNls protected static final String HTML_CLOSE = ""; + @NonNls protected static final String HTML = ""; + @NonNls private static final String BR = "
"; + @NonNls private static final String 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() { + 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 = ""; + @NonNls String endSection = "SUMMARY ========"; + @NonNls String greatestEndSection = ""; + boolean isClassDoc = true; + + if (anchorMatcher.find()) { + isClassDoc = false; + startSection = " + 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; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java new file mode 100644 index 000000000000..966d8173c150 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/PlatformDocumentationUtil.java @@ -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 getHttpRoots(final String[] roots, String relPath) { + final ArrayList result = new ArrayList(); + 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 secondSymbols = new LinkedList(); + + 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; + } +}