Constructor not defined error after setting date fields as null during object creation - date

I have created a wrapper class to create an Object and send it as a request to a third party system. It was working well. But after I added a two new arguments of the Datatype Date, I am getting the below error.
Constructor not defined: [SFDC_DataObject.CustomerAccountObject].<Constructor>(Id, String, Id, String, Id, String, Integer, NULL, String, String, Id, String, NULL, String, String, String, String)
The request that I am creating and sending is as below.
SFDC_DataObject.CustomerAccountObject cusAccObj = new SFDC_DataObject.CustomerAccountObject(o.AccountId, o.Customer_Name__c, o.Agency_Name__r.Id,o.Agency_Name_OB__c, o.Opportunity.OwnerId, o.Opportunity.Owner.FederationIdentifier, PrimarySalesSplitPercent, null, secSOSalesforceId.get(o.OpportunityId), secSOSalesforceEmail.get(o.OpportunityId), o.Opportunity.Customer_Success_Manage__r.Id, o.Opportunity.Customer_Success_Manage__r.FederationIdentifier, null, o.Billing_Email__c, o.Billing_Phone__c, o.Bill_To_Name__c, o.Billing_Notes__c);
My wrapper class for the same object is as below.
public class CustomerAccountObject {
public String sfCustomerId;
public String customerName;
public String sfAgencyId;
public String agencyName;
public String sfPrimarySalesOwnerId;
public String primarySalesOwnerEmail;
public Integer primarySalesOwnerPercentage;
public Date primarySalesOwnerEffectiveFrom;
public String sfSecondarySalesOwnerId;
public String secondarySalesOwnerEmail;
public Date secondarySalesOwnerEffectiveFrom;
public String sfAccountManagerId;
public String accountManagerEmail;
public String billingEmail;
public String billingPhone;
public String billingName;
public String billingNotes;
public CustomerAccountObject() {}
public CustomerAccountObject(String sfCustomerId, String customerName, String sfAgencyId, String agencyName, String sfPrimarySalesOwnerId, String primarySalesOwnerEmail, Integer primarySalesOwnerPercentage, Date primarySalesOwnerEffectiveFrom, String sfSecondarySalesOwnerId, String secondarySalesOwnerEmail, Date secondarySalesOwnerEffectiveFrom, String sfAccountManagerId, String accountManagerEmail, String billingEmail, String billingPhone, String billingName, String billingNotes) {
this.sfCustomerId = sfCustomerId;
this.customerName = customerName;
this.sfAgencyId = sfAgencyId;
this.agencyName = agencyName;
this.sfPrimarySalesOwnerId = sfPrimarySalesOwnerId;
this.primarySalesOwnerEmail = primarySalesOwnerEmail;
this.primarySalesOwnerPercentage = primarySalesOwnerPercentage;
this.primarySalesOwnerEffectiveFrom = primarySalesOwnerEffectiveFrom;
this.sfSecondarySalesOwnerId = sfSecondarySalesOwnerId;
this.secondarySalesOwnerEmail = secondarySalesOwnerEmail;
this.secondarySalesOwnerEffectiveFrom = secondarySalesOwnerEffectiveFrom;
this.sfAccountManagerId = sfAccountManagerId;
this.accountManagerEmail = accountManagerEmail;
this.billingEmail = billingEmail;
this.billingPhone = billingPhone;
this.billingName = billingName;
this.billingNotes = billingNotes;
}
}
I began getting the error after I added the null for the Date arguments I.e primarySalesOwnerEffectiveFrom and secondarySalesOwnerEffectiveFrom during the Object creation.
Can anyone please let me know what am I doing wrong here.

