Simplify method with many for and Ifs statement - java

ExtractSourceQueryOB is an object that represents queries, a query could have a master query and in this case the master query should not be removed from the list, querySet contains strings of keys that represent queries that are required in this operation but it doesnt take account of the possibility of that query having a master. So i tried to loop through all queries checking that query has a master and in that case add that object (ExtractSourceQueryOB ) to the final list.
private List<ExtractSourceQueryOB> checkRequiredQueries(List<ExtractSourceQueryOB> extractSourceQueryList, ExtractElement extractElement) {
Set<ExtractSourceQueryOB> queryList = new HashSet();
Set<String> querySet = new HashSet();
fillUsedSymbolList(querySet, extractElement);
for(ExtractSourceQueryOB extractSourceQuery : extractSourceQueryList) {
if(extractSourceQuery.getMaster() != null ) {
for(ExtractSourceQueryOB extractSourceQuery2 : extractSourceQueryList) {
if(extractSourceQuery.getMaster().equals(extractSourceQuery2.getSymbol())){
queryList.add(extractSourceQuery2);
}
}
}
}
for (ExtractSourceQueryOB extractSourceQuery : extractSourceQueryList) {
for (String s : querySet) {
if (extractSourceQuery.getSymbol().equalsIgnoreCase(s)) {
queryList.add(extractSourceQuery);
}
}
}
return new ArrayList<>(queryList);
}
How can i simplify this method ?

You could eliminate the second for by combining that code with the first. Like,
for(ExtractSourceQueryOB extractSourceQuery : extractSourceQueryList) {
if(extractSourceQuery.getMaster() != null ) {
for(ExtractSourceQueryOB extractSourceQuery2 : extractSourceQueryList) {
if(extractSourceQuery.getMaster().equals(extractSourceQuery2.getSymbol())){
queryList.add(extractSourceQuery2);
}
}
}
for (String s : querySet) {
if (extractSourceQuery.getSymbol().equalsIgnoreCase(s)) {
queryList.add(extractSourceQuery);
}
}
}

private List<ExtractSourceQueryOB> checkRequiredQueries(List<ExtractSourceQueryOB> extractSourceQueryList, ExtractElement extractElement) {
Set<ExtractSourceQueryOB> queryList = new HashSet();
Set<String> querySet = new HashSet();
fillUsedSymbolList(querySet, extractElement);
for (ExtractSourceQueryOB extractSourceQuery : extractSourceQueryList) {
if (isyUsedAsMaster(extractSourceQuery,extractSourceQueryList) || isUsed(extractSourceQuery,querySet, extractSourceQueryList)) {
queryList.add(extractSourceQuery);
}
}
return new ArrayList<>(queryList);
}
private boolean isyUsedAsMaster(ExtractSourceQueryOB extractSourceQuery, List<ExtractSourceQueryOB> extractSourceQueryList) {
if (extractSourceQuery.getMaster() != null) {
for (ExtractSourceQueryOB extractSourceQuery2 : extractSourceQueryList) {
if (extractSourceQuery.getMaster().equals(extractSourceQuery2.getSymbol())) {
return true;
}
}
}
return false;
}
private boolean isUsed(ExtractSourceQueryOB extractSourceQuery, Set<String> querySet, List<ExtractSourceQueryOB> extractSourceQueryList) {
for (String s : querySet) {
if(extractSourceQuery.getSymbol().equalsIgnoreCase(s)) {
return true;
}
}
return false;
}
Previous answer above is correct but this is with some refactoring

Related

Race condition in multiple instance in kubernates

Hi i have a race condition in given method i have 2 instances in kubernates and checking in redis
public void method(GuestDTO guestDTO) {
String executeName = "addingGuestToCache" + guestDTO.getUser();
if (!redisService.checkExecute(executeName)) {
redisService.startExecute(executeName);
OpenGuestDTO openGuestDTO = new OpenGuestDTO();
RMap<String, List<OpenGuestDTO>> openGuestDTOList = redisService.getOpenGuestDTOList();
List<OpenGuestDTO> userGuestList = openGuestDTOList.get(guestDTO.getUser());
if (userGuestList == null) {
userGuestList = Collections.synchronizedList(new ArrayList<OpenGuestDTO>());
}
for (OpenGuestDTO guestDTO1 : userGuestList) {
if (guestDTO1.getGuestName().equalsIgnoreCase(guestDTO.getGuestName())) {
redisService.deleteExecute(executeName);
return;
}
}
openGuestDTOList.add(openGuestDTO);
openGuestDTOList.fastPut(guestDTO.getUser(), userGuestList);
redisService.deleteExecute(executeName);
}else{
method(guestDTO);
}
}

