| /* |
| * 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.openapi.paths; |
| |
| import com.intellij.codeHighlighting.HighlightDisplayLevel; |
| import com.intellij.codeInsight.intention.IntentionAction; |
| import com.intellij.lang.annotation.Annotation; |
| import com.intellij.lang.annotation.AnnotationHolder; |
| import com.intellij.lang.annotation.ExternalAnnotator; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.PsiAnchor; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.util.containers.HashMap; |
| import org.apache.commons.httpclient.HttpClient; |
| import org.apache.commons.httpclient.HttpStatus; |
| import org.apache.commons.httpclient.cookie.CookiePolicy; |
| import org.apache.commons.httpclient.methods.GetMethod; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.IOException; |
| import java.net.UnknownHostException; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Map; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| public abstract class WebReferencesAnnotatorBase extends ExternalAnnotator<WebReferencesAnnotatorBase.MyInfo[], WebReferencesAnnotatorBase.MyInfo[]> { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.paths.WebReferencesAnnotatorBase"); |
| |
| private final Map<String, MyFetchCacheEntry> myFetchCache = new HashMap<String, MyFetchCacheEntry>(); |
| private final Object myFetchCacheLock = new Object(); |
| private static final long FETCH_CACHE_TIMEOUT = 10000; |
| |
| protected static final WebReference[] EMPTY_ARRAY = new WebReference[0]; |
| |
| @NotNull |
| protected abstract WebReference[] collectWebReferences(@NotNull PsiFile file); |
| |
| @Nullable |
| protected static WebReference lookForWebReference(@NotNull PsiElement element) { |
| return lookForWebReference(Arrays.asList(element.getReferences())); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Nullable |
| private static WebReference lookForWebReference(Collection<PsiReference> references) { |
| for (PsiReference reference : references) { |
| if (reference instanceof WebReference) { |
| return (WebReference)reference; |
| } |
| else if (reference instanceof PsiDynaReference) { |
| final WebReference webReference = lookForWebReference(((PsiDynaReference)reference).getReferences()); |
| if (webReference != null) { |
| return webReference; |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public MyInfo[] collectInformation(@NotNull PsiFile file) { |
| final WebReference[] references = collectWebReferences(file); |
| final MyInfo[] infos = new MyInfo[references.length]; |
| |
| for (int i = 0; i < infos.length; i++) { |
| final WebReference reference = references[i]; |
| infos[i] = new MyInfo(PsiAnchor.create(reference.getElement()), reference.getRangeInElement(), reference.getValue()); |
| } |
| return infos; |
| } |
| |
| @Override |
| public MyInfo[] doAnnotate(MyInfo[] infos) { |
| final MyFetchResult[] fetchResults = new MyFetchResult[infos.length]; |
| for (int i = 0; i < fetchResults.length; i++) { |
| fetchResults[i] = checkUrl(infos[i].myUrl); |
| } |
| |
| boolean containsAvailableHosts = false; |
| |
| for (MyFetchResult fetchResult : fetchResults) { |
| if (fetchResult != MyFetchResult.UNKNOWN_HOST) { |
| containsAvailableHosts = true; |
| } |
| } |
| |
| for (int i = 0; i < fetchResults.length; i++) { |
| final MyFetchResult result = fetchResults[i]; |
| |
| // if all hosts are not available, internet connection may be disabled, so it's better to not report warnings for unknown hosts |
| if (result == MyFetchResult.OK || (!containsAvailableHosts && result == MyFetchResult.UNKNOWN_HOST)) { |
| infos[i].myResult = true; |
| } |
| } |
| |
| return infos; |
| } |
| |
| @Override |
| public void apply(@NotNull PsiFile file, MyInfo[] infos, @NotNull AnnotationHolder holder) { |
| if (infos.length == 0) { |
| return; |
| } |
| |
| final HighlightDisplayLevel displayLevel = getHighlightDisplayLevel(file); |
| |
| for (MyInfo info : infos) { |
| if (!info.myResult) { |
| final PsiElement element = info.myAnchor.retrieve(); |
| if (element != null) { |
| final int start = element.getTextRange().getStartOffset(); |
| final TextRange range = new TextRange(start + info.myRangeInElement.getStartOffset(), |
| start + info.myRangeInElement.getEndOffset()); |
| final String message = getErrorMessage(info.myUrl); |
| |
| final Annotation annotation; |
| |
| if (displayLevel == HighlightDisplayLevel.ERROR) { |
| annotation = holder.createErrorAnnotation(range, message); |
| } |
| else if (displayLevel == HighlightDisplayLevel.WARNING) { |
| annotation = holder.createWarningAnnotation(range, message); |
| } |
| else if (displayLevel == HighlightDisplayLevel.WEAK_WARNING) { |
| annotation = holder.createInfoAnnotation(range, message); |
| } |
| else { |
| annotation = holder.createWarningAnnotation(range, message); |
| } |
| |
| for (IntentionAction action : getQuickFixes()) { |
| annotation.registerFix(action); |
| } |
| } |
| } |
| } |
| } |
| |
| @NotNull |
| protected abstract String getErrorMessage(@NotNull String url); |
| |
| @NotNull |
| protected IntentionAction[] getQuickFixes() { |
| return IntentionAction.EMPTY_ARRAY; |
| } |
| |
| @NotNull |
| protected abstract HighlightDisplayLevel getHighlightDisplayLevel(@NotNull PsiElement context); |
| |
| @NotNull |
| private MyFetchResult checkUrl(String url) { |
| synchronized (myFetchCacheLock) { |
| final MyFetchCacheEntry entry = myFetchCache.get(url); |
| final long currentTime = System.currentTimeMillis(); |
| |
| if (entry != null && currentTime - entry.getTime() < FETCH_CACHE_TIMEOUT) { |
| return entry.getFetchResult(); |
| } |
| |
| final MyFetchResult fetchResult = doCheckUrl(url); |
| myFetchCache.put(url, new MyFetchCacheEntry(currentTime, fetchResult)); |
| return fetchResult; |
| } |
| } |
| |
| private static MyFetchResult doCheckUrl(String url) { |
| final HttpClient client = new HttpClient(); |
| client.setTimeout(3000); |
| client.setConnectionTimeout(3000); |
| // see http://hc.apache.org/httpclient-3.x/cookies.html |
| client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); |
| try { |
| final GetMethod method = new GetMethod(url); |
| final int code = client.executeMethod(method); |
| |
| return code == HttpStatus.SC_OK || code == HttpStatus.SC_REQUEST_TIMEOUT |
| ? MyFetchResult.OK |
| : MyFetchResult.NONEXISTENCE; |
| } |
| catch (UnknownHostException e) { |
| LOG.info(e); |
| return MyFetchResult.UNKNOWN_HOST; |
| } |
| catch (IOException e) { |
| LOG.info(e); |
| return MyFetchResult.OK; |
| } |
| catch (IllegalArgumentException e) { |
| LOG.debug(e); |
| return MyFetchResult.OK; |
| } |
| } |
| |
| private static class MyFetchCacheEntry { |
| private final long myTime; |
| private final MyFetchResult myFetchResult; |
| |
| private MyFetchCacheEntry(long time, @NotNull MyFetchResult fetchResult) { |
| myTime = time; |
| myFetchResult = fetchResult; |
| } |
| |
| public long getTime() { |
| return myTime; |
| } |
| |
| @NotNull |
| public MyFetchResult getFetchResult() { |
| return myFetchResult; |
| } |
| } |
| |
| private static enum MyFetchResult { |
| OK, UNKNOWN_HOST, NONEXISTENCE |
| } |
| |
| protected static class MyInfo { |
| final PsiAnchor myAnchor; |
| final String myUrl; |
| final TextRange myRangeInElement; |
| |
| volatile boolean myResult; |
| |
| private MyInfo(PsiAnchor anchor, TextRange rangeInElement, String url) { |
| myAnchor = anchor; |
| myRangeInElement = rangeInElement; |
| myUrl = url; |
| } |
| } |
| } |