MapStruct Custom EnumTransformationStrategy - java

I am a bit confused looking at the documentation on how to use a custom transformation strategy for enums. I am doing this in Kotlin, but a Java solution is fine too.
I am using 14.2
I have this
package org.mapstruct.custom.spi
import org.mapstruct.ap.spi.EnumTransformationStrategy
class StripLowerCaseEnumTransformationStrategy : EnumTransformationStrategy {
companion object {
const val NAME = "stripLowerCase"
}
override fun getStrategyName(): String {
return NAME
}
override fun transform(value: String, configuration: String): String {
return value.removePrefix(configuration).toLowerCase()
}
}
But when I try to use it within my EnumMappers, it says it cannot be found. What step am I missing?
#Mapper
interface EnumMapper {
#ValueMappings(
ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL),
ValueMapping(source = "PERSON_STATUS_INVALID", target = MappingConstants.NULL)
)
#EnumMapping(
nameTransformationStrategy = StripLowerCaseEnumTransformationStrategy.NAME,
configuration = "PERSON_STATUS_"
)
fun fromProto(status: PersonStatusProto): PersonStatus
#InheritInverseConfiguration
fun toProto(status: PersonStatus): PersonStatusProto
}
Here is the error: error: There is no registered EnumTransformationStrategy for 'stripLowerCase'. Registered strategies are: prefix, stripPrefix, stripSuffix, suffix.
I also have created the file resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy with the value org.mapstruct.custom.spi.StripLowerCaseEnumTransformationStrategy.
Here is also a picture of directory in case it is organized incorrectly:

The reason why the configured EnumTransformationStrategy is not used is because SPIs which are available only in the same compilation unit are not used.
In order for this to work you will need to move the implementation in a different module (not package) and make it available during the compilationl
An example for how this can be done with maven can be seen in this integration test.
Note: I see that you are placing the StripLowerCaseEnumTransformationStrategy under the org.mapstruct package. I would advise not to use packages that you don't own for placing your code, otherwise you might have problems with split packages when running on the Java module path.

Related

Assertj-Generator maven plugin custom variable

I was wondering if there is a way to add a custom variable when using the assertj-assertions-generator-maven-plugin. I would like to introduce a variable in the maven pom that would be the entry point class name. What I have found in multi-module projects using the default EntryPointClass is that you end up with multiple classes called Assertions. This gets confusing if using test jars between modules in a project. What I have done in the past is have a custom assertionsEntryPointClass like the below. However, what I would really like is to be able to define a variable in the pom (say AssertionsEntryPointClassName) that I could use in the template.
Here is what I use today:
package ${package};
#javax.annotation.Generated(value="assertj-assertions-generator")
public class MyModuleAssertions {
${all_assertions_entry_points}
protected MyModuleAssertions() {
// empty
}
}
What I would like to have would be this:
package ${package};
#javax.annotation.Generated(value="assertj-assertions-generator")
public class ${AssertionsEntryPointClassName}Assertions {
${all_assertions_entry_points}
protected ${AssertionsEntryPointClassName}Assertions() {
// empty
}
}

What's the benefit of using the Gradle Property type in a Gradle plugin oppose to just using String type for example?

In a Gradle plugin I could define my properties as:
open class ExamplePluginExtension {
var exampleName: String? = null
}
And accessing it in build.gradle.kts like so:
example {
exampleName = "Lorem Ipsum"
}
Or 2. I can use the Property type of Gradle (org.gradle.api.provider.Property):
open class ExamplePluginExtension #Inject constructor(objectFactory: ObjectFactory) {
val exampleName: Property<String> = objectFactory.property(String::class.java)
}
Accessing it in build.gradle.kts like so:
example {
exampleName.set("Lorem Ipsum")
}
The Gradle documentation promotes the use of the Property type in Gradle Plugins, but why? The use of the Property type is quite a hassle, creates a lot of boilerplate and disables the use of nullable properties. I see many third-party libraries such as Flyway use the first option, completely ignoring the Property type.

How to inspect/read a Java file inside a Gradle plugin

