The problem was that I couldn't force Android Studio to respect @ParametersAreNonnullByDefault in all subpackages (i.e. recursively for all java files in com.company.name)
I wrote a gradle script, however, to check whether package-info.java files are generated inside each of the subpackage folders and create it if necessary. The script runs just before assembleDebug task.
So it turns out it is possible to force @ParametersAreNonnullByDefault annotation on all java classes in your project.
Download the source here. Remember to replace line 19 with your package name.
Usage:
apply plugin: 'com.android.application'
apply from: 'nonnull.gradle'
android {
compileSdkVersion 24
buildToolsVersion "24.0.2"
[...]
}
Remember to include package-info.java files in .gitignore
//File: .gitignore
package-info.java
Checkstyle rule:
<module name="JavadocPackage"/>
checks whether package-info.java is included in each subpackage.
Source:
/**
* File: nonnull.gradle
*
* Generates package-info.java for appropriate packages
* inside src/main/java folder.
*
* This is a workaround to define @ParametersAreNonnullByDefault for all Java classes in a package
* i.e. including all subpackages (note: edit package name in line no. 19).
*/
task generateNonNullJavaFiles(dependsOn: "assembleDebug", type: Copy) {
group = "Copying"
description = "Generate package-info.java classes"
def infoFileContentHeader = getFileContentHeader();
def infoFileContentFooter = getFileContentFooter();
def sourceDir = file( "${projectDir}" + File.separatorChar + "src" + File.separatorChar +
"main" + File.separatorChar + "java" + File.separatorChar +
"com" + File.separatorChar + "company" + File.separatorChar + "name" )
sourceDir.eachDirRecurse { dir ->
def infoFilePath = dir.getAbsolutePath() + File.separatorChar + "package-info.java"
if (!file(infoFilePath).exists()) {
def infoFileContentPackage = getFileContentPackage(dir.getAbsolutePath());
new File(infoFilePath).write(infoFileContentHeader +
infoFileContentPackage + infoFileContentFooter)
println "[dir] " + infoFilePath + " created";
}
}
println "[SUCCESS] NonNull generator: package-info.java files checked"
}
def getFileContentPackage(path) {
def mainSrcPhrase = "src" + File.separatorChar + "main" + File.separatorChar +
"java" + File.separatorChar
def mainSrcPhraseIndex = path.indexOf(mainSrcPhrase)
def output = path.substring(mainSrcPhraseIndex)
// Win hotfix
if (System.properties['os.name'].toLowerCase().contains('windows')) {
output = output.replace("\\", "/")
mainSrcPhrase = mainSrcPhrase.replace("\\", "/")
}
return "package " + output.replaceAll(mainSrcPhrase, "").replaceAll(
"/", ".") + ";\n"
}
def getFileContentHeader() {
return "/** javadoc goes here \n */\n" +
"@ParametersAreNonnullByDefault\n" +
"@ReturnValuesAreNonnullByDefault\n"
}
def getFileContentFooter() {
return "\n" +
"import javax.annotation.ParametersAreNonnullByDefault;\n" +
"\n" +
"import edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault;"
}
Extended version which generates package info files for src main, test & androidTest folders can be found here.
Findbugs & Lint:
It does work with findbugs & lint.