How to configurate mapstruct to ignore map object when all field are null

env:
jdk: 17.0.1
mapstruct: 1.5.1.Final
Using the default configuration I generated the following code
protected AgentInfo wealthProdAccountInfoDTOToAgentInfo(WealthProdAccountInfoDTO wealthProdAccountInfoDTO) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
String agentName = null;
String agentIdentityType = null;
String agentIdentityNo = null;
String agentIdentityExpireAt = null;
agentName = wealthProdAccountInfoDTO.getAgentName();
agentIdentityType = wealthProdAccountInfoDTO.getAgentIdentityType();
agentIdentityNo = wealthProdAccountInfoDTO.getAgentIdentityNo();
agentIdentityExpireAt = wealthProdAccountInfoDTO.getAgentIdentityExpireAt();
AgentInfo agentInfo = new AgentInfo( agentName, agentIdentityType, agentIdentityNo, agentIdentityExpireAt );
return agentInfo;
}
But I want to return null when all field of source are null, like this
protected AgentInfo wealthProdAccountInfoDTOToAgentInfo(WealthProdAccountInfoDTO wealthProdAccountInfoDTO) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
// add check logic
if (agentName == null && agentIdentityType == null && agentIdentityNo == null && agentIdentityExpireAt == null) {
return null;
}
String agentName = null;
String agentIdentityType = null;
String agentIdentityNo = null;
String agentIdentityExpireAt = null;
agentName = wealthProdAccountInfoDTO.getAgentName();
agentIdentityType = wealthProdAccountInfoDTO.getAgentIdentityType();
agentIdentityNo = wealthProdAccountInfoDTO.getAgentIdentityNo();
agentIdentityExpireAt = wealthProdAccountInfoDTO.getAgentIdentityExpireAt();
AgentInfo agentInfo = new AgentInfo( agentName, agentIdentityType, agentIdentityNo, agentIdentityExpireAt );
return agentInfo;
}
how should I configure it?
Unfortunately there's no clean solution for your problem, except implementing code for null check by yourself, Marc specified the right approach to your problem (I'd go with it personally or would use default method for the same purpose).
I can add some workarounds, which will only work if mapping target is inner object:
Use #BeforeMapping to set input inner object to null, so when there will be null-check it will be skipped
#BeforeMapping
default void clearData(TestB source, #MappingTarget TestA target) {
TestD innerD = source.getInnerD();
if (innerD.getSecond() == null && innerD.getFirst() == null) {
source.setInnerD(null);
}
}
And it will generate the following code:
#Override
public TestA from(TestB input) {
....
clearData( input, testA ); //set input field to null
testA.setInnerC( fromInner( input.getInnerD() ) );
....
}
#Override
public TestC fromInner(TestD input) {
if ( input == null ) { //skip because of null
return null;
}
....
}
Use #AfterMapper to set output parameter to null(it will be mapped in the first place, so there will be some overhead)
#AfterMapping
default void clearData(TestB source, #MappingTarget TestA target) {
TestD innerD = source.getInnerD();
if (innerD.getSecond() == null && innerD.getFirst() == null) {
target.setInnerC(null);
}
}
And generated code will be:
#Override
public TestA from(TestB input) {
....
testA.setInnerC( fromInner( input.getInnerD() ) ); //field is actually mapped but cleared later
clearData( input, testA );
return testA;
}
As I said, these solutions aren't really clean and should be seen as workarounds only. Pros of these workaround is that you will keep working with autogenerated code and these hacks will be hidden inside that code.
UPD Stumbled upon #DecoratedWith lately and it also can do the trick. https://mapstruct.org/documentation/stable/reference/html/#_customizing_mappings
Just implement decorator for iterable2iterable mapping method: List<A> from(List<b> b) and just manually iterate over b checking if all b's fields are null and if so skip it
brute force... it's a simple class, so create a custom mapper
#Mapper
public interface AgentInfoMapper {
#Named("AgentInfoNullIfContentsNull")
public static AgentInfo custom(WealthProdAccountInfoDTO dto) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
if (agentName == null && agentIdentityType == null && agentIdentityNo == null && agentIdentityExpireAt == null) {
return null;
}
// mapping code
}
}
https://www.baeldung.com/mapstruct-custom-mapper
Thanks to ArtemAgaev's idea, I ended up considering using #AfterMapping and java reflection for this type of scenario
#AfterMapping
default void cleanData(#MappingTarget AccountInfoDomain domain) {
Optional.ofNullable(domain).ifPresent(c -> {
if (isAllFieldNull(domain.getAgentInfo())) {
domain.setAgentInfo(null);
}
});
}
public static boolean isAllFieldNull(Object o) {
Object[] fieldsValue = getFieldsValue(o);
return Optional.ofNullable(fieldsValue).map(f -> Arrays.stream(f).allMatch(Objects::isNull)).orElse(true);
}
public static Object[] getFieldsValue(Object obj) {
if (null != obj) {
final Field[] fields = getFields(obj instanceof Class ? (Class<?>) obj : obj.getClass());
if (null != fields) {
final Object[] values = new Object[fields.length];
for (int i = 0; i < fields.length; i++) {
values[i] = getFieldValue(obj, fields[i]);
}
return values;
}
}
return null;
}

