summaryrefslogtreecommitdiff
path: root/android/src
diff options
context:
space:
mode:
authorFuyao Zhao <fuyaoz@google.com>2015-06-22 11:27:05 -0700
committerFuyao Zhao <fuyaoz@google.com>2015-08-03 12:22:41 -0700
commitd2b0ecbfdc3fe8858ad615ff39b2c41d1d616512 (patch)
tree7fe09a51dabb9a8dc64605964489a8090cd27a5a /android/src
parentd0aa6f6e42a8ff80e71b676efb6240446e41d697 (diff)
downloadidea-d2b0ecbfdc3fe8858ad615ff39b2c41d1d616512.tar.gz
Change gradle's language setting when trigger language level change quick fix
Change-Id: I7d7438ab1324f9fedd38b43700a69cf9df0e08e1
Diffstat (limited to 'android/src')
-rwxr-xr-xandroid/src/META-INF/plugin.xml1
-rw-r--r--android/src/com/android/tools/idea/highlight/AndroidHighlightVisitor.java128
-rw-r--r--android/src/com/android/tools/idea/quickfix/GradleIncreaseLanguageLevelFix.java136
3 files changed, 265 insertions, 0 deletions
diff --git a/android/src/META-INF/plugin.xml b/android/src/META-INF/plugin.xml
index b6e2ab1e71f..2efec6f7fc6 100755
--- a/android/src/META-INF/plugin.xml
+++ b/android/src/META-INF/plugin.xml
@@ -733,6 +733,7 @@
<generatedSourcesFilter implementation="com.android.tools.idea.editors.AndroidGeneratedSourcesFilter"/>
<codeInsight.unresolvedReferenceQuickFixProvider
implementation="com.android.tools.idea.quickfix.AndroidUnresolvedReferenceQuickFixProvider" order="last"/>
+ <highlightVisitor implementation="com.android.tools.idea.highlight.AndroidHighlightVisitor" order="last"/>
</extensions>
<extensions defaultExtensionNs="com.intellij.properties">
diff --git a/android/src/com/android/tools/idea/highlight/AndroidHighlightVisitor.java b/android/src/com/android/tools/idea/highlight/AndroidHighlightVisitor.java
new file mode 100644
index 00000000000..7c96caa26bd
--- /dev/null
+++ b/android/src/com/android/tools/idea/highlight/AndroidHighlightVisitor.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 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.highlight;
+
+import com.android.tools.idea.gradle.parser.GradleBuildFile;
+import com.android.tools.idea.quickfix.GradleIncreaseLanguageLevelFix;
+import com.intellij.codeInsight.daemon.impl.HighlightInfo;
+import com.intellij.codeInsight.daemon.impl.HighlightVisitor;
+import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder;
+import com.intellij.codeInsight.daemon.impl.analysis.IncreaseLanguageLevelFix;
+import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.Condition;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.openapi.module.ModuleUtilCore.findModuleForPsiElement;
+import static com.intellij.util.ReflectionUtil.getField;
+
+public class AndroidHighlightVisitor extends JavaElementVisitor implements HighlightVisitor {
+ private HighlightInfoHolder myHolder = null;
+
+ @Override
+ public void visit(@NotNull PsiElement element) {
+ if (myHolder == null) return;
+ Module contextModule = findModuleForPsiElement(element);
+ if (contextModule == null) {
+ return;
+ }
+
+ final AndroidFacet facet = AndroidFacet.getInstance(contextModule);
+ if (facet == null) {
+ return;
+ }
+
+ GradleBuildFile gradleBuildFile = GradleBuildFile.get(contextModule);
+
+ for (int i = 0; i < myHolder.size(); i++) {
+ HighlightInfo info = myHolder.get(i);
+
+ final LanguageLevel[] targetLanguageLevel = {null};
+
+ try {
+ info.unregisterQuickFix(new Condition<IntentionAction>() {
+ @Override
+ public boolean value(IntentionAction intentionAction) {
+ if (intentionAction.getClass() == IncreaseLanguageLevelFix.class) {
+ // TODO add accessor for IncreaseLanguageLevelFix.myLevel
+ targetLanguageLevel[0] = getField(IncreaseLanguageLevelFix.class, intentionAction, LanguageLevel.class, "myLevel");
+ if (targetLanguageLevel[0] != null) return true;
+ }
+ return false;
+ }
+ });
+ }
+ catch (NullPointerException e) {
+ // ignore, unregisterQuickFix doesn't have null check
+ }
+ if (targetLanguageLevel[0] == null) {
+ continue;
+ }
+ if (targetLanguageLevel[0].isAtLeast(LanguageLevel.JDK_1_8)) {
+ // We don't support Java 8 yet.
+ continue;
+ }
+
+ if (gradleBuildFile == null) {
+ // Currently our API doesn't address the case that gradle.build file does not exist at the module folder, so just skip for now.
+ continue;
+ }
+
+ // Notice we don't need special handling for "try with resources" feature.
+ // Though unlike other syntactic sugar in Java 7, try with resources depends on the actual Java 7 library, which is not officially
+ // supported after Android SDK 21, so we can't offer a quickfix to increase the language level to 7.
+ // But if we are not using later SDK, IDEA will first show type mismatch error instead of syntax error because it requires
+ // the resource defined to be 'AutoClosable' type.
+
+ QuickFixAction.registerQuickFixAction(info, new GradleIncreaseLanguageLevelFix(targetLanguageLevel[0], gradleBuildFile));
+ // TODO when we can't increase the language level, maybe we should change the highlight text to reflect that.
+ }
+ }
+
+ @Override
+ public boolean analyze(@NotNull PsiFile file, boolean updateWholeFile, @NotNull HighlightInfoHolder holder, @NotNull Runnable action) {
+ myHolder = holder;
+ try {
+ action.run();
+ }
+ finally {
+ myHolder = null;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean suitableForFile(@NotNull PsiFile file) {
+ return true;
+ }
+
+ @NotNull
+ @Override
+ public AndroidHighlightVisitor clone() {
+ return new AndroidHighlightVisitor();
+ }
+
+ @Override
+ public int order() {
+ return 0;
+ }
+}
diff --git a/android/src/com/android/tools/idea/quickfix/GradleIncreaseLanguageLevelFix.java b/android/src/com/android/tools/idea/quickfix/GradleIncreaseLanguageLevelFix.java
new file mode 100644
index 00000000000..29083b05ef6
--- /dev/null
+++ b/android/src/com/android/tools/idea/quickfix/GradleIncreaseLanguageLevelFix.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 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.quickfix;
+
+import com.android.tools.idea.gradle.parser.BuildFileKey;
+import com.android.tools.idea.gradle.parser.GradleBuildFile;
+import com.android.tools.idea.gradle.project.GradleProjectImporter;
+import com.intellij.codeInsight.CodeInsightBundle;
+import com.intellij.codeInsight.daemon.impl.analysis.IncreaseLanguageLevelFix;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.JdkVersionUtil;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static com.android.tools.idea.gradle.util.GradleUtil.getAndroidProject;
+
+/**
+ * The quickfix for increasing language level by modifying the build.gradle and sync the project.
+ * Most of the code is duplicated from {@link IncreaseLanguageLevelFix} except
+ * the {@link GradleIncreaseLanguageLevelFix#invoke} method.
+ */
+public class GradleIncreaseLanguageLevelFix implements IntentionAction {
+ private static final Logger LOG = Logger.getInstance(GradleIncreaseLanguageLevelFix.class);
+
+ private final LanguageLevel myLevel;
+ private final GradleBuildFile myBuildFile;
+
+ public GradleIncreaseLanguageLevelFix(@NotNull LanguageLevel targetLevel, @NotNull GradleBuildFile buildFile) {
+ myLevel = targetLevel;
+ myBuildFile = buildFile;
+ }
+
+ @Override
+ @NotNull
+ public String getText() {
+ return CodeInsightBundle.message("set.language.level.to.0", myLevel.getPresentableText());
+ }
+
+ @Override
+ @NotNull
+ public String getFamilyName() {
+ return CodeInsightBundle.message("set.language.level");
+ }
+
+ private static boolean isJdkSupportsLevel(@Nullable final Sdk jdk, @NotNull LanguageLevel level) {
+ if (jdk == null) return true;
+ String versionString = jdk.getVersionString();
+ JavaSdkVersion version = versionString == null ? null : JdkVersionUtil.getVersion(versionString);
+ return version != null && version.getMaxLanguageLevel().isAtLeast(level);
+ }
+
+ @Override
+ public boolean isAvailable(@NotNull final Project project, @Nullable final Editor editor, @Nullable final PsiFile file) {
+ if (file == null) return false;
+ final VirtualFile virtualFile = file.getVirtualFile();
+ if (virtualFile == null) return false;
+ final Module module = ModuleUtilCore.findModuleForFile(virtualFile, project);
+ return module != null && isLanguageLevelAcceptable(project, module, myLevel);
+ }
+
+ private static boolean isLanguageLevelAcceptable(@NotNull Project project, @NotNull Module module, @NotNull LanguageLevel level) {
+ return isJdkSupportsLevel(getRelevantJdk(project, module), level);
+ }
+
+ @Override
+ public void invoke(@NotNull final Project project, @Nullable final Editor editor, @Nullable final PsiFile file)
+ throws IncorrectOperationException {
+ if (file == null) return;
+ final VirtualFile virtualFile = file.getVirtualFile();
+ if (virtualFile == null) return;
+ final Module module = ModuleUtilCore.findModuleForFile(virtualFile, project);
+ final LanguageLevel moduleLevel = module == null ? null : LanguageLevelModuleExtensionImpl.getInstance(module).getLanguageLevel();
+
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ if (moduleLevel != null && isLanguageLevelAcceptable(project, module, myLevel)) {
+ String gradleJavaVersion = getGradleJavaVersionString(myLevel);
+ if (getAndroidProject(module) != null) {
+ myBuildFile.setValue(BuildFileKey.SOURCE_COMPATIBILITY, gradleJavaVersion);
+ myBuildFile.setValue(BuildFileKey.TARGET_COMPATIBILITY, gradleJavaVersion);
+ } else {
+ LOG.error("Setting language level on Java module is not supported");
+ }
+ GradleProjectImporter.getInstance().requestProjectSync(project, null);
+ }
+ else {
+ LOG.error("Tried to set language level without specify a module");
+ }
+ }
+ });
+ }
+
+ @Nullable
+ private static Sdk getRelevantJdk(@NotNull Project project, @Nullable Module module) {
+ Sdk projectJdk = ProjectRootManager.getInstance(project).getProjectSdk();
+ Sdk moduleJdk = module == null ? null : ModuleRootManager.getInstance(module).getSdk();
+ return moduleJdk == null ? projectJdk : moduleJdk;
+ }
+
+ @Override
+ public boolean startInWriteAction() {
+ return false;
+ }
+
+ @NotNull
+ private static String getGradleJavaVersionString(@NotNull LanguageLevel languageLevel) {
+ return "JavaVersion." + languageLevel.name().replaceAll("JDK", "VERSION");
+ }
+}