diff --git a/java/idea-ui/src/com/intellij/jarFinder/InternetAttachSourceProvider.java b/java/idea-ui/src/com/intellij/jarFinder/InternetAttachSourceProvider.java index 532fe3234218..c6bb1f199b0e 100644 --- a/java/idea-ui/src/com/intellij/jarFinder/InternetAttachSourceProvider.java +++ b/java/idea-ui/src/com/intellij/jarFinder/InternetAttachSourceProvider.java @@ -34,14 +34,14 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; -import com.intellij.util.io.HttpRequests; import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.net.NetUtils; +import com.intellij.util.io.HttpRequests; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.util.*; import java.util.regex.Pattern; @@ -167,21 +167,8 @@ public class InternetAttachSourceProvider implements AttachSourcesProvider { } try { - File tmpDownload = HttpRequests.request(artifactUrl).connect(new HttpRequests.RequestProcessor() { - @Override - public File process(@NotNull HttpRequests.Request request) throws IOException { - File tmpDownload = FileUtil.createTempFile(libSourceDir, "download.", ".tmp", false, false); - OutputStream out = new BufferedOutputStream(new FileOutputStream(tmpDownload)); - try { - NetUtils.copyStreamContent(indicator, request.getInputStream(), out, request.getConnection().getContentLength()); - } - finally { - out.close(); - } - return tmpDownload; - } - }); - + File tmpDownload = FileUtil.createTempFile(libSourceDir, "download.", ".tmp", false, false); + HttpRequests.request(artifactUrl).saveToFile(tmpDownload, indicator); if (!sourceFile.exists() && !tmpDownload.renameTo(sourceFile)) { LOG.warn("Failed to rename file " + tmpDownload + " to " + sourceFileName); } 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 cac3b57eb90d..16dfdc787097 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocExternalFilter.java @@ -102,7 +102,7 @@ public class JavaDocExternalFilter extends AbstractExternalFilter { } @Override - protected RefConvertor[] getRefConvertors() { + protected RefConvertor[] getRefConverters() { return myReferenceConvertors; } diff --git a/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java b/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java index 90c99703fa97..b404c94b83d2 100644 --- a/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java +++ b/java/java-psi-api/src/com/intellij/psi/augment/PsiAugmentProvider.java @@ -18,7 +18,10 @@ package com.intellij.psi.augment; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiTypeElement; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -44,4 +47,28 @@ public abstract class PsiAugmentProvider { return result; } + + /** + * Extends {@link PsiTypeElement#getType()} so type could be retrieved from external place + * e.g. from variable initializer in lombok case (http://projectlombok.org/features/val.html) + * + * @param typeElement place where inference takes place, + * also nested PsiTypeElement-s (e.g. for List PsiTypeElements corresponding to both List and String would be suggested) + * @return inferred type or null, if inference is not applicable + */ + @Nullable + protected PsiType inferType(PsiTypeElement typeElement) { + return null; + } + + @Nullable + public static PsiType getInferredType(PsiTypeElement typeElement) { + for (PsiAugmentProvider provider : Extensions.getExtensions(EP_NAME)) { + final PsiType type = provider.inferType(typeElement); + if (type != null) { + return type; + } + } + return null; + } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java index 9c7ebcfa0e40..0fbbf3db20e7 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl.source; import com.intellij.lang.ASTNode; import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; import com.intellij.psi.impl.source.tree.CompositePsiElement; @@ -76,6 +77,11 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl } private PsiType calculateType() { + final PsiType inferredType = PsiAugmentProvider.getInferredType(this); + if (inferredType != null) { + return inferredType; + } + PsiType type = null; SmartList annotations = new SmartList(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/augment/LombokVal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/augment/LombokVal.java new file mode 100644 index 000000000000..5d2fa941c47c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/augment/LombokVal.java @@ -0,0 +1,25 @@ +import lombok.val; + +import java.util.*; + +class Test { + val field = 0; + + void method(val param) { + int p = param; + + val i = 0; + int j = i + 1; + + val a = new ArrayList(); + Object o = a.get(0); + + val b = new ArrayList<>(); + String s = b.get(0); + o = b.get(0); + + for (val v : a) { + String vStr = v; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/PsiAugmentProviderTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/PsiAugmentProviderTest.java new file mode 100644 index 000000000000..300d9512ffa3 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/PsiAugmentProviderTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2014 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.daemon; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; +import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +public class PsiAugmentProviderTest extends LightCodeInsightFixtureTestCase { + + private static final String LOMBOK_VAL_FQN = "lombok.val"; + private static final String LOMBOK_VAL_SHORT_NAME = "val"; + + public void testLombokVal() { + PlatformTestUtil.registerExtension(PsiAugmentProvider.EP_NAME, new PsiAugmentProvider() { + @NotNull + @Override + public List getAugments(@NotNull PsiElement element, @NotNull Class type) { + return Collections.emptyList(); + } + + @Nullable + @Override + protected PsiType inferType(PsiTypeElement typeElement) { + final PsiElement parent = typeElement.getParent(); + if (parent instanceof PsiLocalVariable && ((PsiLocalVariable)parent).getInitializer() != null || + parent instanceof PsiParameter && ((PsiParameter)parent).getDeclarationScope() instanceof PsiForeachStatement) { + final String text = typeElement.getText(); + if (LOMBOK_VAL_SHORT_NAME.equals(text) || LOMBOK_VAL_FQN.equals(text)) { + final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement(); + if (referenceElement != null) { + final PsiElement resolve = referenceElement.resolve(); + if (resolve instanceof PsiClass) { + if (parent instanceof PsiLocalVariable) { + final PsiExpression initializer = ((PsiVariable)parent).getInitializer(); + assertNotNull(initializer); + final PsiType initializerType = initializer.getType(); + if (initializer instanceof PsiNewExpression) { + final PsiJavaCodeReferenceElement reference = ((PsiNewExpression)initializer).getClassOrAnonymousClassReference(); + if (reference != null) { + final PsiReferenceParameterList parameterList = reference.getParameterList(); + if (parameterList != null) { + final PsiTypeElement[] elements = parameterList.getTypeParameterElements(); + if (elements.length == 1 && elements[0].getType() instanceof PsiDiamondType) { + return TypeConversionUtil.erasure(initializerType); + } + } + } + } + return initializerType; + } + final PsiForeachStatement foreachStatement = (PsiForeachStatement)((PsiParameter)parent).getDeclarationScope(); + assertNotNull(foreachStatement); + final PsiExpression iteratedValue = foreachStatement.getIteratedValue(); + if (iteratedValue != null) { + return JavaGenericsUtil.getCollectionItemType(iteratedValue); + } + } + } + } + } + return null; + } + }, myTestRootDisposable); + myFixture.addClass("package lombok; public @interface val{}"); + myFixture.testHighlighting(false, false, false, getTestName(false) + ".java"); + } + + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/augment"; + } + +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/ExternalJavadocPresentationTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/ExternalJavadocPresentationTest.java index 3a14a4ef539c..9d51c0ccb508 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/ExternalJavadocPresentationTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/ExternalJavadocPresentationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -71,12 +71,12 @@ public class ExternalJavadocPresentationTest extends LightCodeInsightTestCase { } @Override - public void doBuildFromStream(String surl, Reader input, StringBuffer data, boolean search4Encoding) throws IOException { - super.doBuildFromStream(surl, input, data, search4Encoding); + public void doBuildFromStream(String url, Reader input, StringBuilder data, boolean search4Encoding) throws IOException { + super.doBuildFromStream(url, input, data, search4Encoding); } } JavadocExternalTestFilter filter = new JavadocExternalTestFilter(getProject()); - final StringBuffer extractedData = new StringBuffer(); + StringBuilder extractedData = new StringBuilder(); filter.doBuildFromStream(url, new StringReader(LoadTextUtil.loadText(pageTextFile).toString()), extractedData, false); assertEquals(LoadTextUtil.loadText(expectedTextFile).toString(), extractedData.toString()); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java index 3cc859da0d25..a64917f88e4e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java @@ -20,8 +20,6 @@ import com.intellij.openapi.application.Application; 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.Trinity; @@ -31,8 +29,8 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.psi.PsiElement; import com.intellij.util.SystemProperties; -import com.intellij.util.io.UrlConnectionUtil; -import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.ThrowableConsumer; +import com.intellij.util.io.HttpRequests; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,54 +38,53 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URL; import java.net.URLConnection; +import java.util.Locale; import java.util.concurrent.Future; import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; -/** - * @author db - * @since May 2, 2003 - */ public abstract class AbstractExternalFilter { + private static final Logger LOG = Logger.getInstance(AbstractExternalFilter.class); private static final boolean EXTRACT_IMAGES_FROM_JARS = SystemProperties.getBooleanProperty("extract.doc.images", true); - @NotNull public static final String QUICK_DOC_DIR_NAME = "quickdoc"; + @NotNull + @NonNls + public static final String QUICK_DOC_DIR_NAME = "quickdoc"; - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.javadoc.JavaDocExternalFilter"); - - private static final Pattern ourClassDataStartPattern = Pattern.compile("START OF CLASS DATA", Pattern.CASE_INSENSITIVE); - private static final Pattern ourClassDataEndPattern = Pattern.compile("SUMMARY ========", Pattern.CASE_INSENSITIVE); + private static final Pattern ourClassDataStartPattern = Pattern.compile("START OF CLASS DATA", Pattern.CASE_INSENSITIVE); + private static final Pattern ourClassDataEndPattern = Pattern.compile("SUMMARY ========", Pattern.CASE_INSENSITIVE); private static final Pattern ourNonClassDataEndPattern = Pattern.compile("]*?)\"", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); - private static @NonNls final Pattern ourPathInsideJarPattern = Pattern.compile( + private static @NonNls final Pattern ourPathInsideJarPattern = Pattern.compile( String.format("%s(.+\\.jar)!/(.+?)[^/]+", JarFileSystem.PROTOCOL_PREFIX), Pattern.CASE_INSENSITIVE | Pattern.DOTALL ); - 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 = "
"; - private static final Pattern CHARSET_META_PATTERN = + 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 = "
"; + private static final Pattern CHARSET_META_PATTERN = Pattern.compile("]+\\s*charset=\"?([\\w\\-]*)\\s*\">", Pattern.CASE_INSENSITIVE); - private static final String FIELD_SUMMARY = ""; - private static final String CLASS_SUMMARY = "
"; - private final HttpConfigurable myHttpConfigurable = HttpConfigurable.getInstance(); + private static final String FIELD_SUMMARY = ""; + private static final String CLASS_SUMMARY = "
"; protected static abstract class RefConvertor { - @NotNull private final Pattern mySelector; + @NotNull + private final Pattern mySelector; public RefConvertor(@NotNull Pattern selector) { mySelector = selector; @@ -124,7 +121,7 @@ public abstract class AbstractExternalFilter { } } - protected final RefConvertor myIMGConvertor = new RefConvertor(ourIMGselector) { + protected final RefConvertor myIMGConvertor = new RefConvertor(ourImgSelector) { @Override protected String convertReference(String root, String href) { if (StringUtil.startsWithChar(href, '#')) { @@ -192,7 +189,7 @@ public abstract class AbstractExternalFilter { root = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, "/" + path); } } - return ourHTMLFilesuffix.matcher(root).replaceAll("/") + href; + return ourHtmlFileSuffix.matcher(root).replaceAll("/") + href; } }; @@ -210,39 +207,40 @@ public abstract class AbstractExternalFilter { public String correctRefs(String root, String read) { String result = read; - for (RefConvertor myReferenceConvertor : getRefConvertors()) { + for (RefConvertor myReferenceConvertor : getRefConverters()) { result = myReferenceConvertor.refFilter(root, result); } return result; } - protected abstract RefConvertor[] getRefConvertors(); + protected abstract RefConvertor[] getRefConverters(); - @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)); + private static void getReaderByUrl(@NotNull String url, final @NotNull ThrowableConsumer consumer) + throws IOException { + if (url.startsWith(JAR_PROTOCOL)) { + VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(BrowserUtil.getDocURL(url)); + if (file != null) { + consumer.consume(new StringReader(VfsUtilCore.loadText(file))); + } + return; + } - if (file == null) { + final URL parsedUrl = BrowserUtil.getURL(url); + if (parsedUrl == null) { + return; + } + + HttpRequests.request(parsedUrl.toString()).connect(new HttpRequests.RequestProcessor() { + @Override + public Void process(@NotNull HttpRequests.Request request) throws IOException { + String contentEncoding = guessEncoding(parsedUrl); + InputStream inputStream = request.getInputStream(); + //noinspection IOResourceOpenedButNotSafelyClosed + consumer.consume(contentEncoding != null ? new MyReader(inputStream, contentEncoding) : new MyReader(inputStream)); return null; } - - return new StringReader(VfsUtilCore.loadText(file)); - } - - URL url = BrowserUtil.getURL(surl); - if (url == null) { - return null; - } - final URLConnection urlConnection = httpConfigurable.openConnection(url.toString()); - final String contentEncoding = guessEncoding(url); - final InputStream inputStream = - pi != null ? UrlConnectionUtil.getConnectionInputStreamWithException(urlConnection, pi) : urlConnection.getInputStream(); - //noinspection IOResourceOpenedButNotSafelyClosed - return contentEncoding != null ? new MyReader(inputStream, contentEncoding) : new MyReader(inputStream); + }); } private static String guessEncoding(URL url) { @@ -264,31 +262,34 @@ public abstract class AbstractExternalFilter { catch (IOException ignored) { } finally { - if (reader != null) + if (reader != null) { try { reader.close(); } catch (IOException ignored) { } + } } return result; } @Nullable @SuppressWarnings({"HardCodedStringLiteral"}) - public String getExternalDocInfo(final String surl) throws Exception { + public String getExternalDocInfo(final String url) throws Exception { Application app = ApplicationManager.getApplication(); if (!app.isUnitTestMode() && app.isDispatchThread() || app.isWriteAccessAllowed()) { LOG.error("May block indefinitely: shouldn't be called from EDT or under write lock"); return null; } - if (surl == null) return null; + if (url == null) { + return null; + } if (MyJavadocFetcher.isFree()) { - final MyJavadocFetcher fetcher = new MyJavadocFetcher(surl, new MyDocBuilder() { + final MyJavadocFetcher fetcher = new MyJavadocFetcher(url, new MyDocBuilder() { @Override - public void buildFromStream(String surl, Reader input, StringBuffer result) throws IOException { - doBuildFromStream(surl, input, result); + public void buildFromStream(String url, Reader input, StringBuilder result) throws IOException { + doBuildFromStream(url, input, result); } }); final Future fetcherFuture = app.executeOnPooledThread(fetcher); @@ -304,7 +305,7 @@ public abstract class AbstractExternalFilter { throw exception; } - final String docText = correctRefs(ourAnchorsuffix.matcher(surl).replaceAll(""), fetcher.getData()); + final String docText = correctRefs(ourAnchorSuffix.matcher(url).replaceAll(""), fetcher.getData()); if (LOG.isDebugEnabled()) { LOG.debug("Filtered JavaDoc: " + docText + "\n"); } @@ -318,13 +319,12 @@ public abstract class AbstractExternalFilter { return getExternalDocInfo(docURL); } - protected void doBuildFromStream(String surl, Reader input, StringBuffer data) throws IOException { - doBuildFromStream(surl, input, data, true); + protected void doBuildFromStream(String url, Reader input, StringBuilder data) throws IOException { + doBuildFromStream(url, input, data, true); } - protected void doBuildFromStream(String surl, Reader input, StringBuffer data, boolean search4Encoding) throws IOException { - BufferedReader buf = new BufferedReader(input); - Trinity settings = getParseSettings(surl); + protected void doBuildFromStream(final String url, Reader input, final StringBuilder data, boolean search4Encoding) throws IOException { + Trinity settings = getParseSettings(url); @NonNls Pattern startSection = settings.first; @NonNls Pattern endSection = settings.second; boolean useDt = settings.third; @@ -349,6 +349,8 @@ public abstract class AbstractExternalFilter { String read; String contentEncoding = null; + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + BufferedReader buf = new BufferedReader(input); do { read = buf.readLine(); if (read != null && search4Encoding && read.contains("charset")) { @@ -360,21 +362,23 @@ public abstract class AbstractExternalFilter { } while (read != null && !startSection.matcher(StringUtil.toUpperCase(read)).find()); - if (input instanceof MyReader && contentEncoding != null) { - if (!contentEncoding.equalsIgnoreCase(CharsetToolkit.UTF8) && - !contentEncoding.equals(((MyReader)input).getEncoding())) - { //restart page parsing with correct encoding - Reader stream; - try { - stream = getReaderByUrl(surl, myHttpConfigurable, new ProgressIndicatorBase()); - } - catch (ProcessCanceledException e) { - return; - } - data.delete(0, data.length()); - doBuildFromStream(surl, new MyReader(((MyReader)stream).getInputStream(), contentEncoding), data, false); + if (input instanceof MyReader && contentEncoding != null && !contentEncoding.equalsIgnoreCase(CharsetToolkit.UTF8) && + !contentEncoding.equals(((MyReader)input).getEncoding())) { + //restart page parsing with correct encoding + try { + final String finalContentEncoding = contentEncoding; + getReaderByUrl(url, new ThrowableConsumer() { + @Override + public void consume(Reader reader) throws IOException { + data.delete(0, data.length()); + doBuildFromStream(url, new MyReader(((MyReader)reader).getInputStream(), finalContentEncoding), data, false); + } + }); + } + catch (ProcessCanceledException e) { return; } + return; } if (read == null) { @@ -386,7 +390,7 @@ public abstract class AbstractExternalFilter { boolean skip = false; do { - if (StringUtil.toUpperCase(read).contains(H2) && !read.toUpperCase().contains("H2")) { // read=class name in

+ if (StringUtil.toUpperCase(read).contains(H2) && !read.toUpperCase(Locale.ENGLISH).contains("H2")) { // read=class name in

data.append(H2); skip = true; } @@ -403,8 +407,7 @@ public abstract class AbstractExternalFilter { data.append(DL); - StringBuffer classDetails = new StringBuffer(); - + StringBuilder classDetails = new StringBuilder(); while (((read = buf.readLine()) != null) && !StringUtil.toUpperCase(read).equals(HR) && !StringUtil.toUpperCase(read).equals(P)) { if (reachTheEnd(data, read, classDetails)) return; appendLine(classDetails, read); @@ -438,9 +441,9 @@ public abstract class AbstractExternalFilter { /** * Decides what settings should be used for parsing content represented by the given url. * - * @param url url which points to the target content - * @return following data: (start interested data boundary pattern; end interested data boundary pattern; - * replace table data by <dt>) + * @param url url which points to the target content + * @return following data: (start interested data boundary pattern; end interested data boundary pattern; + * replace table data by <dt>) */ @NotNull protected Trinity getParseSettings(@NotNull String url) { @@ -448,7 +451,7 @@ public abstract class AbstractExternalFilter { Pattern endSection = ourClassDataEndPattern; boolean useDt = true; - Matcher anchorMatcher = ourAnchorsuffix.matcher(url); + Matcher anchorMatcher = ourAnchorSuffix.matcher(url); if (anchorMatcher.find()) { useDt = false; startSection = Pattern.compile(Pattern.quote("() { + @Override + public void consume(Reader reader) throws IOException { + myBuilder.buildFromStream(url, reader, data); + } + }); } - catch (ProcessCanceledException e) { - return; + catch (ProcessCanceledException ignored) { } 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; - } + myException = e; } } finally { + //noinspection AssignmentToStaticFieldFromInstanceMethod ourFree = true; } } public Exception getException() { - return myExceptions[0]; + return myException; } public void cleanup() { - myExceptions[0] = null; + myException = null; } } diff --git a/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java b/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java index 2c0919ae77c7..e1530abb1bff 100644 --- a/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java +++ b/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java @@ -37,19 +37,19 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; -import com.intellij.util.io.HttpRequests; import com.intellij.util.concurrency.BoundedTaskExecutor; import com.intellij.util.containers.hash.LinkedHashMap; import com.intellij.util.download.DownloadableFileDescription; import com.intellij.util.download.FileDownloader; +import com.intellij.util.io.HttpRequests; import com.intellij.util.net.IOExceptionDialog; -import com.intellij.util.net.NetUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.ide.PooledThreadExecutor; import javax.swing.*; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -315,22 +315,8 @@ public class FileDownloaderImpl implements FileDownloader { return existingFile; } - File tempFile = FileUtil.createTempFile("download.", ".tmp"); - boolean deleteFile = true; - OutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile)); - try { - indicator.setText2(IdeBundle.message("progress.download.file.text", description.getPresentableFileName(), presentableUrl)); - indicator.setIndeterminate(size == -1); - NetUtils.copyStreamContent(indicator, request.getInputStream(), out, size); - deleteFile = false; - return tempFile; - } - finally { - out.close(); - if (deleteFile) { - FileUtil.delete(tempFile); - } - } + indicator.setText2(IdeBundle.message("progress.download.file.text", description.getPresentableFileName(), presentableUrl)); + return request.saveToFile(FileUtil.createTempFile("download.", ".tmp"), indicator); } }); } diff --git a/platform/platform-api/src/com/intellij/util/io/HttpRequests.java b/platform/platform-api/src/com/intellij/util/io/HttpRequests.java index 7f0f2b7f3e48..22abd66527bc 100644 --- a/platform/platform-api/src/com/intellij/util/io/HttpRequests.java +++ b/platform/platform-api/src/com/intellij/util/io/HttpRequests.java @@ -19,17 +19,21 @@ import com.intellij.ide.IdeBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.SystemProperties; import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.net.NetUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLClassLoader; @@ -58,6 +62,11 @@ public abstract class HttpRequests { InputStream getInputStream() throws IOException; boolean isSuccessful() throws IOException; + + @NotNull + File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException; + + byte[] toBytes(@Nullable ProgressIndicator indicator) throws IOException; } public interface RequestProcessor { @@ -154,6 +163,26 @@ public abstract class HttpRequests { return errorValue; } } + + public void saveToFile(@NotNull final File file, @Nullable final ProgressIndicator indicator) throws IOException { + connect(new HttpRequests.RequestProcessor() { + @Override + public Void process(@NotNull HttpRequests.Request request) throws IOException { + request.saveToFile(file, indicator); + return null; + } + }); + } + + @NotNull + public byte[] toBytes(@Nullable final ProgressIndicator indicator) throws IOException { + return connect(new HttpRequests.RequestProcessor() { + @Override + public byte[] process(@NotNull HttpRequests.Request request) throws IOException { + return request.toBytes(indicator); + } + }); + } } @NotNull @@ -216,6 +245,53 @@ public abstract class HttpRequests { ((HttpURLConnection)myConnection).disconnect(); } } + + @NotNull + public byte[] toBytes(@Nullable ProgressIndicator indicator) throws IOException { + int contentLength = getConnection().getContentLength(); + BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(contentLength > 0 ? contentLength : 32 * 1024); + NetUtils.copyStreamContent(indicator, getInputStream(), out, contentLength); + return ArrayUtil.realloc(out.getInternalBuffer(), out.size()); + } + + @NotNull + public File saveToFile(@NotNull File file, @Nullable ProgressIndicator indicator) throws IOException { + OutputStream out = null; + boolean deleteFile = true; + try { + if (indicator != null) { + indicator.checkCanceled(); + } + + FileUtilRt.createParentDirs(file); + out = new FileOutputStream(file); + NetUtils.copyStreamContent(indicator, getInputStream(), out, getConnection().getContentLength()); + deleteFile = false; + } + catch (IOException e) { + URLConnection connection = getConnection(); + String errorMessage = "Cannot download '" + builder.myUrl + ", headers: " + connection.getHeaderFields(); + if (connection instanceof HttpURLConnection) { + HttpURLConnection httpConnection = (HttpURLConnection)connection; + errorMessage += "', response code: " + httpConnection.getResponseCode() + + ", response message: " + httpConnection.getResponseMessage(); + } + throw new IOException(errorMessage, e); + } + finally { + try { + if (out != null) { + out.close(); + } + } + finally { + if (deleteFile) { + FileUtilRt.delete(file); + } + } + } + return file; + } } RequestImpl request = new RequestImpl(); diff --git a/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java b/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java index e58abc5465a8..e4f8d0237ebe 100644 --- a/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java +++ b/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java @@ -178,12 +178,11 @@ class HttpProxySettingsUi implements ConfigurableUi { //noinspection ConstantConditions HttpRequests.request(answer) .readTimeout(3 * 1000) - .connect(new HttpRequests.RequestProcessor() { + .connect(new HttpRequests.RequestProcessor() { @Override - public Object process(@NotNull HttpRequests.Request request) throws IOException { - int responseCode = ((HttpURLConnection)request.getConnection()).getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_OK) { - exceptionReference.set(new IOException("Error code: " + responseCode)); + public Void process(@NotNull HttpRequests.Request request) throws IOException { + if (!request.isSuccessful()) { + exceptionReference.set(new IOException("Error code: " + ((HttpURLConnection)request.getConnection()).getResponseCode())); } return null; } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java b/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java index 0c626909e920..dea22800f6e5 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/RepositoryHelper.java @@ -27,7 +27,6 @@ import com.intellij.openapi.util.BuildNumber; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.io.HttpRequests; import com.intellij.util.io.URLUtil; -import com.intellij.util.net.NetUtils; import org.apache.http.client.utils.URIBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,7 +35,10 @@ import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.net.URLConnection; @@ -125,13 +127,7 @@ public class RepositoryHelper { if (pluginListFile != null) { synchronized (RepositoryHelper.class) { FileUtil.ensureExists(pluginListFile.getParentFile()); - OutputStream output = new FileOutputStream(pluginListFile); - try { - NetUtils.copyStreamContent(indicator, request.getInputStream(), output, connection.getContentLength()); - } - finally { - output.close(); - } + request.saveToFile(pluginListFile, indicator); return loadPluginList(pluginListFile); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java index 28ed36dc4d54..f4925c43d71f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java @@ -35,12 +35,12 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.PathUtil; import com.intellij.util.io.HttpRequests; import com.intellij.util.io.ZipUtil; -import com.intellij.util.net.NetUtils; import org.apache.http.client.utils.URIBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.net.*; import java.util.List; import java.util.UUID; @@ -254,19 +254,9 @@ public class PluginDownloader { return HttpRequests.request(myPluginUrl).gzip(false).forceHttps(myForceHttps).connect(new HttpRequests.RequestProcessor() { @Override public File process(@NotNull HttpRequests.Request request) throws IOException { - indicator.checkCanceled(); - - URLConnection connection = request.getConnection(); - OutputStream fileOut = new FileOutputStream(file); - try { - NetUtils.copyStreamContent(indicator, request.getInputStream(), fileOut, connection.getContentLength()); - } - finally { - fileOut.close(); - } - + request.saveToFile(file, indicator); if (myFileName == null) { - myFileName = guessFileName(connection, file); + myFileName = guessFileName(request.getConnection(), file); } File newFile = new File(file.getParentFile(), myFileName); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java index 0ca25b221eca..5980dbe4e0ed 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.java @@ -43,7 +43,6 @@ import com.intellij.util.PlatformUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.HttpRequests; import com.intellij.util.io.URLUtil; -import com.intellij.util.net.NetUtils; import com.intellij.util.ui.UIUtil; import org.apache.http.client.utils.URIBuilder; import org.jdom.Document; @@ -54,7 +53,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.InterruptedIOException; import java.net.URISyntaxException; import java.net.URL; import java.util.*; @@ -587,15 +588,7 @@ public final class UpdateChecker { File tempFile = HttpRequests.request(url).gzip(false).forceHttps(forceHttps).connect(new HttpRequests.RequestProcessor() { @Override public File process(@NotNull HttpRequests.Request request) throws IOException { - File tempFile = FileUtil.createTempFile("ij.platform.", ".patch", true); - OutputStream output = new BufferedOutputStream(new FileOutputStream(tempFile)); - try { - NetUtils.copyStreamContent(indicator, request.getInputStream(), output, request.getConnection().getContentLength()); - } - finally { - output.close(); - } - return tempFile; + return request.saveToFile(FileUtil.createTempFile("ij.platform.", ".patch", true), indicator); } }); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/DefaultRemoteContentProvider.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/DefaultRemoteContentProvider.java index 60515ed5592c..1ee07d2fcab8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/DefaultRemoteContentProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/http/DefaultRemoteContentProvider.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.progress.util.AbstractProgressIndicatorExBase; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsBundle; import com.intellij.openapi.vfs.VirtualFile; @@ -30,7 +31,8 @@ import com.intellij.util.io.HttpRequests; import com.intellij.util.net.ssl.CertificateManager; import org.jetbrains.annotations.NotNull; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.net.HttpURLConnection; public class DefaultRemoteContentProvider extends RemoteContentProvider { @@ -63,39 +65,20 @@ public class DefaultRemoteContentProvider extends RemoteContentProvider { .connect(new HttpRequests.RequestProcessor() { @Override public Object process(@NotNull HttpRequests.Request request) throws IOException { - HttpURLConnection connection = (HttpURLConnection)request.getConnection(); - int responseCode = connection.getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_OK) { - throw new IOException(IdeBundle.message("error.connection.failed.with.http.code.N", responseCode)); + if (!request.isSuccessful()) { + throw new IOException(IdeBundle.message("error.connection.failed.with.http.code.N", ((HttpURLConnection)request.getConnection()).getResponseCode())); } - int size = connection.getContentLength(); - OutputStream output = new BufferedOutputStream(new FileOutputStream(file)); - try { - callback.setProgressText(VfsBundle.message("download.progress.downloading", presentableUrl), size == -1); - if (size != -1) { + int size = request.getConnection().getContentLength(); + callback.setProgressText(VfsBundle.message("download.progress.downloading", presentableUrl), size == -1); + request.saveToFile(file, new AbstractProgressIndicatorExBase() { + @Override + public void setFraction(double fraction) { callback.setProgressFraction(0); } + }); - int count; - byte[] buf = new byte[4096]; - int total = 0; - while ((count = request.getInputStream().read(buf)) > 0) { - if (callback.isCancelled()) { - return null; - } - total += count; - if (size > 0) { - callback.setProgressFraction((double)total / size); - } - output.write(buf, 0, count); - } - } - finally { - output.close(); - } - - FileType fileType = RemoteFileUtil.getFileType(connection.getContentType()); + FileType fileType = RemoteFileUtil.getFileType(request.getConnection().getContentType()); if (fileType == FileTypes.PLAIN_TEXT) { FileType fileTypeByFileName = FileTypeRegistry.getInstance().getFileTypeByFileName(PathUtilRt.getFileName(url.getPath())); if (fileTypeByFileName != FileTypes.UNKNOWN) { diff --git a/platform/platform-resources-en/src/messages/XmlBundle.properties b/platform/platform-resources-en/src/messages/XmlBundle.properties index b3544974da38..53c4fc55aa1f 100644 --- a/platform/platform-resources-en/src/messages/XmlBundle.properties +++ b/platform/platform-resources-en/src/messages/XmlBundle.properties @@ -217,6 +217,8 @@ emmet.configuration.title=Emmet emmet.enable.label=&Enable XML Emmet emmet.filters.enabled.by.default=Filters enabled by default emmet.enable.preview=Enable &abbreviation preview +emmet.allow.compact.boolean.attributes=Allow &compact boolean attributes in HTML (use
instead of
) +emmet.boolean.attributes=Boolean attributes emmet.expand.abbreviation.with=Expand &abbreviation with title.cannot.create.html.file=Cannot create HTML file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java index d68109862ae8..4ac53c2caab6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java @@ -18,11 +18,9 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.util.io.HttpRequests; -import com.intellij.util.net.NetUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.List; @@ -141,25 +139,15 @@ public abstract class AbstractAttachSourceProvider implements AttachSourcesProvi @Override public ActionCallback perform(List orderEntriesContainingFile) { - final ActionCallback callback = new ActionCallback(); - Task task = new Task.Backgroundable(myProject, "Downloading sources...", true) { @Override public void run(@NotNull final ProgressIndicator indicator) { - final ByteArrayOutputStream out; + final byte[] bytes; try { LOG.info("Downloading sources JAR: " + myUrl); indicator.checkCanceled(); - out = HttpRequests.request(myUrl).connect(new HttpRequests.RequestProcessor() { - @Override - public ByteArrayOutputStream process(@NotNull HttpRequests.Request request) throws IOException { - int contentLength = request.getConnection().getContentLength(); - ByteArrayOutputStream out = new ByteArrayOutputStream(contentLength > 0 ? contentLength : 100 * 1024); - NetUtils.copyStreamContent(indicator, request.getInputStream(), out, contentLength); - return out; - } - }); + bytes = HttpRequests.request(myUrl).toBytes(indicator); } catch (IOException e) { LOG.warn(e); @@ -183,7 +171,7 @@ public abstract class AbstractAttachSourceProvider implements AttachSourcesProvi public void run() { AccessToken accessToken = WriteAction.start(); try { - storeFile(out.toByteArray()); + storeFile(bytes); } finally { accessToken.finish(); diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/quickfix/DownloadManager.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/quickfix/DownloadManager.java index e6fda73fe2fc..d448c4f4562d 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/quickfix/DownloadManager.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/quickfix/DownloadManager.java @@ -30,13 +30,10 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.util.io.HttpRequests; -import com.intellij.util.net.NetUtils; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.util.Set; @@ -68,19 +65,10 @@ public abstract class DownloadManager { file = HttpRequests.request(location).connect(new HttpRequests.RequestProcessor() { @Override public File process(@NotNull HttpRequests.Request request) throws IOException { - int total = request.getConnection().getContentLength(); String name = Integer.toHexString(System.identityHashCode(this)) + "_" + Integer.toHexString(location.hashCode()) + "_" + location.substring(location.lastIndexOf('/') + 1); - File file = new File(myResourcePath, name.lastIndexOf('.') == -1 ? name + ".xml" : name); - OutputStream out = new FileOutputStream(file); - try { - NetUtils.copyStreamContent(myProgress, request.getInputStream(), out, total); - } - finally { - out.close(); - } - return file; + return request.saveToFile(new File(myResourcePath, name.lastIndexOf('.') == -1 ? name + ".xml" : name), myProgress); } }); diff --git a/xml/impl/resources/liveTemplates/zen_html.xml b/xml/impl/resources/liveTemplates/zen_html.xml index f6a9f5247f4b..ec5a2d388562 100644 --- a/xml/impl/resources/liveTemplates/zen_html.xml +++ b/xml/impl/resources/liveTemplates/zen_html.xml @@ -249,7 +249,7 @@