All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.android.AndroidDocumentationProvider Maven / Gradle / Ivy

/*
 * Copyright 2000-2010 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 org.jetbrains.android;

import com.android.SdkConstants;
import com.android.resources.ResourceType;
import com.android.tools.idea.AndroidPsiUtils;
import com.android.tools.idea.javadoc.AndroidJavaDocRenderer;
import com.intellij.codeInsight.javadoc.JavaDocExternalFilter;
import com.intellij.facet.ProjectFacetManager;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.lang.documentation.ExternalDocumentationProvider;
import com.intellij.lang.java.JavaDocumentationProvider;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import java.util.Locale;

import static com.android.SdkConstants.CLASS_R;
import static com.android.tools.idea.AndroidPsiUtils.ResourceReferenceType;

/**
 * @author Eugene.Kudelevsky
 */
public class AndroidDocumentationProvider implements DocumentationProvider, ExternalDocumentationProvider {
  private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.AndroidDocumentationProvider");

  @Override
  public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
    return null;
  }

  @Override
  public List getUrlFor(PsiElement element, PsiElement originalElement) {
    return null;
  }

  @Override
  public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) {
    if (originalElement == null) {
      return null;
    }
    ResourceReferenceType referenceType = AndroidPsiUtils.getResourceReferenceType(originalElement);
    if (referenceType == ResourceReferenceType.NONE) {
      return null;
    }

    Module module = ModuleUtilCore.findModuleForPsiElement(originalElement);
    if (module == null) {
      return null;
    }

    ResourceType type = AndroidPsiUtils.getResourceType(originalElement);
    if (type == null) {
      return null;
    }

    String name = AndroidPsiUtils.getResourceName(originalElement);
    boolean isFrameworkResource = referenceType == ResourceReferenceType.FRAMEWORK;
    return AndroidJavaDocRenderer.render(module, type, name, isFrameworkResource);
  }

  @Override
  public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) {
    return null;
  }

  @Override
  public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) {
    return null;
  }

  @Override
  public String fetchExternalDocumentation(final Project project, final PsiElement element, final List docUrls) {
    // Workaround: When you invoke completion on an android.R.type.name field in a Java class, we
    // never get a chance to provide documentation for it via generateDoc, presumably because the
    // field is recognized by an earlier documentation provider (the generic Java javadoc one?) as
    // something we have documentation for. We do however get a chance to fetch documentation for it;
    // that's this call, so in that case we insert our javadoc rendering into the fetched documentation.
    String doc = ApplicationManager.getApplication().runReadAction(new Computable() {
      @Override
      public String compute() {
        if (isFrameworkFieldDeclaration(element)) {
          // We don't have the original module, so just find one of the Android modules in the project.
          // It's theoretically possible that this will point to a different Android version than the one
          // module used by the original request.
          Module module = guessAndroidModule(project, element);
          PsiField field = (PsiField)element;
          PsiClass containingClass = field.getContainingClass();
          assert containingClass != null; // because isFrameworkFieldDeclaration returned true
          ResourceType type = ResourceType.getEnum(containingClass.getName());
          if (module != null && type != null) {
            String name = field.getName();
            String render = AndroidJavaDocRenderer.render(module, type, name, true);
            String external = JavaDocumentationProvider.fetchExternalJavadoc(element, docUrls, new MyDocExternalFilter(project));
            return AndroidJavaDocRenderer.injectExternalDocumentation(render, external);
          }
        }
        return null;
      }
    });
    if (doc != null) return null;


    return isMyContext(element, project) ?
           JavaDocumentationProvider.fetchExternalJavadoc(element, docUrls, new MyDocExternalFilter(project)) :
           null;
  }

  @Nullable
  private static Module guessAndroidModule(Project project, PsiElement element) {
    Module module = ModuleUtilCore.findModuleForPsiElement(element);
    if (module == null) {
      Module[] modules = ModuleManager.getInstance(project).getModules();
      for (Module m : modules) {
        if (AndroidFacet.getInstance(m) != null) {
          module = m;
          break;
        }
      }
      if (module == null) {
        return null;
      }
    }
    return module;
  }

  private static boolean isFrameworkFieldDeclaration(PsiElement element) {
    if (element instanceof PsiField) {
      PsiField field = (PsiField) element;
      PsiClass typeClass = field.getContainingClass();
      if (typeClass != null) {
        PsiClass rClass = typeClass.getContainingClass();
        return rClass != null && CLASS_R.equals(AndroidPsiUtils.getQualifiedNameSafely(rClass));
      }

    }
    return false;
  }

  @Override
  public boolean hasDocumentationFor(PsiElement element, PsiElement originalElement) {
    return false;
  }

  @Override
  public boolean canPromptToConfigureDocumentation(PsiElement element) {
    return false;
  }

  @Override
  public void promptToConfigureDocumentation(PsiElement element) {
  }

  private static boolean isMyContext(@NotNull final PsiElement element, @NotNull final Project project) {
    if (element instanceof PsiClass) {
      return ApplicationManager.getApplication().runReadAction(new Computable() {
        @Override
        public Boolean compute() {
          PsiFile file = element.getContainingFile();
          if (file == null) {
            return false;
          }
          VirtualFile vFile = file.getVirtualFile();
          if (vFile == null) {
            return false;
          }
          String path = FileUtil.toSystemIndependentName(vFile.getPath());
          if (path.toLowerCase(Locale.US).contains("/" + SdkConstants.FN_FRAMEWORK_LIBRARY + "!/")) {
            if (ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID).size() > 0) {
              VirtualFile jarFile = JarFileSystem.getInstance().getVirtualFileForJar(vFile);
              return jarFile != null && SdkConstants.FN_FRAMEWORK_LIBRARY.equals(jarFile.getName());
            }
          }
          return false;
        }
      });
    }
    return false;
  }

  private static class MyDocExternalFilter extends JavaDocExternalFilter {
    public MyDocExternalFilter(Project project) {
      super(project);
    }

    @Override
    protected void doBuildFromStream(String url, Reader input, StringBuilder data) throws IOException {
      try {
        if (ourAnchorsuffix.matcher(url).find()) {
          super.doBuildFromStream(url, input, data);
          return;
        }
        final BufferedReader buf = new BufferedReader(input);
        try {
          @NonNls String startSection = "";
          @NonNls String endHeader = "";

          data.append(HTML);

          String read;

          do {
            read = buf.readLine();
          }
          while (read != null && !read.toUpperCase(Locale.US).contains(startSection));

          if (read == null) {
            data.delete(0, data.length());
            return;
          }

          data.append(read).append("\n");

          boolean skip = false;
          while (((read = buf.readLine()) != null) && !read.toLowerCase(Locale.US).contains("class overview")) {
            if (!skip && read.length() > 0) {
              data.append(read).append("\n");
            }
            if (read.toUpperCase(Locale.US).contains(endHeader)) {
              skip = true;
            }
          }

          if (read != null) {
            data.append("
\n"); while (((read = buf.readLine()) != null) && !read.toUpperCase(Locale.ENGLISH).startsWith("

")) { data.append(read).append("\n"); } data.append("

\n"); } data.append(HTML_CLOSE); } finally { buf.close(); } } catch (Exception e) { LOG.error(e.getMessage(), e, "URL: " + url); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy