I am working with Android and Kotlin and I would like to have an annotation to check if a constant String
parameter (to a function or constructor) matches a certain pattern (regex). I read about the Pattern Annotation, but am not sure if it applies to my problem and if it is available with Android.
So when i would have code like this
fun foo(@MatchesPattern("a*b") bar: String) = println(bar)
then
foo("aaaab")
should compile just fine, but
foo("bb")
shouldn't.
Is this possible, preferably without any third party libraries? If yes, how would I implement an annotation like that? (sorry, I'm not familiar with writing my own custom annotations)
I would like to pass words with hyphen-separated syllables as params, however a word should not have more than 3 syllables (that means max. 2 hyphens per word). I'm aware I could also achieve this with default params, but I think an annotation would be a more elegant way to achieve this.
It's not quite as straightforward in Android as it would be otherwise because you will have to make a new project (java library) where you can put your annotation. The process looks something like this (not tested).
Create a new java library with android studio as a module in your project (select your project root, right click, new module). This will add a folder, for example lib if you don't change its default, with the class you specify, such as MyCustomAnnotationProcessor
.
In your app directory, go to the build.gradle
and modify it to include sourceCompatibility and targetCompatibility like so:
android {
...
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
Then do almost the same thing in your lib's build.gradle
file (if not already present after rebuilding at step 2)
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
In the lib folder, create a new class for the annotation (an @interface
) like so:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PARAMETER)
public @interface PatternMatches {
String value() default ".*";
}
Modify your annotation processor created in step 1. Note that this extends javax.annotation.processing.AbstractProcessor
, which is only available here because we're in a java library.
@SupportedAnnotationTypes("your.package.PatternMatches")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyCustomAnnotationProcessor extends AbstractProcessor {
private ProcessingEnvironment processingEnv;
@Override
public synchronized void init(ProcessingEnvironment env){
processingEnv = env;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(PatternMatches.class);
Set<VariableElement> params = ElementFilter.fieldsIn(elements);
for (VariableElement param : params) {
String val = param.getConstantValue();
String regex = param.getAnnotation(PatternMatches.class).value();
if (!val.matches(regex)) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR, "Regex match failed", param);
}
}
return false;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With