Spring BeanWrapperFieldExtractor FlatFileItemWriter ignore missing null fields

I want to write to a csv file ignoring object field which are null.
Currently it writes:
Test,PBAFFF,,
The 3rd and 4th values can be null in the object.
How can I configure FlatFileItemWriter with BeanWrapperFieldExtractor which would only write to file the non null fields?
I have my writer configured like this:
csvWriter.setLineAggregator(new DelimitedLineAggregator<Transaction>() {
{
setDelimiter(",");
setFieldExtractor(new BeanWrapperFieldExtractor<Transaction>() {
{
setNames(new String[] { "id", "source", "startDate", "endDate"});
}
});
}
});
There are two ways to do this:
Implement your custom LineAggregator
public class NullSafeDelimitedLineAggregator<T> extends ExtractorLineAggregator<T> {
private String delimiter;
public NullSafeDelimitedLineAggregator(String delimiter, FieldExtractor<T> fieldExtractor) {
this.delimiter = delimiter;
setFieldExtractor(fieldExtractor);
}
#Override
public String doAggregate(Object[] fields) {
return arrayToDelimitedString(fields, delimiter);
}
private String arrayToDelimitedString(#Nullable Object[] arr, String delim) {
if (ObjectUtils.isEmpty(arr)) {
return "";
}
if (arr.length == 1) {
return ObjectUtils.nullSafeToString(arr[0]);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if(arr[i] == null || "".equals(arr[i])) {
continue;
}
if (i > 0) {
sb.append(delim);
}
sb.append(arr[i]);
}
return sb.toString();
}
}
And change config in following way:
writer.setLineAggregator(new NullSafeDelimitedLineAggregator<>(
",",
new BeanWrapperFieldExtractor<Transaction>() {
{
setNames(new String[] { "id", "source", "startDate", "endDate"});
}
}));
Implement your custom FieldExtractor
public class NullSafeBeanWrapperFieldExtractor<T> implements FieldExtractor<T>, InitializingBean {
private String[] names;
public void setNames(String[] names) {
Assert.notNull(names, "Names must be non-null");
this.names = Arrays.asList(names).toArray(new String[names.length]);
}
#Override
public Object[] extract(T item) {
List<Object> values = new ArrayList<Object>();
BeanWrapper bw = new BeanWrapperImpl(item);
for (String propertyName : this.names) {
Object value = bw.getPropertyValue(propertyName);
if(value == null || "".equals(value)) {
continue;
}
values.add(value);
}
return values.toArray();
}
#Override
public void afterPropertiesSet() {
Assert.notNull(names, "The 'names' property must be set.");
}
}
And change config in following way:
writer.setLineAggregator(new DelimitedLineAggregator<Transaction>() {
{
setDelimiter(",");
setFieldExtractor(new NullSafeBeanWrapperFieldExtractor<Transaction>() {
{
setNames(new String[] { "id", "source", "startDate", "endDate"});
}
});
}
});
Important: You should chose one of two options based on one question: is it important for you to know how many values you have in the array or not? if yes - LineAggregator, no - FieldExtractor.

How to decrease the time complexity of this O(n^3) code

