/* * Copyright (C) 2013 The Android Open Source Project * * 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.android.tools.idea.startup; import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; import com.intellij.openapi.roots.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtilCore; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import java.util.List; /** * Helper code for attaching the external annotations .jar file * to the user's Android SDK platforms, if necessary */ @SuppressWarnings("SpellCheckingInspection") // "Modificator" in API usage public class ExternalAnnotationsSupport { private static final Logger LOG = Logger.getInstance(ExternalAnnotationsSupport.class); private static final String[] DEVELOPMENT_ANNOTATIONS_PATHS = { "/../adt/idea/android/annotations", "/android/android/annotations", "/community/android/android/annotations" }; // Based on similar code in MagicConstantInspection @SuppressWarnings("ALL") private static void checkAnnotationsJarAttached(@NotNull PsiFile file, @NotNull ProblemsHolder holder) { // Not yet used if (false) { final Project project = file.getProject(); PsiClass actionBar = JavaPsiFacade.getInstance(project).findClass("android.app.ActionBar", GlobalSearchScope.allScope(project)); if (actionBar == null) { return; // no sdk to attach } PsiMethod[] methods = actionBar.findMethodsByName("getNavigationMode", false); if (methods.length != 1) { return; // no sdk to attach } PsiMethod getModifiers = methods[0]; ExternalAnnotationsManager annotationsManager = ExternalAnnotationsManager.getInstance(project); PsiAnnotation annotation = annotationsManager.findExternalAnnotation(getModifiers, MagicConstant.class.getName()); if (annotation != null) { return; } final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(getModifiers); if (virtualFile == null) { return; // no sdk to attach } final List<OrderEntry> entries = ProjectRootManager.getInstance(project).getFileIndex().getOrderEntriesForFile(virtualFile); Sdk sdk = null; for (OrderEntry orderEntry : entries) { if (orderEntry instanceof JdkOrderEntry) { sdk = ((JdkOrderEntry)orderEntry).getJdk(); if (sdk != null) { break; } } } if (sdk == null) { return; // no sdk to attach } final Sdk finalSdk = sdk; String path = finalSdk.getHomePath(); String text = "No IDEA annotations attached to the Android SDK " + finalSdk.getName() + (path == null ? "" : " (" + FileUtil.toSystemDependentName(path) + ")") + ", some issues will not be found"; holder.registerProblem(file, text, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new LocalQuickFix() { @NotNull @Override public String getName() { return "Attach annotations"; } @NotNull @Override public String getFamilyName() { return getName(); } @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { SdkModificator modifier = finalSdk.getSdkModificator(); attachJdkAnnotations(modifier); modifier.commitChanges(); } }); } }); } } // Based on similar code in JavaSdkImpl public static void attachJdkAnnotations(@NotNull SdkModificator modificator) { String homePath = FileUtil.toSystemIndependentName(PathManager.getHomePath()); VirtualFileManager fileManager = VirtualFileManager.getInstance(); // release build? String releaseLocation = homePath + "/plugins/android/lib/androidAnnotations.jar"; VirtualFile root = fileManager.findFileByUrl("jar://" + releaseLocation + "!/"); for (String relativePath : DEVELOPMENT_ANNOTATIONS_PATHS) { if (root != null) break; String developmentLocation = homePath + relativePath; root = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(developmentLocation)); } if (root == null) { // error message tailored for release build file layout LOG.error("jdk annotations not found in: " + releaseLocation); return; } OrderRootType annoType = AnnotationOrderRootType.getInstance(); modificator.removeRoot(root, annoType); modificator.addRoot(root, annoType); } public static void addAnnotations(@NotNull Sdk sdk) { SdkModificator modifier = sdk.getSdkModificator(); attachJdkAnnotations(modifier); modifier.commitChanges(); } public static void addAnnotationsIfNecessary(@NotNull Sdk sdk) { // Attempt to insert SDK annotations VirtualFile[] roots = sdk.getRootProvider().getFiles(AnnotationOrderRootType.getInstance()); if (roots.length > 0) { return; } SdkModificator modifier = sdk.getSdkModificator(); attachJdkAnnotations(modifier); modifier.commitChanges(); } }