Fork me on GitHub

Active Annotations

Active annotations allow developers to participate in the translation process of SARL source code to Java. An active annotation is just an annotation that is processed by a specific annotation processor during the compilation of a SARL program. Depending on the meaning of the active annotation, the generated Java code could be completed by the annotation processor.

SARL comes with ready-to-use active annotations for common code patterns. The following sections describe there annotations.

1. @Accessors

If you want to add getter and or setter methods for your fields, @Accessors is your friend. This annotation can be applied to either object-oriented types and several agent-oriented types. The agent-oriented types in which you could uses the @Accessors annotation are the agents, the behaviors and the skills.

Let's a basic example.

@Accessors var name : String

It is compiled to the code:

private var name : String

public def getName : String {
            this.name
}

public def setName(name : String) : void {
  this.name = name
}

By default, a public getter and a public setter method is created. The @Accessors can be configured to tell that you only want one or the other, and to change the visibility. This is done by passing one or more values of type AccessorType for representing the visibility categories as parameters to the annotation: PUBLIC_GETTER, PROTECTED_GETTER, PACKAGE_GETTER, PRIVATE_GETTER, PUBLIC_SETTER, PROTECTED_SETTER, PACKAGE_SETTER, PRIVATE_SETTER, NONE.

You can also use the annotation on class level to do the same for all fields.

Here is a more complex example, that shows how it works:

@Accessors class Person {
  var name : String
  var firstName : String
  @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) var age : int
  @Accessors(NONE) var internalField : String
}

It is compiled to the code:

class Person {
  private var name : String
  private var firstName : String
  private var age : int
  private var internalField : String

  public def getName : String {
    this.name
  }

  public def setName(name : String) : void {
    this.name = name
  }

  public def getFirstName : String {
    this.firstName
  }

  public def setFirstName(firstName : String) : void {
    this.firstName = firstName
  }

  public def getAge : int {
    this.age
  }

  protected def setAge(age : int) : void {
    this.age = age
  }
}

2.  @Data

The annotation @Data will turn an annotated class into a value object class. A class annotated with [:dataannon] is processed according to the following rules:

This annotation can be applied to object-oriented types. The agent-oriented types cannot be annotated.

Example:

@Data class Person {
  val firstName : String
  val lastName : String

  static def main(args : String*) {
    val p = new Person(args.get(0), args.get(1))
    println(p.getFirstName + ' ' + p.lastName)
  }
}

3.  @Delegate

The @Delegate annotation automatically generates delegate methods for all interfaces shared between the delegate and the currently implemented class. You can optionally restrict it to explicitly stated interfaces. This annotation can be applied to object-oriented types. The agent-oriented types cannot be annotated.

Let's start with a basic example:

interface SomeInterface {
    def function(param : String) : int
}
interface SubTypeOfSomeInterface extends SomeInterface {
    def anotherFunction
}
class MyClass implements SomeInterface {

  // generates all methods of SomeInterface and delegates to this field
  @Delegate var myDelegate : SubTypeOfSomeInterface

}

The previous code is equivalent to:

class MyClass implements SomeInterface {             
  var myDelegate : SubTypeOfSomeInterface

  def function(param : String) : int {
    return this.myDelegate.function(param)
  }
}

It is not only possible to delegate to fields, but also to methods so you could lazily create the delegate object or use a different one each time.

class MyClass implements SomeInterface {
    @Delegate def provideDelegate : SomeInterface {
        return new MyDelegate
    }
}

The previous code is equivalent to:

class MyClass implements SomeInterface {
    def function(param : String) : int {
        return provideDelegate().function(param)
    }

    def provideDelegate : SomeInterface {
        return new MyDelegate
    }
}

If you use a method, additional parameters could be declared, that will tell you about the method that should be invoked:

Let's the following example:

class MyClass implements SomeInterface {
    @Delegate def provideDelegate(methodName : String, parameterTypes : Class<?>[], arguments : Object[]) : SomeInterface {
        return new MyDelegate
    }
}

The previous code is equivalent to:

class MyClass implements SomeInterface {
    def function(param : String) : int {
        return provideDelegate(
            "function",
            #[typeof(String)],
            #[param]).function(param)
    }

    def provideDelegate(methodName : String, parameterTypes : Class<?>[], arguments : Object[]) : SomeInterface {
        return new MyDelegate
    }
}

4. @NoEqualityTestFunctionsGeneration

The @NoEqualityTestFunctionsGeneration annotation disables the generation the equality test functions, i.e. equals() and hashCode() from the field declarations.

By default, the SARL compiler generates the equality test functions from the type's fields. In several cases, this automatic behavior should be avoiding because the standard equality test that is provided by the Java run-time environment should be used. In this case, @NoEqualityTestFunctionsGeneration annotation may be used to mark a type or a field for being excluded of the equality test generation.

The annotation may mark a type, as in the following example. In this case, no equality test function is generated within the marked type and all its subtypes.

@NoEqualityTestFunctionsGeneration
class MyClass {
  var field1 : int
  var field2 : String
}

The annotation may mark a specific field in order to exclude it from the equality test generation. In the following example, the field2 field is marked with the annotation. Consequently, it is not included within the equality test within the equals() function, and the hash code replied by the hashCode() function does not include the hash code of the field2 field.

class MyClass {
  var field1 : int
  @NoEqualityTestFunctionsGeneration
  var field2 : String
}

5. @ToString

The @ToString annotation enables to generate the function that replies the string representation of an object, a.k.a. as the toString() function in a Java program. All non-static fields of the annotated class, and all of its superclasses are used for generating the toString() function. This annotation can be applied to object-oriented types. The agent-oriented types cannot be annotated.

Let's a basic example:

@ToString
class MyClass {
  var field1 : int
  var field2 : String
}

The previous code is equivalent to:

class MyClass {
  var field1 : int
  var field2 : String

  def toString() : String {
    var buffer = new ToStringBuilder(this)
    buffer.add("field1", this.field1);
    buffer.add("field2", this.field2);
    return buffer.toString
  }
}

For brevity there are options to the annotation to hide field names, skip fields with null values and print everything on one line.

6. Acknowledgements

This documentation is inspired by the documentations from the Xtext and Xtend projects.

Copyright © 2014-2019 the original authors or authors.

Licensed under the Apache License, Version 2.0; you may not use this file except in compliance with the License. You may obtain a copy of the License.

You are free to reproduce the content of this page on copyleft websites such as Wikipedia.

Generated with the translator io.sarl.maven.docs.generator 0.9.0.