Here is the piece of code that's bothering me. It does the task but I want to optimize it if possible.
I have list of Entities
Config for each Entity based on ID.
From Config has Tips for each Entity
From Config has Rejects for each Entity
Rejects have ID for each Tip
I get ID of Tip to be reject, remove it from allItems and add it to removeItems
Map<String, String> removeItems = new HashMap<>();
Map<String, Pair<String, Config>> allItems = new HashMap<>();
for(final Entity entity : entities) {
final Config config = Configs
.get(entity.getId());
if (config == null || entity.getTxId() == null) {
continue;
}
if (config.getTips() != null) {
for (final Tip tip : config.getTips()) {
String currentId = entity.getId();
String currentTipId = tip.getTipId();
if(allItems.containsKey(currentTipId)) {
Pair<String, Config> item = allItems.get(currentTipId);
if(tip.getPriority() > item.getValue().getPriority()) {
removeItems.put(currentTipId, item.getKey());
allItems.put(currentTipId, new Pair(currentId, tip));
} else {
removeItems.put(currentTipId, currentId);
}
} else {
allItems.put(currentTipId, new Pair(currentId, tip));
}
List<String> rejects = tip.getRejects();
if(CollectionUtils.isEmpty(rejects)) {
continue;
}
for (String reject : rejects) {
Pair<String, Config> pair = allItems.get(reject);
if (null != pair) {
String rejectId = pair.getKey();
if (StringUtils.isNotEmpty(rejectId)) {
removeItems.put(reject, rejectId);
}
}
}
}
}
}

Testing controllers using Spring, JUNIT, MockMvc and Hamcrest

