Setting nullValueMappingStrategy on the mapper / mapper config level for categories of mappings - mapstruct

MapStruct documentation has the following to say about the sensible defaults chosen for NullValueMappingStrategy.RETURN_DEFAULT:
Bean mappings: an 'empty' target bean will be returned, with the
exception of constants and expressions, they will be populated when
present.
Primitives: the default values for primitives will be returned, e.g.
false for boolean or 0 for int.
Iterables / Arrays: an empty iterable will be returned.
Maps: an empty map will be returned.
The problem is, we want to be able specify on the #Mapper level that, e.g., Iterables should have NullValueMappingStrategy.RETURN_DEFAULT but not primitives. The reason for this is that an empty iterable is a sensible default for our use case, but 0 is not a sensible default for int. We'd prefer not to have to, e.g., declare:
#IterableMapping(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
for every iterable we are mapping.
Does MapStruct provide a clean way to do this that I am not finding in the documentation?

I think the above line is actually a copy-paste error in the documentation. To make myself clear: the NullValueMappingStrategy only applies to arguments of mapping methods. Not to bean properties. It is not possible to define a primitive argument as source in a bean mapping method.
It is however possible to define non-beans as source of a bean mapping method. Like the mapFrom method below:
package org.mapstruct.ap.test.bugs._xyz;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.NullValueMappingStrategy;
import org.mapstruct.factory.Mappers;
#Mapper( nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
public interface XyzMapper {
XyzMapper INSTANCE = Mappers.getMapper(XyzMapper.class);
Target map(Source source);
Target mapFrom( Integer myInt, List<Integer> myList);
class Source {
private int myInt;
private List<Integer> myList;
public int getMyInt() {
return myInt;
}
public void setMyInt(int myInt) {
this.myInt = myInt;
}
public List<Integer> getMyList() {
return myList;
}
public void setMyList(List<Integer> myList) {
this.myList = myList;
}
}
class Target {
private int myInt;
private List<Integer> myList;
public int getMyInt() {
return myInt;
}
public void setMyInt(int myInt) {
this.myInt = myInt;
}
public List<Integer> getMyList() {
return myList;
}
public void setMyList(List<Integer> myList) {
this.myList = myList;
}
}
}
When using the NullValueMappingStrategy.RETURN_DEFAULT the mapFrom will return an empty Target just as is specified in the documentation.
This line should be removed from the documentation in relation to the NullValueMappingStrategy: Primitives: the default values for primitives will be returned, e.g. false for boolean or 0 for int.

Related

this singleton has way can be improved?

