diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java index b00372cc9552..9dfa90f5b4f2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/PsiTypeLookupItem.java @@ -78,8 +78,9 @@ public class PsiTypeLookupItem extends LookupItem { PsiElement position = context.getFile().findElementAt(context.getStartOffset()); assert position != null; - context.getDocument().insertString(context.getTailOffset(), calcGenerics(position, context)); - JavaCompletionUtil.shortenReference(context.getFile(), context.getStartOffset()); + int genericsStart = context.getTailOffset(); + context.getDocument().insertString(genericsStart, calcGenerics(position, context)); + JavaCompletionUtil.shortenReference(context.getFile(), genericsStart - 1); int tail = context.getTailOffset(); String braces = StringUtil.repeat("[]", getBracketsCount()); diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 2a255d292d31..af8ccfdd5b82 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -595,16 +595,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState { } if (dfaRight instanceof DfaNotNullValue) { - if (dfaLeft instanceof DfaVariableValue) { - DfaVariableState varState = getVariableState((DfaVariableValue)dfaLeft); - DfaVariableValue dfaVar = (DfaVariableValue)dfaLeft; - DfaTypeValue type = myFactory.getTypeFactory().create(((DfaNotNullValue)dfaRight).getType()); - if (isNegated) { - return applyCondition(myFactory.getRelationFactory().create(dfaVar, DfaUnknownValue.getInstance(), JavaTokenType.EQEQ, false)); - } - return applyCondition(compareToNull(dfaVar, false)) && varState.setInstanceofValue(type); - - } return true; } diff --git a/java/java-tests/testData/codeInsight/completion/smartType/InnerAfterNew-out.java b/java/java-tests/testData/codeInsight/completion/smartType/InnerAfterNew-out.java new file mode 100644 index 000000000000..3dd226da6e68 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/InnerAfterNew-out.java @@ -0,0 +1,14 @@ +public class Test { + + { + new Foo().method(new Outer.Inner()); + } + + static class Outer { + static class Inner {} + } + + class Foo { + public void method(Outer.Inner inner) {} + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/InnerAfterNew.java b/java/java-tests/testData/codeInsight/completion/smartType/InnerAfterNew.java new file mode 100644 index 000000000000..ea4d21da6f9c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/InnerAfterNew.java @@ -0,0 +1,14 @@ +public class Test { + + { + new Foo().method(new ); + } + + static class Outer { + static class Inner {} + } + + class Foo { + public void method(Outer.Inner inner) {} + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ComparingToNotNullShouldNotAffectNullity.java b/java/java-tests/testData/inspection/dataFlow/fixture/ComparingToNotNullShouldNotAffectNullity.java new file mode 100644 index 000000000000..b44b6f862e82 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ComparingToNotNullShouldNotAffectNullity.java @@ -0,0 +1,26 @@ +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public class Bar3 { + + @NotNull + Object getObj() { + return new Object(); + } + + void foo(Collection collection) { + if (!collection.isEmpty()) { + Object first = collection.iterator().next(); + if (first != getObj() || collection.size() > 0) { + System.out.println(first.hashCode()); + } + if (first == getObj() || collection.size() > 0) { + System.out.println(first.hashCode()); + } + if (first == null) { + System.out.println(first.hashCode()); + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java index 48510cd073ac..352b09a4b62c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java @@ -1034,6 +1034,7 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { public void testArrayInitializerBeforeVarargs() throws Throwable { doTest(); } public void testDuplicateMembersFromSuperClass() throws Throwable { doTest(); } + public void testInnerAfterNew() throws Throwable { doTest(); } public void testMemberImportStatically() { configureByTestName(); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java index 77bd8e03e1f0..b8c5012f562e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java @@ -61,5 +61,6 @@ public class DataFlowInspectionFixtureTest extends JavaCodeInsightFixtureTestCas public void testGeneratedEquals() throws Throwable { doTest(); } public void testIDEA84489() throws Throwable { doTest(); } + public void testComparingToNotNullShouldNotAffectNullity() throws Throwable { doTest(); } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspection.java index a9e1cb43492f..cbb11ead2be1 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2010 Bas Leijdekkers + * Copyright 2009-2012 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +19,13 @@ import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.HighlightUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; @@ -34,12 +34,10 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.HashSet; -public class DynamicRegexReplaceableByCompiledPatternInspection - extends BaseInspection { +public class DynamicRegexReplaceableByCompiledPatternInspection extends BaseInspection { @NonNls - private static final Collection regexMethodNames = - new HashSet(4); + private static final Collection regexMethodNames = new HashSet(4); static { regexMethodNames.add("matches"); @@ -73,8 +71,7 @@ public class DynamicRegexReplaceableByCompiledPatternInspection return new DynamicRegexReplaceableByCompiledPatternFix(); } - private static class DynamicRegexReplaceableByCompiledPatternFix - extends InspectionGadgetsFix { + private static class DynamicRegexReplaceableByCompiledPatternFix extends InspectionGadgetsFix { @NotNull public String getName() { @@ -86,8 +83,7 @@ public class DynamicRegexReplaceableByCompiledPatternInspection protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { final PsiElement element = descriptor.getPsiElement(); - final PsiClass aClass = PsiTreeUtil.getParentOfType(element, - PsiClass.class); + final PsiClass aClass = ClassUtils.getContainingStaticClass(element); if (aClass == null) { return; } @@ -95,38 +91,28 @@ public class DynamicRegexReplaceableByCompiledPatternInspection if (!(parent instanceof PsiReferenceExpression)) { return; } - final PsiReferenceExpression methodExpression = - (PsiReferenceExpression)parent; + final PsiReferenceExpression methodExpression = (PsiReferenceExpression)parent; final PsiElement grandParent = methodExpression.getParent(); if (!(grandParent instanceof PsiMethodCallExpression)) { return; } - final PsiMethodCallExpression methodCallExpression = - (PsiMethodCallExpression)grandParent; - final PsiExpressionList list = - methodCallExpression.getArgumentList(); + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; + final PsiExpressionList list = methodCallExpression.getArgumentList(); final PsiExpression[] expressions = list.getExpressions(); @NonNls final StringBuilder fieldText = - new StringBuilder( - "private static final java.util.regex.Pattern PATTERN = " + - "java.util.regex.Pattern.compile("); + new StringBuilder("private static final java.util.regex.Pattern PATTERN = java.util.regex.Pattern.compile("); final int expressionsLength = expressions.length; if (expressionsLength > 0) { fieldText.append(expressions[0].getText()); } fieldText.append(");"); - final PsiElementFactory factory = - JavaPsiFacade.getElementFactory(project); - final PsiField newField = - factory.createFieldFromText(fieldText.toString(), element); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiField newField = factory.createFieldFromText(fieldText.toString(), element); final PsiElement field = aClass.add(newField); - @NonNls final StringBuilder expressionText = - new StringBuilder("PATTERN."); - @NonNls final String methodName = - methodExpression.getReferenceName(); - final PsiExpression qualifier = - methodExpression.getQualifierExpression(); + @NonNls final StringBuilder expressionText = new StringBuilder("PATTERN."); + @NonNls final String methodName = methodExpression.getReferenceName(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); @NonNls final String qualifierText; if (qualifier == null) { qualifierText = "this"; @@ -160,30 +146,18 @@ public class DynamicRegexReplaceableByCompiledPatternInspection expressionText.append(')'); } - final PsiExpression newExpression = - factory.createExpressionFromText(expressionText.toString(), - element); - PsiMethodCallExpression newMethodCallExpression = - (PsiMethodCallExpression)methodCallExpression.replace( - newExpression); - newMethodCallExpression = - CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement( - newMethodCallExpression); - final PsiReferenceExpression reference = - getReference(newMethodCallExpression); - HighlightUtils.showRenameTemplate(aClass, - (PsiNameIdentifierOwner)field, reference); + final PsiExpression newExpression = factory.createExpressionFromText(expressionText.toString(), element); + PsiMethodCallExpression newMethodCallExpression = (PsiMethodCallExpression)methodCallExpression.replace(newExpression); + newMethodCallExpression = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newMethodCallExpression); + final PsiReferenceExpression reference = getReference(newMethodCallExpression); + HighlightUtils.showRenameTemplate(aClass, (PsiNameIdentifierOwner)field, reference); } - private static PsiReferenceExpression getReference( - PsiMethodCallExpression newMethodCallExpression) { - final PsiReferenceExpression methodExpression = - newMethodCallExpression.getMethodExpression(); - final PsiExpression qualifierExpression = - methodExpression.getQualifierExpression(); + private static PsiReferenceExpression getReference(PsiMethodCallExpression newMethodCallExpression) { + final PsiReferenceExpression methodExpression = newMethodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); if (qualifierExpression instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCallExpression = - (PsiMethodCallExpression)qualifierExpression; + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)qualifierExpression; return getReference(methodCallExpression); } if (!(qualifierExpression instanceof PsiReferenceExpression)) { @@ -198,12 +172,10 @@ public class DynamicRegexReplaceableByCompiledPatternInspection return new DynamicRegexReplaceableByCompiledPatternVisitor(); } - private static class DynamicRegexReplaceableByCompiledPatternVisitor - extends BaseInspectionVisitor { + private static class DynamicRegexReplaceableByCompiledPatternVisitor extends BaseInspectionVisitor { @Override - public void visitMethodCallExpression( - PsiMethodCallExpression expression) { + public void visitMethodCallExpression(PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); if (!isCallToRegexMethod(expression)) { return; @@ -212,10 +184,8 @@ public class DynamicRegexReplaceableByCompiledPatternInspection } - private static boolean isCallToRegexMethod( - PsiMethodCallExpression expression) { - final PsiReferenceExpression methodExpression = - expression.getMethodExpression(); + private static boolean isCallToRegexMethod(PsiMethodCallExpression expression) { + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); final String name = methodExpression.getReferenceName(); if (!regexMethodNames.contains(name)) { return false; diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ClassUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ClassUtils.java index 20a3f3375fb4..b7239520d5ec 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ClassUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,14 +30,12 @@ public class ClassUtils { /** * @noinspection StaticCollection */ - private static final Set immutableTypes = - new HashSet(19); + private static final Set immutableTypes = new HashSet(19); /** * @noinspection StaticCollection */ - private static final Set primitiveNumericTypes = - new HashSet(7); + private static final Set primitiveNumericTypes = new HashSet(7); /** * @noinspection StaticCollection @@ -170,6 +168,20 @@ public class ClassUtils { } } + @Nullable + public static PsiClass getContainingStaticClass(PsiElement element) { + PsiClass aClass = PsiTreeUtil.getParentOfType(element, PsiClass.class); + if (aClass == null) { + return null; + } + PsiClass containingClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class); + while (containingClass != null && !containingClass.hasModifierProperty(PsiModifier.STATIC) && !containingClass.isInterface()) { + aClass = containingClass; + containingClass = aClass.getContainingClass(); + } + return aClass; + } + public static boolean isClassVisibleFromClass(PsiClass baseClass, PsiClass referencedClass) { if (referencedClass.hasModifierProperty(PsiModifier.PUBLIC)) { diff --git a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java index f52243482aa3..b1ebfaf600bd 100644 --- a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java +++ b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java @@ -53,8 +53,9 @@ public class AndroidCommonUtils { private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*"); private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*"); - private static Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class"); - private static Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class"); + private static final Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class"); + private static final Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class"); + private static final String BUILD_CONFIG_CLASS_NAME = "BuildConfig.class"; public static final Pattern COMPILER_MESSAGE_PATTERN = Pattern.compile("(.+):(\\d+):.+"); @@ -226,7 +227,8 @@ public class AndroidCommonUtils { if (!packRAndManifestClasses && (R_PATTERN.matcher(file.getName()).matches() || - MANIFEST_PATTERN.matcher(file.getName()).matches())) { + MANIFEST_PATTERN.matcher(file.getName()).matches() || + BUILD_CONFIG_CLASS_NAME.equals(file.getName()))) { return; } diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 8787c3bc246b..e247452399f6 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -134,6 +134,7 @@ + diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java index 6349f4880e33..ac03235e23f0 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java @@ -26,8 +26,6 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.HashSet; import org.jetbrains.android.compiler.tools.AndroidApt; import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.facet.AndroidFacet; @@ -260,7 +258,7 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler { -1, -1); continue; } - final String[] libPackages = getLibPackages(module, packageName); + final String[] libPackages = AndroidCompileUtil.getLibPackages(module, packageName); final Module circularDepLibWithSamePackage = AndroidCompileUtil.findCircularDependencyOnLibraryWithSamePackage(facet); if (circularDepLibWithSamePackage != null && !facet.getConfiguration().LIBRARY_PROJECT) { @@ -304,22 +302,6 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler { } return false; } - - @NotNull - private static String[] getLibPackages(@NotNull Module module, @NotNull String packageName) { - final Set packageSet = new HashSet(); - packageSet.add(packageName); - - final List result = new ArrayList(); - - for (String libPackage : AndroidUtils.getDepLibsPackages(module)) { - if (packageSet.add(libPackage)) { - result.add(libPackage); - } - } - - return ArrayUtil.toStringArray(result); - } } private static class MyValidityState extends ResourceNamesValidityState { diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java index c5d4f05b6c48..f79e8f4eea96 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogenerator.java @@ -2,6 +2,7 @@ package org.jetbrains.android.compiler; import com.android.AndroidConstants; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.internal.build.BuildConfigGenerator; import com.intellij.compiler.impl.CompilerUtil; import com.intellij.compiler.impl.ModuleCompileScope; import com.intellij.openapi.application.ApplicationManager; @@ -58,6 +59,7 @@ public class AndroidAutogenerator { case AIDL: return facet.getConfiguration().REGENERATE_JAVA_BY_AIDL; case RENDERSCRIPT: + case BUILDCONFIG: return true; default: LOG.error("Unknown autogenerator mode " + mode); @@ -81,6 +83,9 @@ public class AndroidAutogenerator { case RENDERSCRIPT: runRenderscript(facet, context); break; + case BUILDCONFIG: + runBuildConfigGenerator(facet, context); + break; default: LOG.error("Unknown mode" + mode); } @@ -96,6 +101,80 @@ public class AndroidAutogenerator { } } + private static void runBuildConfigGenerator(@NotNull final AndroidFacet facet, @NotNull final CompileContext context) { + final Module module = facet.getModule(); + + final BuildconfigAutogenerationItem item = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Nullable + @Override + public BuildconfigAutogenerationItem compute() { + if (module.isDisposed() || module.getProject().isDisposed()) { + return null; + } + + final String sourceRootPath = AndroidRootUtil.getBuildconfigGenSourceRootPath(facet); + if (sourceRootPath == null) { + return null; + } + + final VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet); + if (manifestFile == null) { + context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("android.compilation.error.manifest.not.found"), + null, -1, -1); + return null; + } + + final Manifest manifest = AndroidUtils.loadDomElement(module, manifestFile, Manifest.class); + if (manifest == null) { + context.addMessage(CompilerMessageCategory.ERROR, "Cannot parse file", manifestFile.getUrl(), -1, -1); + return null; + } + + String packageName = manifest.getPackage().getValue(); + if (packageName != null) { + packageName = packageName.trim(); + } + + if (packageName == null || packageName.length() <= 0) { + context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("package.not.found.error"), manifestFile.getUrl(), + -1, -1); + return null; + } + return new BuildconfigAutogenerationItem(packageName, FileUtil.toSystemDependentName(sourceRootPath)); + } + }); + + // doesn't matter, because autogenerated class is used for resolve/completion only + final boolean debug = true; + final BuildConfigGenerator generator = new BuildConfigGenerator(item.mySourceRootOsPath, item.myPackage, debug); + try { + generator.generate(); + + final VirtualFile genSourceRoot = LocalFileSystem.getInstance().findFileByPath(item.mySourceRootOsPath); + if (genSourceRoot != null) { + genSourceRoot.refresh(false, true); + } + facet.clearAutogeneratedFiles(AndroidAutogeneratorMode.BUILDCONFIG); + + final VirtualFile genFile = LocalFileSystem.getInstance().findFileByPath( + item.mySourceRootOsPath + '/' + item.myPackage.replace('.', '/') + '/' + BuildConfigGenerator.BUILD_CONFIG_NAME); + + if (genFile != null && genFile.exists()) { + facet.markFileAutogenerated(AndroidAutogeneratorMode.BUILDCONFIG, genFile); + } + } + catch (final IOException e) { + LOG.info(e); + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + if (module.getProject().isDisposed()) return; + context.addMessage(CompilerMessageCategory.ERROR, "I/O error: " + e.getMessage(), null, -1, -1); + } + }); + } + } + private static void runAapt(@NotNull final AndroidFacet facet, @NotNull final CompileContext context) { final Module module = facet.getModule(); @@ -644,4 +723,14 @@ public class AndroidAutogenerator { myRawDirPath = rawDirPath; } } + + private static class BuildconfigAutogenerationItem { + final String myPackage; + final String mySourceRootOsPath; + + private BuildconfigAutogenerationItem(@NotNull String aPackage, @NotNull String sourceRootOsPath) { + myPackage = aPackage; + mySourceRootOsPath = sourceRootOsPath; + } + } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogeneratorMode.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogeneratorMode.java index 7b8852e674a0..1d685045a55e 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogeneratorMode.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidAutogeneratorMode.java @@ -4,5 +4,5 @@ package org.jetbrains.android.compiler; * @author Eugene.Kudelevsky */ public enum AndroidAutogeneratorMode { - AAPT, AIDL, RENDERSCRIPT + AAPT, AIDL, RENDERSCRIPT, BUILDCONFIG } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidBuildConfigGeneratingCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidBuildConfigGeneratingCompiler.java new file mode 100644 index 000000000000..f0f199ffe6ec --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidBuildConfigGeneratingCompiler.java @@ -0,0 +1,193 @@ +package org.jetbrains.android.compiler; + +import com.android.sdklib.internal.build.BuildConfigGenerator; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.compiler.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.android.dom.manifest.Manifest; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidBuildConfigGeneratingCompiler implements SourceGeneratingCompiler { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidBuildConfigGeneratingCompiler"); + + @Nullable + @Override + public VirtualFile getPresentableFile(CompileContext context, Module module, VirtualFile outputRoot, VirtualFile generatedFile) { + return null; + } + + @Override + public GenerationItem[] getGenerationItems(final CompileContext context) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public GenerationItem[] compute() { + final List result = new ArrayList(); + + for (Module module : ModuleManager.getInstance(context.getProject()).getModules()) { + final AndroidFacet facet = AndroidFacet.getInstance(module); + if (facet == null) { + continue; + } + + final VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet); + if (manifestFile == null) { + context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("android.compilation.error.manifest.not.found"), + null, -1, -1); + continue; + } + + final Manifest manifest = AndroidUtils.loadDomElement(module, manifestFile, Manifest.class); + if (manifest == null) { + context.addMessage(CompilerMessageCategory.ERROR, "Cannot parse file", manifestFile.getUrl(), -1, -1); + continue; + } + + String packageName = manifest.getPackage().getValue(); + if (packageName != null) { + packageName = packageName.trim(); + } + if (packageName == null || packageName.length() <= 0) { + context.addMessage(CompilerMessageCategory.ERROR, AndroidBundle.message("package.not.found.error"), manifestFile.getUrl(), + -1, -1); + continue; + } + final boolean debug = !AndroidCompileUtil.isReleaseBuild(context); + result.add(new MyGenerationItem(module, packageName, debug)); + + for (String libPackage : AndroidCompileUtil.getLibPackages(module, packageName)) { + result.add(new MyGenerationItem(module, libPackage, debug)); + } + } + return result.toArray(new GenerationItem[result.size()]); + } + }); + } + + @Override + public GenerationItem[] generate(CompileContext context, + GenerationItem[] items, + VirtualFile outputRootDirectory) { + if (items == null || items.length == 0) { + return new GenerationItem[0]; + } + context.getProgressIndicator().setText("Generating BuildConfig.java..."); + + final String genFolderOsPath = FileUtil.toSystemDependentName(outputRootDirectory.getPath()); + final List result = new ArrayList(); + + for (GenerationItem item : items) { + final MyGenerationItem genItem = (MyGenerationItem)item; + final BuildConfigGenerator generator = new BuildConfigGenerator(genFolderOsPath, genItem.myPackage, genItem.myDebug); + try { + generator.generate(); + result.add(genItem); + } + catch (IOException e) { + context.addMessage(CompilerMessageCategory.ERROR, "I/O error: " + e.getMessage(), null, -1, -1); + LOG.info(e); + } + } + + if (result.size() > 0) { + outputRootDirectory.refresh(false, true); + } + return result.toArray(new GenerationItem[result.size()]); + } + + @NotNull + @Override + public String getDescription() { + return "Android BuildConfig Generator"; + } + + @Override + public boolean validateConfiguration(CompileScope scope) { + return true; + } + + @Override + public ValidityState createValidityState(DataInput in) throws IOException { + return new MyValidityState(in); + } + + private static class MyGenerationItem implements GenerationItem{ + final Module myModule; + final String myPackage; + final boolean myDebug; + + private MyGenerationItem(@NotNull Module module, @NotNull String aPackage, boolean debug) { + myModule = module; + myPackage = aPackage; + myDebug = debug; + } + + @Override + public String getPath() { + return myPackage.replace('.', '/') + '/' + BuildConfigGenerator.BUILD_CONFIG_NAME; + } + + @Override + public ValidityState getValidityState() { + return new MyValidityState(myPackage, myDebug); + } + + @Override + public Module getModule() { + return myModule; + } + + @Override + public boolean isTestSource() { + return false; + } + } + + private static class MyValidityState implements ValidityState { + private final String myPackage; + private boolean myDebug; + + private MyValidityState(DataInput in) throws IOException { + myPackage = in.readUTF(); + myDebug = in.readBoolean(); + } + + private MyValidityState(@NotNull String aPackage, boolean debug) { + myPackage = aPackage; + myDebug = debug; + } + + @Override + public boolean equalsTo(ValidityState otherState) { + if (!(otherState instanceof MyValidityState)) { + return false; + } + final MyValidityState otherState1 = (MyValidityState)otherState; + return otherState1.myPackage.equals(myPackage) && otherState1.myDebug == myDebug; + } + + @Override + public void save(DataOutput out) throws IOException { + out.writeUTF(myPackage); + out.writeBoolean(myDebug); + } + } +} diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index 53041f9739b3..c681b3dd4a60 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -654,6 +654,8 @@ public class AndroidCompileUtil { final Module module = facet.getModule(); final GlobalSearchScope moduleScope = facet.getModule().getModuleScope(); + initializeGenSourceRoot(module, AndroidRootUtil.getBuildconfigGenSourceRootPath(facet), true, true); + initializeGenSourceRoot(module, AndroidRootUtil.getRenderscriptGenSourceRootPath(facet), FileTypeIndex.getFiles(AndroidRenderscriptFileType.INSTANCE, moduleScope).size() > 0, true); @@ -900,4 +902,20 @@ public class AndroidCompileUtil { } return null; } + + @NotNull + public static String[] getLibPackages(@NotNull Module module, @NotNull String packageName) { + final Set packageSet = new HashSet(); + packageSet.add(packageName); + + final List result = new ArrayList(); + + for (String libPackage : AndroidUtils.getDepLibsPackages(module)) { + if (packageSet.add(libPackage)) { + result.add(libPackage); + } + } + + return ArrayUtil.toStringArray(result); + } } \ No newline at end of file diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java index 577d46b4d98b..7539aa2329a4 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java @@ -190,6 +190,11 @@ public class AndroidPrecompileTask implements CompileTask { sourceRootSet.remove(rsGenRoot); } + final VirtualFile buildconfigGenDir = AndroidRootUtil.getBuildconfigGenDir(facet); + if (buildconfigGenDir != null) { + sourceRootSet.remove(buildconfigGenDir); + } + final ExcludeEntryDescription[] descriptions = configuration.getExcludeEntryDescriptions(); configuration.removeAllExcludeEntryDescriptions(); diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java index 3642a15280e7..b16659b7212b 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java @@ -459,6 +459,7 @@ public class AndroidFacet extends Facet { AndroidCompileUtil.generate(module, AndroidAutogeneratorMode.AIDL); } AndroidCompileUtil.generate(module, AndroidAutogeneratorMode.RENDERSCRIPT); + AndroidCompileUtil.generate(module, AndroidAutogeneratorMode.BUILDCONFIG); activateSourceAutogenerating(); } diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java index 733a27394c50..6a67ea9fac2e 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidResourceFilesListener.java @@ -39,6 +39,9 @@ import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Set; import static org.jetbrains.android.util.AndroidUtils.findSourceRoot; @@ -144,59 +147,61 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { return; } - final AndroidAutogeneratorMode autogenerationMode = - ApplicationManager.getApplication().runReadAction(new Computable() { + final List autogenerationModes = + ApplicationManager.getApplication().runReadAction(new Computable>() { @Nullable - public AndroidAutogeneratorMode compute() { - return computeCompilerToRunAndInvalidateLocalAttributesMap(); + public List compute() { + return computeCompilersToRunAndInvalidateLocalAttributesMap(); } }); - if (autogenerationMode == null) { + if (autogenerationModes.isEmpty()) { return; } - if (autogenerationMode == AndroidAutogeneratorMode.AAPT && - AndroidRootUtil.getManifestFile(myFacet) != myEvent.getFile()) { + for (AndroidAutogeneratorMode autogenerationMode : autogenerationModes) { + if (autogenerationMode == AndroidAutogeneratorMode.AAPT && + AndroidRootUtil.getManifestFile(myFacet) != myEvent.getFile()) { - final HashSet resourceSet = new HashSet(); + final HashSet resourceSet = new HashSet(); - DumbService.getInstance(myFacet.getModule().getProject()).waitForSmartMode(); + DumbService.getInstance(myFacet.getModule().getProject()).waitForSmartMode(); - AndroidCompileUtil.collectAllResources(myFacet, resourceSet); + AndroidCompileUtil.collectAllResources(myFacet, resourceSet); - synchronized (RESOURCES_SET_LOCK) { - if (resourceSet.equals(myResourceSet) && - !myFacet.areSourcesGeneratedWithErrors(AndroidAutogeneratorMode.AAPT)) { - return; + synchronized (RESOURCES_SET_LOCK) { + if (resourceSet.equals(myResourceSet) && + !myFacet.areSourcesGeneratedWithErrors(AndroidAutogeneratorMode.AAPT)) { + return; + } + myResourceSet = resourceSet; } - myResourceSet = resourceSet; } + AndroidCompileUtil.generate(myFacet.getModule(), autogenerationMode, true); } - AndroidCompileUtil.generate(myFacet.getModule(), autogenerationMode, true); } - @Nullable - private AndroidAutogeneratorMode computeCompilerToRunAndInvalidateLocalAttributesMap() { + @NotNull + private List computeCompilersToRunAndInvalidateLocalAttributesMap() { if (myFacet.isDisposed()) { - return null; + return Collections.emptyList(); } final Module myModule = myFacet.getModule(); final Project project = myModule.getProject(); if (project.isDisposed()) { - return null; + return Collections.emptyList(); } final VirtualFile file = myEvent.getFile(); final Module module = ModuleUtil.findModuleForFile(file, project); if (module != myModule) { - return null; + return Collections.emptyList(); } VirtualFile parent = myEvent.getParent(); if (parent == null) { - return null; + return Collections.emptyList(); } final VirtualFile gp = parent.getParent(); @@ -207,10 +212,13 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { ResourceFolderType.VALUES.getName().equals(AndroidCommonUtils.getResourceTypeByDirName(parent.getName()))) { myFacet.getLocalResourceManager().invalidateAttributeDefinitions(); } + final VirtualFile manifestFile = AndroidRootUtil.getManifestFile(myFacet); + + final List modes = new ArrayList(); if (AndroidAptCompiler.isToCompileModule(module, myFacet.getConfiguration()) && (myFacet.getConfiguration().REGENERATE_R_JAVA && (gp == resourceDir || - AndroidRootUtil.getManifestFile(myFacet) == file))) { + manifestFile == file))) { final Manifest manifest = myFacet.getManifest(); final String aPackage = manifest != null ? manifest.getPackage().getValue() : null; @@ -219,23 +227,27 @@ class AndroidResourceFilesListener extends VirtualFileAdapter { AndroidCompileUtil.removeDuplicatingClasses(myModule, myCachedPackage, AndroidUtils.R_CLASS_NAME, null, aptGenDirPath); } myCachedPackage = aPackage; - return AndroidAutogeneratorMode.AAPT; + modes.add(AndroidAutogeneratorMode.AAPT); } if (myFacet.getConfiguration().REGENERATE_JAVA_BY_AIDL && file.getFileType() == AndroidIdlFileType.ourFileType) { VirtualFile sourceRoot = findSourceRoot(myModule, file); if (sourceRoot != null && AndroidRootUtil.getAidlGenDir(myFacet) != sourceRoot) { - return AndroidAutogeneratorMode.AIDL; + modes.add(AndroidAutogeneratorMode.AIDL); } } if (file.getFileType() == AndroidRenderscriptFileType.INSTANCE) { final VirtualFile sourceRoot = findSourceRoot(myModule, file); if (sourceRoot != null && AndroidRootUtil.getRenderscriptGenDir(myFacet) != sourceRoot) { - return AndroidAutogeneratorMode.RENDERSCRIPT; + modes.add(AndroidAutogeneratorMode.RENDERSCRIPT); } } - return null; + + if (manifestFile == file) { + modes.add(AndroidAutogeneratorMode.BUILDCONFIG); + } + return modes; } @Override diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java b/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java index e88bdb910992..b46650cfb0f6 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidRootUtil.java @@ -178,6 +178,12 @@ public class AndroidRootUtil { return path != null ? LocalFileSystem.getInstance().findFileByPath(path) : null; } + @Nullable + public static VirtualFile getBuildconfigGenDir(@NotNull AndroidFacet facet) { + final String path = getBuildconfigGenSourceRootPath(facet); + return path != null ? LocalFileSystem.getInstance().findFileByPath(path) : null; + } + // works even if there is no Android facet in a module @Nullable @@ -355,14 +361,25 @@ public class AndroidRootUtil { @Nullable public static String getRenderscriptGenSourceRootPath(@NotNull AndroidFacet facet) { - // todo: return correct path for mavenized module + // todo: return correct path for mavenized module when it'll be supported + return getDefaultGenSourceRoot(facet); + } + + @Nullable + public static String getBuildconfigGenSourceRootPath(@NotNull AndroidFacet facet) { + // todo: return correct path for mavenized module when it'll be supported + return getDefaultGenSourceRoot(facet); + } + + @Nullable + private static String getDefaultGenSourceRoot(AndroidFacet facet) { final VirtualFile mainContentRoot = getMainContentRoot(facet); final String moduleDirPath = mainContentRoot != null ? mainContentRoot.getPath() : null; return moduleDirPath != null ? moduleDirPath + '/' + SdkConstants.FD_GEN_SOURCES : null; } - + @Nullable public static VirtualFile getMainContentRoot(@NotNull AndroidFacet facet) { final Module module = facet.getModule();