I am trying to test a controller of mine which returns me a List of Objects on the get method to populate a dropdown on my page.
I am trying to write a JUnit test using MockMvc and Hamcrest to test the same.
I want to compare the List of objects and test if it fails or not.
I have created a static List of objects in my Test.java and I am getting a List of objects from the model.attribute method.
To Test: if both the List of Objects are equal and don't contain any other objects.
My object is called Option which has 3 properties. Key, Value and Selected. I have to check if the all the keys exists in the List or not.
I am unable to create a matcher to do the same. I am trying to create a matcher to compare my List.
So far I have done the following:
#Before
public void setup() throws Exception {
// This would build a MockMvc with only the following controller
this.mockMvc = MockMvcBuilders.standaloneSetup(openAccountController)
.build();
}
#Test
public void testOpenAccount() {
try {
setAllLegislations();
this.mockMvc
.perform(get("/open_account.htm"))
// This method is used to print out the actual httprequest
// and httpresponse on the console.
.andDo(print())
// Checking if status is 200
.andExpect(status().isOk())
.andExpect(
model().attributeExists("appFormAccountPlans",
"appFormLiraLegislations",
"appFormLrspLegislations",
"appFormRlspLegislations"))
.andExpect(
model().attribute("appFormAccountPlans", hasSize(5)))
.andExpect(
model().attribute("appFormLiraLegislations",
hasSize(8)))
.andExpect(
model().attribute("appFormLrspLegislations",
hasSize(2)))
.andExpect(
model().attribute("appFormRlspLegislations",
hasSize(1)))
.andExpect(
model().attribute(
"appFormLiraLegislations",
hasKeyFeatureMatcher(getLiraLegislations(allLegislations))));
private Matcher<List<Option>> hasKeyFeatureMatcher(
final List<Option> expectedOptions) {
return new FeatureMatcher<List<Option>, List<Option>>(
equalTo(expectedOptions), "Options are", "was") {
#Override
protected List<Option> featureValueOf(List<Option> actualOptions) {
boolean flag = false;
if (actualOptions.size() == expectedOptions.size()) {
for (Option expectedOption : expectedOptions) {
for (Option actualOption : actualOptions) {
if (expectedOption.getKey().equals(
actualOption.getKey())) {
flag = true;
} else {
flag = false;
break;
}
}
}
}
if (flag)
return actualOptions;
else
return null;
}
};
}
private List<Option> getLiraLegislations(List<Option> legislation) {
List<Option> liraLegislations = new ArrayList<Option>();
Iterator<Option> iterator = legislation.iterator();
while (iterator.hasNext()) {
Option option = iterator.next();
if (LIRA_LEGISLATIONS.contains(option.getKey())) {
liraLegislations.add(option);
}
}
return liraLegislations;
}
private List<Option> allLegislations;
public List<Option> getAllLegislations() {
return allLegislations;
}
public void setAllLegislations() {
allLegislations = new ArrayList<Option>();
for (String key : ALL_LEGISLATIONS) {
Option option = new Option();
option.setKey(key);
allLegislations.add(option);
}
}
private static final Set<String> ALL_LEGISLATIONS = new HashSet<String>(
Arrays.asList(AccountLegislationEnum.AB.toString(),
AccountLegislationEnum.MB.toString(),
AccountLegislationEnum.NB.toString(),
AccountLegislationEnum.NL.toString(),
AccountLegislationEnum.NS.toString(),
AccountLegislationEnum.ON.toString(),
AccountLegislationEnum.QC.toString(),
AccountLegislationEnum.SK.toString(),
AccountLegislationEnum.BC.toString(),
AccountLegislationEnum.FE.toString(),
AccountLegislationEnum.NT.toString(),
AccountLegislationEnum.PE.toString(),
AccountLegislationEnum.YT.toString(),
AccountLegislationEnum.NU.toString(),
AccountLegislationEnum.UNKNOWN.toString()));
This is how I am getting my model attribute as:
Attribute = appFormLiraLegislations
value = [com.abc.arch.core.gui.eform.gui.Option#199d1739, com.abc.arch.core.gui.eform.gui.Option#185fac52, com.abc.arch.core.gui.eform.gui.Option#312a47fe, com.abc.arch.core.gui.eform.gui.Option#4edc8de9, com.abc.arch.core.gui.eform.gui.Option#71e8e471, com.abc.arch.core.gui.eform.gui.Option#70edf123, com.abc.arch.core.gui.eform.gui.Option#15726ac1, com.abc.arch.core.gui.eform.gui.Option#abeafe7]
Thanks in advance.
You can make your life definitely easier when you correctly implement Option object hashCode() and equals() methods using key attribute; then you can simply write:
model().attribute("appFormLiraLegislations",getLiraLegislations(allLegislations)))
and rely on list1.equals(list2) method to do the work for you.
Option hashCode and equals implementation:
public class Option {
private String key;
private String label;
...
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Option other = (Option) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
}
Methods above are generated by my IDE. I also don't know exactly what is structure of your Option class, so I add label property for example in addition to key property.
I created a custom Hamcrest matcher to compare the List of Option by checking the size and the keys.
private Matcher<List<Option>> hasOptionsFeatureMatcher(
final List<Option> expectedOptions) {
return new FeatureMatcher<List<Option>, List<Option>>(
equalTo(expectedOptions), "Options are", "Options were") {
#Override
protected List<Option> featureValueOf(List<Option> actualOptions) {
boolean flag = false;
if (expectedOptions.size() == actualOptions.size()) {
for (Option expected : expectedOptions) {
for (Option actual : actualOptions) {
if (expected.getKey().equals(actual.getKey())) {
flag = true;
break;
} else {
flag = false;
}
}
}
} else
flag = false;
if (flag)
return expectedOptions;
else
return null;
}
};
Implementation would be as follows:
private static final ImmutableBiMap<String, String> LIRA = new ImmutableBiMap.Builder<String, String>()
.put(AccountLegislationEnum.AB.toString(), "ALBERTA")
.put(AccountLegislationEnum.MB.toString(), "MANITTOBA")
.put(AccountLegislationEnum.NB.toString(), "NEW BRUNSWICK")
.put(AccountLegislationEnum.NL.toString(), "NEWFOUNDLAND")
.put(AccountLegislationEnum.NS.toString(), "NOVA SCOTIA")
.put(AccountLegislationEnum.ON.toString(), "ONTARIO")
.put(AccountLegislationEnum.QC.toString(), "QUEBEC")
.put(AccountLegislationEnum.SK.toString(), "SASKATCHEWAN")
.put(AccountLegislationEnum.UNKNOWN.toString(), "UNKNOWN").build();
private List<Option> prepareOptions(ImmutableBiMap<String, String> map) {
List<Option> legislations = new ArrayList<Option>();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
Option option = new Option();
option.setKey(key);
option.setValue(value);
legislations.add(option);
}
return legislations;
}
.andExpect(model().attribute("appFormLiraLegislations",hasOptionsFeatureMatcher(prepareOptions(LIRA))))

Categories