com.intellij.openapi.paths.WebReferencesAnnotatorBase.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.openapi.paths.WebReferencesAnnotatorBase.java

Source

/*
 * 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;
        }
    }
}