Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Maxim Medvedev
2011-02-07 18:21:40 +03:00
5 changed files with 468 additions and 468 deletions
@@ -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 = "&lt;";
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();
@@ -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;
}
}
}
@@ -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 = "&lt;";
@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;
}
}