MapStruct: map from multiple variables to a collection - mapstruct

I got the following situation:
Source class:
public class OutcodeStats {
double avgPrice;
double avgPricePsf;
double avgRent;
double avgYield;
double growth1y;
double growth3y;
double growth5y;
String outcode;
int salesPerMonth;
int turnover;
long effectiveDate;
}
target class:
public class GrowthStats {
public String status;
public String postcode;
public String postcode_type;
public String url;
public List<List<Object>> data;
public String process_time;
}
How do I instruct mapStruct to:
map growth1y, growth3y and growth5y in data?
ignore all the remaining fields
In other words, how to make this "pseudo" mapping working?
#Mapper
public interface OutcodeStatsMapper {
OutcodeStatsMapper INSTANCE = Mappers.getMapper(OutcodeStatsMapper.class);
#Mapping(source="outcode", target = "postcode")
#Mapping(source = "growth1y", target = "data")
#Mapping(source = "growth3y", target = "data")
#Mapping(source = "growth5y", target = "data")
GrowthStats outcodeStatsToGrowthStats(OutCodeStats stats);
}
Here an example of the source class:
"outcode":"BR1",
"avg_price":436061.3,
"avg_price_psf":0,
"avg_rent":295.2,
"avg_yield":"3.5%",
"growth_1y":"5.6%",
"growth_3y":"6.3%",
"growth_5y":"17.9%",
"sales_per_month":28,
"turnover":"4%"
and here what I would love to see in the mapped object:
"status":"success",
"postcode":"BR1",
"postcode_type":"outcode",
"url":"https:\/\/propertydata.co.uk\/draw?input=BR1",
"data":[
[
null,
null,
"3.5"
],
[
null,
null,
"5.6"
],
[
null,
null,
"6.3"
]
],
"process_time":"0.26"

Here the way I made it working:
#Mapper
public interface OutcodeStatsMapper {
OutcodeStatsMapper INSTANCE = Mappers.getMapper(OutcodeStatsMapper.class);
#Mapping(source = "outcode", target = "postcode")
GrowthStats outcodeStatsToGrowthStats(OutCodeStats stats);
#AfterMapping
default void fillData(OutCodeStats outCodeStats, #MappingTarget GrowthStats growthStats) {
growthStats.status = "success";
growthStats.postcode_type= "outcode";
growthStats.data = new ArrayList<>();
Stream.of(outCodeStats.growth1y, outCodeStats.growth3y, outCodeStats.growth5y)
.forEach(yearStats -> growthStats.data.add(List.of("null", "null", yearStats)));
}
}

Related

HAPI FHIR: how to read extension from custom datatype of standard resource?

I would like to add custom extension to Patient->telecom class by extending HAPI's ContactPoint class and adding new extension . So this is standard boilerplate code I took from https://hapifhir.io/hapi-fhir/docs/model/custom_structures.html
#DatatypeDef(name="MyContactPoint")
public class MyContactPoint extends ContactPoint implements ICompositeType {
private static final long serialVersionUID = 1L;
#Override
protected MyContactPoint typedCopy() {
MyContactPoint retVal = new MyContactPoint();
super.copyValues(retVal);
return retVal;
}
#Child(name="my-ext-value")
#Extension(definedLocally = false, isModifier = false, url = "http://someurl")
private BooleanType valueMyExtValue;
public Boolean getMyExtValue() {
return valueMyExtValue.getValue();
}
public MyContactPoint setMyExtValue(Boolean theValue) {
this.valueMyExtValue = new BooleanType(theValue);
return this;
}
}
And it works fine when I generate XML:
Patient data = new Patient();
MyContactPoint telecom = new MyContactPoint();
telecom.setMyExtValue(true);
data.addTelecom(telecom);
However, I cannot wrap my head how I can LOAD this data from XML to get strongly-typed MyContactPoint object? I know I can do telecom.getExtensionByUrl() etc and finally load it - but I was hoping there is a way to outsource this legwork to HAPI-FHIR by declaring extensions.
I know I can do it easily in "parent" profile like this:
#ResourceDef()
public class MyTask extends Task {
#Child(name="my-ext-value")
#Extension(definedLocally = false, isModifier = false, url = "http://someurl")
private BooleanType valueMyExtValue;
public Boolean getMyExtValue() {
return valueMyExtValue.getValue();
}
public MyTask setMyExtValue(Boolean theValue) {
this.valueMyExtValue = new BooleanType(theValue);
return this;
}
}
and then I load it as follows:
var fhirContext = FhirContext.forR4Cached();
var fhirParser = fhirContext.newXmlParser();
fhirParser.setPreferTypes(List.of(MyTask.class));
var res = fhirParser.parseResource(xml);
var myTask = (MyTask)res;
var theValue = myTask.getMyExtValue();
So that was easy for IBaseResource->custom extension flow. But how do I do this for IBaseResource->Custom DataType (overriding existing field!)->custom extension?