i was using google's singleton but this must need too many reference.
example, when I have to use another class in my Player class that used singleton, I must be using reference three time. Like this : Player.instance.another.blank=0;
my singleton
public static Player instance;
public void Awake()
{
if(instance ==null){
instance=this;
}
else
{
if(instance!=this){
Destroy(this.gameObject);
}
}
Is there any reason to destroy the instance? Even so, we are not updating the existing instance immediately after destroying it whenever a player is added.
I have a singleton Gist that I usually use: https://gist.github.com/xepherys/34d3d5ce3f44749e8649a25b38127347
It has decent comments for anyone unfamiliar with singletons, and is threadsafe. You can remove everything except the lazy field and the constructor region. I use this as the basis for Manager classes.
using System;
// Update namespace as needed
namespace WhatsYourName
{
/*
This is the name of your threadsafe Singleton - change "SingletonLazyThreadsafe" to value that makes sense, and be sure to use your
editors [Rename] option, or update all values to match.
Just because the Singleton itself is threadsafe does not mean that all methods that might be contained are automatically threadsafe.
If threading is important, use threadsafe variables, such as:
System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue>
https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2
rather than:
System.Collections.Generic.Dictionary<TKey,TValue>
https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2
Alternatively, lock() can be used in a pinch, but there is the potential for slight performance hits.
Any field, property, or method not marked with "// REQUIRED" means that it's just a sample and can be removed or changed as needed.
Comments are inline as a reminder and as a point of education for those not familiar with Singletons.
Initial snippet added 12/08/2018 - JSW (Xepherys).
*/
public class SingletonLazyThreadsafe
{
#region Fields
// Private
private static readonly Lazy<SingletonLazyThreadsafe> lazy = new Lazy<SingletonLazyThreadsafe>(() => new SingletonLazyThreadsafe()); // REQUIRED
private int changeCount;
private int myInteger;
private string myString;
// Public
public char MyPublicChar; // Note: Even though it's a field, if it's publicly accessible, I generally capitalize the first letter. This is a personal design choice. Most folk tend to use lowercase for fields regardless of their accessibility level.
#endregion
#region Properties
// Note: Private getter/setter for private field.
private int ChangeCount
{
get
{
return this.changeCount;
}
set
{
this.changeCount = value;
}
}
// Note: Public getter/setter for private field.
public int MyInteger
{
get
{
return this.myInteger;
}
set
{
this.myInteger = value;
}
}
// Note: Public getter / protected setter for private field. This allows a {get} from anywhere, but only a {set} from inside the class or derived classes.
public string MyString
{
get
{
return this.myString;
}
protected set
{
this.myString = value;
}
}
#endregion
#region Constructors
private SingletonLazyThreadsafe() // REQUIRED
{ }
public static SingletonLazyThreadsafe Instance // REQUIRED
{
get
{
return lazy.Value;
}
}
#endregion
#region Methods
// Note: This is a public method that just changes the myInteger field. It's useless since the property is public, but it's just an example. It also call IncreaseCount().
public void IncrementInteger(int value)
{
this.MyInteger = value;
IncreaseCount();
}
// Note: This is a public method that just changes the myString field. It's useless since the property is public, but it's just an example. It also call IncreaseCount().
public void ChangeString(string value)
{
this.MyString = value;
IncreaseCount();
}
// Note: This is a private method, which means it can only be called by other methods in this class, and not publicly or outside of the class. While it could directly change
// 'changeCount', I also have it making changes via the private 'ChangeCount' property, which is also only accessible inside the class.
private void IncreaseCount()
{
this.ChangeCount++;
}
#endregion
}
}

Mapping a field using existing target value (Mapstruct)

i have a custom case that some of my dto's have a field of type X, and i need to map this class to Y by using a spring service method call(i do a transactional db operation and return an instance of Y). But in this scenario i need to use existing value of Y field. Let me explain it by example.
// DTO
public class AnnualLeaveRequest {
private FileInfoDTO annualLeaveFile;
}
//ENTITY
public class AnnualLeave {
#OneToOne
private FileReference annualLeaveFile;
}
#Mapper
public abstract class FileMapper {
#Autowired
private FileReferenceService fileReferenceService;
public FileReference toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) {
return fileReferenceService.updateFile(fileInfoDTO, fileReference);
}
}
//ACTUAL ENTITY MAPPER
#Mapper(uses = {FileMapper.class})
public interface AnnualLeaveMapper {
void updateEntity(#MappingTarget AnnualLeave entity, AnnualLeaveRequest dto);
}
// WHAT IM TRYING TO ACHIEVE
#Component
public class MazeretIzinMapperImpl implements tr.gov.hmb.ikys.personel.izinbilgisi.mazeretizin.mapper.MazeretIzinMapper {
#Autowired
private FileMapper fileMapper;
#Override
public void updateEntity(AnnualLeave entity, AnnualLeaveUpdateRequest dto) {
entity.setAnnualLeaveFile(fileMapper.toFileReference(dto.getAnnualLeaveFile(), entity.getAnnualLeaveFile()));
}
}
But mapstruct ignores the result of "FileReference toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) " and does not map the result of it to the actual entity's FileReference field. Do you have any idea for resolving this problem?
Question
How do I replace the annualLeaveFile property while updating the AnnualLeave entity?
Answer
You can use expression to get this result. For example:
#Autowired
FileMapper fileMapper;
#Mapping( target = "annualLeaveFile", expression = "java(fileMapper.toFileReference(entity.getAnnualLeaveFile(), dto.getAnnualLeaveFile()))" )
abstract void updateEntity(#MappingTarget AnnualLeave entity, AnnualLeaveRequest dto);
MapStruct does not support this without expression usage. See the end of the Old analysis for why.
Alternative without expression
Instead of fixing it in the location where FileMapper is used, we fix it inside the FileMapper itself.
#Mapper
public abstract class FileMapper {
#Autowired
private FileReferenceService fileReferenceService;
public void toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) {
FileReference wanted = fileReferenceService.updateFile(fileInfoDTO, fileReference);
updateFileReference(fileReference, wanted);
}
// used to copy the content of the service one to the mapstruct one.
abstract void updateFileReference(#MappingTarget FileReference fileReferenceTarget, FileReference fileReferenceFromService);
}
Old analysis
The following is what I notice:
(Optional) your FileMapper class is not a MapStruct mapper. This can just be a normal class annotated with #Component, since it does not have any unimplemented abstract methods. (Does not affect code generation of the MazeretIzinMapper implementation)
(Optional, since you have this project wide configured) you do not have componentModel="spring" in your #Mapper definition, maybe you have this configured project wide, but that is not mentioned. (required for the #Autowired annotation, and #Component on implementations)
Without changing anything I already get a working result as you want it to be, but for non-update methods (not listed in your question, but was visible on the gitter page where you also requested help) the FileMapper as is will not be used. It requires an additional method that takes only 1 argument: public FileReference toFileReference(FileInfoDTO fileInfoDTO)
(Edit) to get rid of the else statement with null value handling you can add nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE to the #Mapper annotation.
I've run a test and with 1.5.0.Beta2 and 1.4.2.Final I get the following result with the thereafter listed FileMapper and MazeretIzinMapper classes.
Generated mapper implementation
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-03-11T18:01:30+0100",
comments = "version: 1.4.2.Final, compiler: Eclipse JDT (IDE) 1.4.50.v20210914-1429, environment: Java 17.0.1 (Azul Systems, Inc.)"
)
#Component
public class MazeretIzinMapperImpl implements MazeretIzinMapper {
#Autowired
private FileMapper fileMapper;
#Override
public AnnualLeave toEntity(AnnualLeaveRequest dto) {
if ( dto == null ) {
return null;
}
AnnualLeave annualLeave = new AnnualLeave();
annualLeave.setAnnualLeaveFile( fileMapper.toFileReference( dto.getAnnualLeaveFile() ) );
return annualLeave;
}
#Override
public void updateEntity(AnnualLeave entity, AnnualLeaveRequest dto) {
if ( dto == null ) {
return;
}
if ( dto.getAnnualLeaveFile() != null ) {
if ( entity.getAnnualLeaveFile() == null ) {
entity.setAnnualLeaveFile( new FileReference() );
}
fileMapper.toFileReference( entity.getAnnualLeaveFile(), dto.getAnnualLeaveFile() );
}
}
}
Source classes
Mapper
#Mapper( componentModel = "spring", uses = { FileMapper.class }, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE )
public interface MazeretIzinMapper {
AnnualLeave toEntity(AnnualLeaveRequest dto);
void updateEntity(#MappingTarget AnnualLeave entity, AnnualLeaveRequest dto);
}
FileMapper component
#Mapper
public abstract class FileMapper {
#Autowired
private FileReferenceService fileReferenceService;
public FileReference toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) {
return fileReferenceService.updateFile( fileInfoDTO, fileReference );
}
public FileReference toFileReference(FileInfoDTO fileInfoDTO) {
return toFileReference( new FileReference(), fileInfoDTO );
}
// other abstract methods for MapStruct mapper generation.
}
Why the exact wanted code will not be generated
When generating the mapping code MapStruct will use the most generic way to do this.
An update mapper has the following criteria:
The #MappingTarget annotated argument will always be updated.
It is allowed to have no return type.
the generic way to update a field is then as follows:
// check if source has the value.
if (source.getProperty() != null) {
// Since it is allowed to have a void method for update mappings the following steps are needed:
// check if the property exists in the target.
if (target.getProperty() == null) {
// if it does not have the value then create it.
target.setProperty( new TypeOfProperty() );
}
// now we know that target has the property so we can call the update method.
propertyUpdateMappingMethod( target.getProperty(), source.getProperty() );
// The arguments will match the order as specified in the other update method. in this case the #MappingTarget annotated argument is the first one.
} else {
// default behavior is to set the target property to null, you can influence this with nullValuePropertyMappingStrategy.
target.setProperty( null );
}

Is it possible to pass arguments in the expression of a PropertyModel?

I have a model object that has a getter/setter that accepts a String.
public String getStringValue(String key)
I need to know if it is possible to use that getter with a PropertyModel and if so how do I do it? An example might look something like this:
new PropertyModel<String>(myObj, "StringValue[key]");
There isn't built in way to do it. But you can define your own Wicket Model to do it via reflection.
For example:
public class FunctionReflectionReadOnlyModel<T, R> extends AbstractReadOnlyModel<T> {
private Object object;
private String functionName;
private R key;
private Class<R> keyClass;
public FunctionReflectionReadOnlyModel(Object object, String expression, Class<R> keyClass) {
this.object = object;
this.functionName = getFunctionName(expression);
this.key = getKey(expression);
this.keyClass = keyClass;
}
#Override
public T getObject() {
try {
Method method = object.getClass().getMethod(functionName, keyClass);
return (T)method.invoke(object, key);
} catch (Exception ex) {
//process exception
return null;
}
}
}
You just need implement getFunctionName(String expression) and getKey(String expression) on your needs.
But I think that is better use another variant. It's not particularly what you ask, but it is typified. Also required Java 8.
public class FunctionWithKeyReadOnlyModel<T, R> extends AbstractReadOnlyModel<T> {
private Function<R, T> function;
private R key;
public FunctionWithKeyReadOnlyModel(Function<R, T> function, R key) {
this.function = function;
this.key = key;
}
#Override
public T getObject() {
return function.apply(key);
}
}
And then you can use it like this:
new FunctionWithKeyReadOnlyModel(obj::getStringValue, "key");
I've read about usage only PropertyModel too late. In this case you can inherit your class from PropertyModel and change getModel/setModel like in example FunctionReflectionReadOnlyModel. So you don't need change other classes API. But if you want all features of PropertyModel (nested objects) you need implement it.
As answered by #merz this is not supported by Wicket's PropertyModel, actually by PropertyResolver.
PropertyResolver supports such access if you use a java.util.Map:
public Map<String, String> getProperty() {return theMap;}
Check org.apache.wicket.core.util.lang.PropertyResolver's javadoc.

Morphia converter calling other converters

I want to convert Optional<BigDecimal> in morphia. I created BigDecimalConverter, and it works fine. Now I want to create OptionalConverter.
Optional can hold any object type. In my OptionalConverter.encode method I can extract underlying object, and I'd like to pass it to default mongo conversion. So that if there is string, I'll just get string, if there is one of my entities, I'll get encoded entity. How can I do it?
There are two questions:
1. How to call other converters?
2. How to create a converter for a generic class whose type parameters are not statically known?
The first one is possible by creating the MappingMongoConveter and the custom converter together:
#Configuration
public class CustomConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
// ...
}
#Override
#Bean
public Mongo mongo() throws Exception {
// ...
}
#Override
#Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter mmc = new MappingMongoConverter(
mongoDbFactory(), mongoMappingContext());
mmc.setCustomConversions(new CustomConversions(CustomConverters
.create(mmc)));
return mmc;
}
}
public class FooConverter implements Converter<Foo, DBObject> {
private MappingMongoConverter mmc;
public FooConverter(MappingMongoConverter mmc) {
this.mmc = mmc;
}
public DBObject convert(Foo foo) {
// ...
}
}
public class CustomConverters {
public static List<?> create(MappingMongoConverter mmc) {
List<?> list = new ArrayList<>();
list.add(new FooConverter(mmc));
return list;
}
}
The second one is much more difficult due to type erasure. I've tried to create a converter for Scala's Map but haven't found a way. Unable to get the exact type information for the source Map when writing, or for the target Map when reading.
For very simple cases, e.g. if you don't need to handle all possible parameter types, and there is no ambiguity while reading, it may be possible though.