I am writing a Gradle plugin that needs to scan Kotlin files and find if there is a certain interface included in the class. For example, with this snippet of code:
class MyClass {
interface MyInterface {
fun doSomething()
}
}
my plugin would print on the console that the interface MyInterface was found, and for this snippet:
class MySecondClass {}
would not print anything.
I have successfully created the plugin structure and a DefaultTask like this, where I get the file that needs to be inspected from the user input (written in Kotlin):
open class MyGradleTas : DefaultTask() {
#InputFile lateinit var inputFile: File
#OutputDirectory lateinit var outputDirectory: File
#TaskAction
fun run() {
// How can I inspect the Java/Kotlin code inside the inputFile object
}
}
How can I inspect the inputFile File object? Is there a way to transform it to UAST or PSI? If so, how?
So I found this project: http://javaparser.org/ that I believe it will put me in the right direction, but any suggestions are welcome, since I do not know if this is the preferred/right approach
I agree with your thought that using a parser is the correct way to approach this. I found a discussion of parsing Kotlin that has pointers to 2 projects on GitHub that use antlr4 grammars to parse Kotlin. The second project appears to be basically a copy of the first, but provides slightly more useful examples of how to use it.

Groovy global storing of closures from different scripts

Is it to possible to store closures from different groovy scripts?
Let's say I have some kind of class that should store the closures:
package com.test
class ClosureContainer {
static closures = [:]
static def AddClosure(String name, Closure cl) {
println "Adding closure named ${name}"
closures[name] = cl
}
}
And then I would like to have groovy scripts that would add closures to it:
import com.test.ClosureContainer as container
container.AddClosure("Interesting stuff", {
println "I'm doing some interesting stuff"
})
And later, I should be able to call it like:
def closureCode = ClosureContainer.closures["Interesting stuff"]
closureCode()
What is the best approach to do it in Groovy? I'm not sure how to handle invoking of the scripts because classes are generated from these scripts.
I'm able to create instances of the scripts during runtime, but I'm not able to retrieve list of the classes/scripts without hardcoding it.
UPDATE:
Let's suppose I have testScript.groovy in package com.test.scripts that adds a few closures to the container. I tried to let the gradle generate classes from the scripts and create instance in the code like this:
def className = Class.forName("com.test.scripts.testScript")
def instance = className.newInstance()
instance.run()
And I'm hardcoding the testScript name. But there will be a lot of scripts and I should be able to retrieve it dynamically.
The recommended way to run groovy script is to use GroovyScriptEngine:
String[] path = new String[] { "." };
GroovyScriptEngine engine = new GroovyScriptEngine(path);
engine.run("yoursriptname.groovy", new Binding())
NB: yoursriptname should be the path of your script relative to path.
If you want to pass bindings (arguments and then get the results) you have to use Binding.

how to access internal properties from java test code

I've got some class with property marked as internal.
Then I try to set that property from test code which is in java.
How can I access those properties? test code and class code are in the same package.
example:
class MainActivity : AppCompatActivity() {
interal var someProperty = "test"
}
test code:
#Test
public void firstStartTest() {
val activity = MainActivity()
activity.setSomeProperty("something") //does not compile
}
Android Studio is suggesting activity.setSomeProperty$production_sources_for_module_app();
but this also does not compile.
Both classes (MainActivity and test class) must be in one module. This is a module definition:
More specifically, a module is a set of Kotlin files compiled together:
an IntelliJ IDEA module;
a Maven or Gradle project;
a set of files
compiled with one invocation of the Ant task.
https://kotlinlang.org/docs/reference/visibility-modifiers.html
It means, check your project structure.
add #JvmField annotation.
It treats variable as java protected
There are two ways of doing this:
Make the property protected. Note on how Java & Kotlin treat protected differently. In Java it's possible that other classes in the same package access protected members. Thus your test class (in Java) can access it.
Access the property via its ugly name. It should be sort of like activity.setSomeProperty$production_.... Make use the autocomplete. From the documentation:
Members of internal classes go through name mangling, to make it
harder to accidentally use them from Java and to allow overloading for
members with the same signature that don't see each other according to
Kotlin rules;

Categories

Resources