MapStruct use nested mapper based on input parameters

in this simple scenario I have a Pen that have a Color. I want to map the color of a pen using a nested mapper method instead of another, based on a variable input in the Pen mapper.
Color entity:
public class ColorEntity {
private Long colorId;
private String colorName;
}
Pen entity:
public class PenEntity {
private Long penId;
private String penArticle;
private ColorEntity color;
}
Color DTO:
public class ColorDTO {
private Long colorId;
private String colorName;
}
Pen DTO:
public class PenDTO {
private Long penId;
private String penArticle;
private ColorDTO color;
}
Color mapper: if I call ColorMapper.toDto(color, locale) the second method should be used.
#Mapper(componentModel = "spring")
public interface ColorMapper {
ColorDTO toDto(ColorEntity colorEntity);
default ColorDTO toDto(ColorEntity colorEntity, Locale locale) {
ColorDTO colorDTO = toDto(colorEntity);
if(locale.equals(Locale.ITALIAN)) {
colorDTO.setColorName("ROSSO");
}
else {
colorDTO.setColorName("Language not supported");
}
return colorDTO;
}
}
Pen mapper: same of ColorMapper, if I call PenMapper.toDto(pen, locale) the second method should be used.
#Mapper(componentModel = "spring", uses = {ColorMapper.class})
public interface PenMapper {
PenDTO toDto(PenEntity penEntity);
PenDTO toDto(PenEntity penEntity, Locale locale);
}
The problem is that in the generated PenMapperImpl, the method public PenDTO toDto(PenEntity penEntity, Locale locale) doesn't use the method ColorMapper.toDto(color, locale) to translate the color.
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-04-21T17:33:22+0200",
comments = "version: 1.4.2.Final, compiler: Eclipse JDT (IDE) 1.3.1200.v20200916-0645, environment: Java 15.0.2 (Oracle Corporation)"
)
#Component
public class PenMapperImpl implements PenMapper {
#Autowired
private ColorMapper colorMapper;
#Override
public PenDTO toDto(PenEntity penEntity) {
if ( penEntity == null ) {
return null;
}
PenDTO penDTO = new PenDTO();
penDTO.setColor( colorMapper.toDto( penEntity.getColor() ) );
penDTO.setPenArticle( penEntity.getPenArticle() );
penDTO.setPenId( penEntity.getPenId() );
return penDTO;
}
#Override
public PenDTO toDto(PenEntity penEntity, Locale locale) {
if ( penEntity == null && locale == null ) {
return null;
}
PenDTO penDTO = new PenDTO();
if ( penEntity != null ) {
// HERE SHOULD BE GENERATED THE
// penDTO.setColor( colorMapper.toDto( penEntity.getColor() , locale ) );
penDTO.setColor( colorMapper.toDto( penEntity.getColor() ) );
penDTO.setPenArticle( penEntity.getPenArticle() );
penDTO.setPenId( penEntity.getPenId() );
}
return penDTO;
}
}
How can I resolve this? I'm looking for a general solution (not puntual for this case) because I have a lot of this same situations in a project.
Thanks and regards
MapStruct does not use the input parameters when looking for methods in other mappers.
In order to achieve this you'll need to give some help to MapStruct.
The way to do this would be to use #Context. Using that MapStruct will propagate that to other mappers.
So in your case it will look something like:
#Mapper(componentModel = "spring")
public interface ColorMapper {
ColorDTO toDto(ColorEntity colorEntity);
default ColorDTO toDto(ColorEntity colorEntity, #Context Locale locale) {
ColorDTO colorDTO = toDto(colorEntity);
if(locale.equals(Locale.ITALIAN)) {
colorDTO.setColorName("ROSSO");
}
else {
colorDTO.setColorName("Language not supported");
}
return colorDTO;
}
}
#Mapper(componentModel = "spring", uses = {ColorMapper.class})
public interface PenMapper {
PenDTO toDto(PenEntity penEntity);
PenDTO toDto(PenEntity penEntity, #Context Locale locale);
}
This way when using the toDto with the Locale context MapStruct will choose the ColorMapper#toDto with the Locale.

How can I archive my arg pack in Ocean for Petrel?

I have a custom Ocean workstep in Petrel, but I cannot succeed in persisting my arguments package. My package contains PillarGrid objects (Gird, Property, Zone) as shown below :
[Archivable(Version = 1, Release = "2013.6")]
public class MyArguments : DescribedArgumentsByReflection, IIdentifiable
{
[Archived]
private Grid grid = Grid.NullObject;
[Archived]
private int seedNumber;
[Archived]
private int numberRealizations = 1;
[Archived]
private Zone regionZone = Zone.NullObject;
[Archived]
private Property property = Property.NullObject;
[Archived]
private Droid droid = Droid.Empty;
...
public DeeSseArguments()
: base()
{
// adding this to datasource
IDataSourceManager dsManager = DataManager.DataSourceManager;
string DataSourceId = DeeSseDataSourceFactory.DataSourceId;
StructuredArchiveDataSource dataSource = dsManager.GetSource(DataSourceId) as StructuredArchiveDataSource;
this.droid = dataSource.GenerateDroid();
dataSource.AddItem(this.droid, this);
}
}
I created a DataSourceFactory based on a StructuredArchiveDataSource :
public class MyDataSourceFactory : DataSourceFactory
{
public static string DataSourceId = "MyArgsPackId";
public override Slb.Ocean.Core.IDataSource GetDataSource()
{
return new StructuredArchiveDataSource(DataSourceId, new[] { typeof(MyArguments) });
}
}
I registered this DataSourceFactory in he module method "integrate".
When I try to save my project in Petrel, I have the following error message : "System.Exception: MyArgsPackId: Not an archivable type 'Slb.Ocean.Petrel.DomainObject.PillarGrid.Property'"
How can I manage this persistence please ?
PillarGrid and other complex types cannot be persisted in this manner.
For IIdentifiable objects you should persist their DROID.
Later you can then retrieve the object from said DROID using DataManager.Resolve.
Chippy

Image Carousel using Twitter Bootstrap and Orchard CMS

I am using Orchard CMS and the Bootstrap theme. I have been trying to use the carousel built in bootstrap to work as shown in the following blog post: http://www.stevetaylor.me.uk/image-carousel-using-twitter-bootstrap-and-orchard-cms-projections. I have followed the tutorial but i cannot get my new layout file to appear in the query under grid and html list. I believe i have copied the code word for word but still can get it to work. Anybody please help with this as think it would be a great feature to add. if i get it to work i will request it gets added to the bootstrap theme here: http://orchardbootstrap.codeplex.com/
See code below:
CarouselLayoutForm.cs
using System;
using Orchard.DisplayManagement;
using Orchard.Forms.Services;
using Orchard.Localization;
namespace Orchard.Projections.Providers.Layouts {
public class CarouselLayoutForms : IFormProvider {
protected dynamic Shape { get; set; }
public Localizer T { get; set; }
public CarouselLayoutForms(
IShapeFactory shapeFactory) {
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
public void Describe(DescribeContext context) {
Func<IShapeFactory, object> form =
shape => {
var f = Shape.Form(
Id: "CarouselLayout",
_HtmlProperties: Shape.Fieldset(
Title: T("Html properties"),
_ListId: Shape.TextBox(
Id: "outer-grid-id", Name: "OuterDivId",
Title: T("Outer div id"),
Description: T("The id to provide on the div element."),
Classes: new[] { "textMedium", "tokenized" }
),
_ListClass: Shape.TextBox(
Id: "outer-div-class", Name: "OuterDivClass",
Title: T("Outer div class"),
Description: T("The class to provide on the div element."),
Classes: new[] { "textMedium", "tokenized" }
),
_InnerClass: Shape.TextBox(
Id: "inner-div-class", Name: "InnerDivClass",
Title: T("Inner div class"),
Description: T("The class to provide on the inner div element."),
Classes: new[] { "textMedium", "tokenized" }
),
_FirstItemClass: Shape.TextBox(
Id: "first-item-class", Name: "FirstItemClass",
Title: T("First item class"),
Description: T("The class to provide on the first item element."),
Classes: new[] { "textMedium", "tokenized" }
),
_ItemClass: Shape.TextBox(
Id: "item-class", Name: "ItemClass",
Title: T("Item class"),
Description: T("The class to provide on the item element."),
Classes: new[] { "textMedium", "tokenized" }
)
)
);
return f;
};
context.Form("CarouselLayout", form);
}
}
/*
public class CarouselLayoutFormsValitator : FormHandler {
public Localizer T { get; set; }
public override void Validating(ValidatingContext context) {
if (context.FormName == "CarouselLayout") {
if (context.ValueProvider.GetValue("Alignment") == null) {
context.ModelState.AddModelError("Alignment", T("The field Alignment is required.").Text);
}
if (context.ValueProvider.GetValue("Columns") == null) {
context.ModelState.AddModelError("Columns", T("The field Columns/Lines is required.").Text);
}
else {
int value;
if (!Int32.TryParse(context.ValueProvider.GetValue("Columns").AttemptedValue, out value)) {
context.ModelState.AddModelError("Columns", T("The field Columns/Lines must be a valid number.").Text);
}
}
}
}
}
*/
}
CarouselLayout.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Projections.Descriptors.Layout;
using Orchard.Projections.Models;
using Orchard.Projections.Services;
namespace Orchard.Projections.Providers.Layouts {
public class CarouselLayout : ILayoutProvider {
private readonly IContentManager _contentManager;
protected dynamic Shape { get; set; }
public CarouselLayout(IShapeFactory shapeFactory, IContentManager contentManager) {
_contentManager = contentManager;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public void Describe(DescribeLayoutContext describe) {
describe.For("Html", T("Html"),T("Html Layouts"))
.Element("Carousel", T("Carousel"), T("Organizes content items in a carousel."),
DisplayLayout,
RenderLayout,
"CarouselLayout"
);
}
public dynamic RenderLayout(LayoutContext context, IEnumerable<LayoutComponentResult> layoutComponentResults) {
string outerDivClass = context.state.outerDivClass;
string OuterDivId = context.state.OuterDivID;
string innerDivClass = context.state.InnerDicClass;
string firstItemClass = context.state.FirstItemClass;
string itemClass = context.state.ItemClass;
IEnumerable<dynamic> shapes =
context.LayoutRecord.Display == (int)LayoutRecord.Displays.Content
? layoutComponentResults.Select(x => _contentManager.BuildDisplay(x.ContentItem, context.LayoutRecord.DisplayType))
: layoutComponentResults.Select(x => x.Properties);
return Shape.Carousel(Id: outerDivId, Items: shapes, OuterClasses: new[] { outerDivClass },
InnerClasses: new[] {innerDivClass}, FirstItemClasses: new[] {firstItemClass}, ItemClasses: new[] {itemClass});
}
}
}
LayoutShapes.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using Orchard.ContentManagement;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Mvc.Html;
using Orchard.Utility.Extensions;
namespace Orchard.Projections.Providers.Layouts {
public class LayoutShapes : IDependency {
public LayoutShapes() {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
[Shape]
public void Carousel(dynamic Display, TextWriter Output, HtmlHelper Html, string Id, IEnumerable<dynamic> Items,
IEnumerable<string> OuterClasses, IDictionary<string, string> OuterAttributes,
IEnumerable<string> InnerClasses, IDictionary<string, string> InnerAttributes,
IEnumerable<string> FirstItemClasses, IDictionary<string, string> FirstItemAttributes, IEnumerable<string> ItemClasses, IDictionary<string, string> ItemAttributes )
{
if (Items == null) return;
var items = Items.ToList();
var itemsCount = items.Count;
if (itemsCount < 1) return;
var outerDivTag = GetTagBuilder("div", Id, OuterClasses, OuterAttributes);
var innerDivTag = GetTagBuilder("div", string.Empty, InnerClasses, InnerAttributes);
var firstItemTag = GetTagBuilder("div", string.Empty, FirstItemClasses, FirstItemAttributes);
var itemTag = GetTagBuilder("div", string.Empty, ItemClasses, ItemAttributes);
Output.Write(outerDivTag.ToString(TagRenderMode.StartTag));
Output.Write(innerDivTag.ToString(TagRenderMode.StartTag));
int i = 0;
foreach (var item in items)
{
if (i== 0)
Output.Write(firstItemTag.ToString(TagRenderMode.StartTag));
else
Output.Write(itemTag.ToString(TagRenderMode.StartTag));
Output.Write(Display(item));
Output.Write(itemTag.ToString(TagRenderMode.EndTag));
i++;
}
Output.Write(innerDivTag.ToString(TagRenderMode.EndTag));
Output.Write("‹",id);
Output.Write("‹",id);
Output.Write(outerDivTag.ToString(TagRenderMode.EndTag));
Output.Write("<script>$(function () {$('"+ Id +"').carousel();}); </script>");
}
static TagBuilder GetTagBuilder(string tagName, string id, IEnumerable<string> classes, IDictionary<string, string> attributes) {
var tagBuilder = new TagBuilder(tagName);
tagBuilder.MergeAttributes(attributes, false);
foreach (var cssClass in classes ?? Enumerable.Empty<string>())
tagBuilder.AddCssClass(cssClass);
if (!string.IsNullOrWhiteSpace(id))
tagBuilder.GenerateId(id);
return tagBuilder;
}
}
}
Did you add your code to the module Orchard.Projections as explained?
You need to add the files in Visual Studio and to save the project for them to be included in the .csproj (unless, the dynamic compilation won't take the files in account), or you can build the module.
You are missing the definition for DisplayLayout() in your CarouselLayou.cs file:
public LocalizedString DisplayLayout(LayoutContext context)
{
string columns = context.State.Columns;
bool horizontal = Convert.ToString(context.State.Alignment) != "vertical";
return horizontal
? T("{0} columns grid", columns)
: T("{0} lines grid", columns);
}
Also, variable 'id' in LayourShapes.cs should be 'Id', and there are other incorrect casings in the declarations:
string outerDivClass = context.state.outerDivClass;
string OuterDivId = context.state.OuterDivID;
string innerDivClass = context.state.InnerDicClass;
string firstItemClass = context.state.FirstItemClass;
string itemClass = context.state.ItemClass;
should be:
string outerDivClass = context.State.OuterDivClass;
string outerDivId = context.State.OuterDivID;
string innerDivClass = context.State.InnerDicClass;
string firstItemClass = context.State.FirstItemClass;
string itemClass = context.State.ItemClass;

Using base View Model Class with GalaSoft MVVM Light

I am creating a project using WPF and MVVM-Light library from GalaSoft. I will have a base abstract View Model class, which will be used by all other View Model classes implemented. There I will have MVVM-Light base class as my base class. However, inside this base class, when I try to use RaisePropertyChanged function I get the following error:
An object reference is required for the non-static field, method, or property 'GalaSoft.MvvmLight.ViewModelBase.RaisePropertyChanged(string)'
The code will look like this:
AnalysisViewModelBase : ViewModelBase
{
public const string TagDescriptionStringListPropertyName = "TagDescriptionStringList";
protected static List<string> m_tagDescriptionStringList;
public static List<string> TagDescriptionStringList
{
get
{ return m_tagDescriptionStringList; }
set
{
if (m_tagDescriptionStringList == value)
return;
m_tagDescriptionStringList = value;
RaisePropertyChanged(TagDescriptionStringListPropertyName);
}
}
protected AnalysisViewModelBase()
{
m_tagDescriptionStringList = new List<string>();
m_tagDescriptionStringList.AddRange(new string[] { "North Position", "East Position", "Depth" });
}
}
AnotherViewModel : AnalysisViewModelBase
{ ... }
Could anyone please help me understand what is wrong with my RaiseProperyChanged function?
You are trying to access a Non Static Method From a Static Method... It does not have access to this value, you have to make your method non static.
here is a web page which explains about static methods if you want to have a better understanding of why you can't do what you are trying to do.
Link
You simply must declare your property "Tax DescriptionStringList " as non static. Since the backingfield (m_tagDescriptionStringList) is static it remains the same thing. Make this :
class AnalysisViewModelBase : ViewModelBase
{
public const string TagDescriptionStringListPropertyName = "TagDescriptionStringList";
protected static List<string> m_tagDescriptionStringList;
public List<string> TagDescriptionStringList
{
get
{ return m_tagDescriptionStringList; }
set
{
if (m_tagDescriptionStringList == value)
return;
m_tagDescriptionStringList = value;
RaisePropertyChanged(TagDescriptionStringListPropertyName);
}
}
protected AnalysisViewModelBase()
{
m_tagDescriptionStringList = new List<string>();
m_tagDescriptionStringList.AddRange(new string[] { "North Position", "East Position", "Depth" });
}
}
AnotherViewModel : AnalysisViewModelBase
{ ... }
If it is absolutely necessary to keep the property as a static property in this case, here is a solution: raise the property changes (using RaisePropertyChanged("TagDescriptionStringList")) when it happens, as I indicated in the code below
class AnalysisViewModelBase : ViewModelBase
{
public const string TagDescriptionStringListPropertyName = "TagDescriptionStringList";
protected static List<string> m_tagDescriptionStringList;
public static List<string> TagDescriptionStringList
{
get
{ return m_tagDescriptionStringList; }
set
{
if (m_tagDescriptionStringList != value)
{
m_tagDescriptionStringList = value;
}
}
}
protected AnalysisViewModelBase()
{
m_tagDescriptionStringList = new List<string>();
m_tagDescriptionStringList.AddRange(new string[] { "North Position", "East Position", "Depth" });
RaisePropertyChanged("TagDescriptionStringList");
}
}
AnotherViewModel : AnalysisViewModelBase
{ ... }