The order is wrong.
In c-tor definition you have
String sfCustomerId, String customerName, String sfAgencyId, String
agencyName, String sfPrimarySalesOwnerId, String
primarySalesOwnerEmail, Integer primarySalesOwnerPercentage, Date
primarySalesOwnerEffectiveFrom, String sfSecondarySalesOwnerId, String
secondarySalesOwnerEmail, Date secondarySalesOwnerEffectiveFrom + 6 more Strings
So
... Integer, Date, String, String, Date, ...
But the code that calls it goes
o.AccountId, o.Customer_Name__c,
o.Agency_Name__r.Id,o.Agency_Name_OB__c, o.Opportunity.OwnerId,
o.Opportunity.Owner.FederationIdentifier, PrimarySalesSplitPercent,
null, secSOSalesforceId.get(o.OpportunityId),
secSOSalesforceEmail.get(o.OpportunityId),
o.Opportunity.Customer_Success_Manage__r.Id,
o.Opportunity.Customer_Success_Manage__r.FederationIdentifier, null, +
4 strings
There are extra 2 strings before 2nd null. And only 4 strings after it. You need to inject that null just after secSOSalesforceEmail?
This will get only worse to maintain as time goes on. Consider making a simple constructor and making the properties public. You could then set them after constructor in normal call. And if you don't need dates you just don't write line that sets date fields instead of injecting null at right position.
Follow-up edit
Not sure if there's an official guide to that technique or a blog post. Tools like Apex-PMD complain when you make methods with too many arguments, rules like "Avoid long parameter lists".
One way would be to do something like this:
SFDC_DataObject.CustomerAccountObject cusAccObj = new SFDC_DataObject.CustomerAccountObject();
cusAccObj.sfCustomerId = o.AccountId;
cusAccObj.customerName = o.Customer_Name__c;
cusAccObj.sfAgencyId = o.Agency_Name__c;
cusAccObj.agencyName = o.Agency_Name_OB__c;
cusAccObj.sfPrimarySalesOwnerId = o.Opportunity.OwnerId;
cusAccObj.primarySalesOwnerEmail = o.Opportunity.Owner?.FederationIdentifier;
cusAccObj.primarySalesOwnerPercentage = PrimarySalesSplitPercent;
// cusAccObj.primarySalesOwnerEffectiveFrom = null; // just don't bother with the line?
cusAccObj.sfSecondarySalesOwnerId = secSOSalesforceId.get(o.OpportunityId);
// ..
That's not very object oriented, not very elegant but caller has full control on the mapping. Problem will be if you need to map new field and this has been copy-pasted into 10 places. You'll have to update them all (which will be easier than adding N-th parameter to long call but still)
Another way would be to create a baseline constructor that takes whole Order object (it's an Order, right?), it'd map the fields internally. Then if needed - you specify some extra fields after constructor. Or maybe make few constructors?
public CustomerAccountObject(){
// I'm parameterless, I'm doing nothing! I'm just here if somebody needs a really custom field mapping or JSON deserialisations need a parameterless one
}
public CustomerAccountObject(Order o){
// I can map all fields from Order! Want to map new field? Just chuck it in here!
sfCustomerId = o.AccountId;
// ...
}
public CustomerAccountObject(Order o, Map<Id, String> secSOSalesforceId, Map<Id, String> secSOSalesforceEmail){
// I can do everything above and few more fields too!
this(o);
sfSecondarySalesOwnerId = secSOSalesforceId.get(o.OpportunityId);
secondarySalesOwnerEmail = secSOSalesforceEmail.get(o.OpportunityId);
}
You have bit of code reuse, the Order fields mapping is defined in just 1 place, just 1 line to change in future. You don't have an orgy of this everywhere anymore. And then your call if you really need the last constructor or you'll call the one that just takes Order o and then set the 2 extra fields after it finishes.

Related

What is the purpose of the line "public Word(#NonNull String word) {this.mWord = word;}" in this example?

I'm trying to figure out how to use Android's Room library for implementing a prepopulated sqlite database in my app and I came across this Android tutorial. One of the lines (the one in the title) confuses me though, because in another tutorial (also by Android), this line isn't present. Why is this line of code present in the first tutorial but not the second? What is its purpose?
I ask this because my code (which I'm basing off the second tutorial) doesn't include this line and yet this post by a different user attempting to do something similar with a prepopulated database does include it.
Here is some of the code I have (each of the fields has a getter method which just returns this.thatfield'sname):
#Entity (tableName = "words")
public class Words {
#PrimaryKey
#NonNull
#ColumnInfo (name = "word_id")
private int wordId;
#ColumnInfo(name = "a_words")
private String aWords;
#ColumnInfo(name = "b_words")
private String bWords;
#ColumnInfo(name = "c_words")
private String cWords;
This code gives me a "Cannot find setter for field" but just changing the fields from public to private seems to solve that (not sure if this is the best way to solve this error, though).
Why is this line of code present in the first tutorial but not the second?
That line is an additional class constructor that takes 1 non-null String and sets the mWord member/variable to the provided String.
Without then you can only use myWord = new Word(); to instantiate a Word object and the value would be either the default value if provided or null.
With the additional constructor then you could use both
myWord = new Word();
or
myOtherWord = new Word("A Word");
So, in short it's provided an alternative way of constructing/instantiating a new Object of that Class.
Using your code then you could have, for example :-
#Entity(tableName = "words")
class Words {
#ColumnInfo(name = "word_id")
#PrimaryKey
private int wordId;
#ColumnInfo(name = "a_words")
String aWords;
#ColumnInfo(name = "b_words")
String bWords;
#ColumnInfo(name = "c_words")
String cWords;
public void setWordId(int wordId, String aWord, String bWords, String c) {
this.wordId = wordId;
this.aWords = aWord;
this.bWords = bWords;
this.cWords = c;
}
}
Note for demonstration the parameter names use 3 different standards, ideally you would stick to a single standard/convention for naming the parameters.
So now you could use the one constructor that expects 4 parameters e.g.
myWord = new Words(1,"Apple","Banana","Cherry");
which equates to
myWord = new Words();
myWord.wordId = 1;
myWord.aWords = "Apple;
myWord.bWords = "Banana";
myWord.cWords = "Cherry";
As you have specified a constructor, the default constructor is no longer usable.
What is its purpose?
As can be seen, additional constructors, can reduce the amount of coding, there use will also prompt for the values (hence the use of useful parameter names improves i.e. c as above is not very meaningful at all (although in conjunction with the other parameters if would be better than x))

Java convert list of string with comma separated values to list of objects

I'd like to convert a json array of person
persons: [
{"1, Franck, 1980-01-01T00:00:00"},
{"2, Marc, 1981-01-01T00:00:00"}
]
To a list of Person object using this class:
class Person {
private Integer id;
private String name;
private Date dateOfBirth;
// getter and setter
}
Would it be possible to use a converter and Java 8 to do it?
public Person convert(String from) {
String[] data = from.split(",");
return new Person(Integer.parseInt(data[0]), data[1], new Date(data[2]));
}
You can do it like so,
Pattern idNumber = Pattern.compile("\\d+");
List<Person> persons = Arrays.stream(from.split("}")).filter(s -> idNumber.matcher(s).find())
.map(s -> s.substring(s.indexOf("{") + 1)).map(s -> s.split(","))
.map(a -> new Person(Integer.parseInt(a[0].replaceAll("\"", "")), a[1],
LocalDateTime.parse(a[2].trim().replaceAll("\"", ""))))
.collect(Collectors.toList());
First split each string using "}" character, and then filter out invalid tokens, which does not contain a digit. Notice that each valid payload should contain Id number which is a digit. Finally remove any spurious trailing characters occur before the Id digit and map each resulting String to a Person object. At last collect the Person instances into a List.
Notice that I have used LocalDateTime for the dateOfBirth field in the Person class. So the Person class looks like this.
public class Person {
private final Integer id;
private final String name;
private final LocalDateTime dateOfBirth;
// remainder omitted for the sake of brevity.
}
However as you can see it is always intuitive to use some framework such as Jackson ObjectMapper to get the work done than writing all this. But in this case your Json is malformed so you won't be able to use such a framework unless you fix the json payload structure.
Update
Here's much more elegant Java9 solution.
String regexString = Pattern.quote("{\"") + "(.*?)" + Pattern.quote("\"}");
Pattern pattern = Pattern.compile(regexString);
List<Person> persons = pattern.matcher(from)
.results()
.map(mr -> mr.group(1)).map(s -> s.split(", "))
.map(a -> new Person(Integer.parseInt(a[0]), a[1], LocalDateTime.parse(a[2])))
.collect(Collectors.toList());
In Java 9, you can now use Matcher#results() to get a Stream<MatchResult>. Here's an excerpt from the documentation.
Returns a stream of match results for each subsequence of the input
sequence that matches the pattern. The match results occur in the same
order as the matching subsequences in the input sequence.
I would rather recommend you to use Java9 to solve this.
If you need to do it just for a List of String representation of a Person, you may do it like so.
List<String> personStr = Arrays.asList("1, Franck, 1980-01-01T00:00:00", "2, Marc, 1981-01-01T00:00:00");
List<Person> persons = personStr.stream()
.map(s -> s.replaceAll(" ", "").split(","))
.map(a -> new Person(Integer.parseInt(a[0]), a[1], LocalDateTime.parse(a[2])))
.collect(Collectors.toList());
Notice the latter is far more easier than the prior and contains just a subset of the original solution.
An Extention of your solution itself & with a bit of abstraction:
List<String> personStr = Arrays.asList("1, Franck, 1980-01-01T00:00:00", "2, Marc, 1981-01-01T00:00:00");
List<Person> persons = personStr.stream()
.map(Person::new)
.collect(Collectors.toList());
Where, Person class has a constructor which accepts a string arg to convert it to a Person object, as follows:
public Person(String from) {
String[] data = from.split(",");
Arrays.parallelSetAll(data, i -> data[i].trim());
this.id = Integer.parseInt(data[0]);
this.name = data[1];
this.dateOfBirth = new Date(data[2]);
}

how to use array of string as condition in Drools decision table?

I am new to drools decision table, so my question may be invalid.
In my decision table i am using "in" in condition column.
Exampe : i have class Student and there is another class UniversityConstant.
In UniversityConstant class there is array of string subject code : public static final String[] subjectCode ={"150","920","930","940","154"};
In this case my condition not working properly(Above picture: Not working). Instead of using string array constant of java class if i use direct subject code string than it is working(Below picture:working).
In my project there are lots of string array ,so it is not possible to copy paste them in decision table excel. Even in case i use string constant in UniversityConstant class which represent all subject code like public static final String subjectCodeStr1 ="\"155\",\"920\",\"930\",\"940\",\"154\"" OR
public static final String subjectCodeStr2 ="155,920,930,940,154"; than also it is not working. My question is there any way to use string array constant or simple string which represents array of string of java
in decision table .
You can declare the constant sets as
public static final List<String> subjectCodes =
Arrays.asList( "155","920","930","940","154" );
and use
...getPrimarySub() memberOf $param
...
UniversityConstamt.subjectCodes

How to handle optional query parameters in Play framework

Lets say I have an already functioning Play 2.0 framework based application in Scala that serves a URL such as:
http://localhost:9000/birthdays
which responds with a listing of all known birthdays
I now want to enhance this by adding the ability to restrict results with optional "from" (date) and "to" request params such as
http://localhost:9000/birthdays?from=20120131&to=20120229
(dates here interpreted as yyyyMMdd)
My question is how to handle the request param binding and interpretation in Play 2.0 with Scala, especially given that both of these params should be optional.
Should these parameters be somehow expressed in the "routes" specification? Alternatively, should the responding Controller method pick apart the params from the request object somehow? Is there another way to do this?
Encode your optional parameters as Option[String] (or Option[java.util.Date], but you’ll have to implement your own QueryStringBindable[Date]):
def birthdays(from: Option[String], to: Option[String]) = Action {
// …
}
And declare the following route:
GET /birthday controllers.Application.birthday(from: Option[String], to: Option[String])
A maybe less clean way of doing this for java users is setting defaults:
GET /users controllers.Application.users(max:java.lang.Integer ?= 50, page:java.lang.Integer ?= 0)
And in the controller
public static Result users(Integer max, Integer page) {...}
One more problem, you'll have to repeat the defaults whenever you link to your page in the template
#routes.Application.users(max = 50, page = 0)
In Addition to Julien's answer. If you don't want to include it in the routes file.
You can get this attribute in the controller method using RequestHeader
String from = request().getQueryString("from");
String to = request().getQueryString("to");
This will give you the desired request parameters, plus keep your routes file clean.
Here's Julien's example rewritten in java, using F.Option: (works as of play 2.1)
import play.libs.F.Option;
public static Result birthdays(Option<String> from, Option<String> to) {
// …
}
Route:
GET /birthday controllers.Application.birthday(from: play.libs.F.Option[String], to: play.libs.F.Option[String])
You can also just pick arbitrary query parameters out as strings (you have to do the type conversion yourself):
public static Result birthdays(Option<String> from, Option<String> to) {
String blarg = request().getQueryString("blarg"); // null if not in URL
// …
}
For optional Query parameters, you can do it this way
In routes file, declare API
GET /birthdays controllers.Application.method(from: Long, to: Long)
You can also give some default value, in case API doesn't contain these query params it will automatically assign the default values to these params
GET /birthdays controllers.Application.method(from: Long ?= 0, to: Long ?= 10)
In method written inside controller Application these params will have value null if no default values assigned else default values.
My way of doing this involves using a custom QueryStringBindable. This way I express parameters in routes as:
GET /birthdays/ controllers.Birthdays.getBirthdays(period: util.Period)
The code for Period looks like this.
public class Period implements QueryStringBindable<Period> {
public static final String PATTERN = "dd.MM.yyyy";
public Date start;
public Date end;
#Override
public F.Option<Period> bind(String key, Map<String, String[]> data) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
try {
start = data.containsKey("startDate")?sdf.parse(data.get("startDate") [0]):null;
end = data.containsKey("endDate")?sdf.parse(data.get("endDate")[0]):null;
} catch (ParseException ignored) {
return F.Option.None();
}
return F.Option.Some(this);
}
#Override
public String unbind(String key) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
return "startDate=" + sdf.format(start) + "&" + "endDate=" + sdf.format(end);
}
#Override
public String javascriptUnbind() {
return null;
}
public void applyDateFilter(ExpressionList el) {
if (this.start != null)
el.ge("eventDate", this.start);
if (this.end != null)
el.le("eventDate", new DateTime(this.end.getTime()).plusDays(1).toDate());
}
}
applyDateFilter is just a convienence method i use in my controllers if I want to apply date filtering to the query. Obviously you could use other date defaults here, or use some other default than null for start and end date in the bind method.

ASP.NET GetFullHtmlFieldId not returning valid id

In MVC2 template, I normally use this.ViewData.TemplateInfo.GetFullHtmlFieldId(fieldName)
to generate the id field of an html element. This has worked in most of the cases.
However this method does not actually return valid id field, it merely prefix fieldName with ViewData.TemplateInfo.HtmlFieldPrefix, this is causing problems for me when rendering collections which has [] in the HtmlFieldPrefix.
I have been manually converting those characters to _ where I find the need, but this seems to be not elegant (repeated code), has anyone found a good way to generate id field properly?
Can you be more specific about the kind of problems do you have?
For example, there is an elegant approach to editing variable length list with validation support provided. While it doesn't use templates still remains DRY with partial views.
While the ids are inconsistent - the names are OK and only problem I encountered is that using jquery.infieldlabel it appeared that label's for attribute (generated by GetFullHtmlFieldId inside LabelFor helper) didn't match id of the appropriate TextBoxFor input. So I created LabelForCollectionItem helper method that just uses the same method for id generation as the TextBox - TagBuilder.GenerateId(fullName)
Maybe the code doesn't correspond to your need but hope it will help somebody since I found your question among the first searching for solution to my problem.
public static class LabelExtensions
{
/// <summary>
/// Generates Label with "for" attribute corresponding to the id rendered by input (e.g. TextBoxFor),
/// for the case when input is a collection item (full name contains []).
/// GetFullHtmlFieldId works incorrect inside Html.BeginCollectionItem due to brackets presense.
/// This method copies TextBox's id generation.
/// </summary>
public static MvcHtmlString LabelForCollectionItem<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression,
string labelText = null, object htmlAttributes = null) where TModel : class
{
var tag = new TagBuilder("label");
tag.MergeAttributes(new RouteValueDictionary(htmlAttributes)); // to convert an object into an IDictionary
// set inner text
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string innerText = labelText ?? GetDefaultLabelText(html, expression, htmlFieldName);
if (string.IsNullOrEmpty(innerText))
{
return MvcHtmlString.Empty;
}
tag.SetInnerText(innerText);
// set for attribute
string forId = GenerateTextBoxId(tag, html, htmlFieldName);
tag.Attributes.Add("for", forId);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
/// <summary>
/// Extracted from System.Web.Mvc.Html.InputExtensions
/// </summary>
private static string GenerateTextBoxId<TModel>(TagBuilder tagBuilder, HtmlHelper<TModel> html, string htmlFieldName)
{
string fullName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
tagBuilder.GenerateId(fullName);
string forId = tagBuilder.Attributes["id"];
tagBuilder.Attributes.Remove("id");
return forId;
}
/// <summary>
/// Extracted from System.Web.Mvc.Html.LabelExtensions
/// </summary>
private static string GetDefaultLabelText<TModel, TValue>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string htmlFieldName)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
return labelText;
}
}