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