Forcing the use of a specific overload of a method in C#

I have an overloaded generic method used to obtain the value of a property of an object of type PageData. The properties collection is implemented as a Dictionary<string, object>. The method is used to avoid the tedium of checking if the property is not null and has a value.
A common pattern is to bind a collection of PageData to a repeater. Then within the repeater each PageData is the Container.DataItem which is of type object.
I wrote the original extension method against PageData:
public static T GetPropertyValue<T>(this PageData page, string propertyName);
But when data binding, you have to cast the Container.DataItem to PageData:
<%# ((PageData)Container.DataItem).GetPropertyValue("SomeProperty") %>
I got a little itch and wondered if I couldn't overload the method to extend object, place this method in a separate namespace (so as not to pollute everything that inherits object) and only use this namespace in my aspx/ascx files where I know I've databound a collection of PageData. With this, I can then avoid the messy cast in my aspx/ascx e.g.
// The new overload
public static T GetPropertyValue<T>(this object page, string propertyName);
// and the new usage
<%# Container.DataItem.GetPropertyValue("SomeProperty") %>
Inside the object version of GetPropertyValue, I cast the page parameter to PageData
public static T GetPropertyValue<T>(this object page, string propertyName)
{
PageData data = page as PageData;
if (data != null)
{
return data.GetPropertyValue<T>(propertyName);
}
else
{
return default(T);
}
}
and then forward the call onto, what I would expect to be PageData version of GetPropertyValue, however, I'm getting a StackOverflowException as it's just re-calling the object version.
How can I get the compiler to realise that the PageData overload is a better match than the object overload?
The extension method syntax is just syntactic sugar to call static methods on objects. Just call it like you would any other regular static method (casting arguments if necessary).
i.e.,
public static T GetPropertyValue<T>(this object page, string propertyName)
{
PageData data = page as PageData;
if (data != null)
{
//will call the GetPropertyValue<T>(PageData,string) overload
return GetPropertyValue<T>(data, propertyName);
}
else
{
return default(T);
}
}
[edit]
In light of your comment, I wrote a test program to see this behavior. It looks like it does go with the most local method.
using System;
using Test.Nested;
namespace Test
{
namespace Nested
{
public static class Helper
{
public static void Method(this int num)
{
Console.WriteLine("Called method : Test.Nested.Helper.Method(int)");
}
}
}
static class Helper
{
public static void Method(this object obj)
{
Console.WriteLine("Called method : Test.Helper.Method(object)");
}
}
class Program
{
static void Main(string[] args)
{
int x = 0;
x.Method(); //calls the object overload
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
Console.WriteLine();
}
}
}
To make sure the nesting is not affecting anything, tried this also removing the object overload:
using System;
using Test.Nested;
namespace Test
{
namespace Nested
{
public static class Helper
{
public static void Method(this int num)
{
Console.WriteLine("Called method : Test.Nested.Helper.Method(int)");
}
}
}
static class Helper
{
public static void Method(this string str)
{
Console.WriteLine("Called method : Test.Helper.Method(string)");
}
}
class Program
{
static void Main(string[] args)
{
int x = 0;
x.Method(); //calls the int overload
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
Console.WriteLine();
}
}
}
Sure enough, the int overload is called.
So I think it's just that, when using the extension method syntax, the compiler looks within the current namespace first for appropriate methods (the "most local"), then other visible namespaces.
It should already be working fine. I've included a short but complete example below. I suggest you double-check your method signatures and calls, and if you're still having problems, try to come up with a similar short-but-complete program to edit into your question. I suspect you'll find the answer while coming up with the program, but at least if you don't, we should be able to reproduce it and fix it.
using System;
static class Extensions
{
public static void Foo<T>(this string x)
{
Console.WriteLine("Foo<{0}>(string)", typeof(T).Name);
}
public static void Foo<T>(this object x)
{
Console.WriteLine("Foo<{0}>(object)", typeof(T).Name);
string y = (string) x;
y.Foo<T>();
}
}
class Test
{
static void Main()
{
object s = "test";
s.Foo<int>();
}
}