Refactor multiple or conditions with java8 - java-8

I have a scenario to refactor multiple or conditions in if block with java8
private boolean isStatusAvailable(List<Hierarchy> hierarchyList, String status, int i) {
return isContainsStatus(hierarchyList.get(i+1), status) || isContainsStatus(hierarchyList.get(i+1), ResultStatus.SUCCESS) || isContainsStatus(hierarchyList.get(i+1), ResultStatus.PEND);
}
private boolean isContains(Hierarchy hierarchy, String status) {
return hierarchy.getStatus().contains(status);
}
public enum ResultStatus {
SUCCESS,
SUCCESS_PENDING,
SUCCESS_SUMMARY,
PEND
}
Is there any way to refactor the above code in java8?

There is always the possibility to introduce a loop like:
private boolean isStatusAvailable(List<Hierarchy> hierarchyList, ResultStatus status, int i) {
Hierarchy hierarchy = hierarchyList.get(i + 1);
for (ResultStatus s : Arrays.asList(status, SUCCESS, PEND)) {
if (isContainsStatus(hierarchy, s)) {
return true;
}
}
return false;
}
If you want to use Streams then you can write:
private boolean isStatusAvailable(List<Hierarchy> hierarchyList, ResultStatus status, int i) {
Hierarchy hierarchy = hierarchyList.get(i + 1);
return Stream.of(status, SUCCESS, PEND).anyMatch(s -> isContainsStatus(hierarchy, s));
}

Related

How to correctly chain Mono/Flux calls

I'm having trouble with understanding how to achieve my goal with reactive approach.
Let's assume that I have a Controller, that will return Flux:
#PostMapping(value = "/mutation/stream/{domainId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Mutation> getMutationReactive(#RequestBody List<MutationRequest> mutationRequests, #PathVariable Integer domainId) {
return mutationService.getMutations(mutationRequests, domainId);
}
In service, currently with .subscribeOn(Schedulers.boundedElastic()), because it calls for a blocking code that is wrapped into a Callable.
public Flux<Mutation> getMutations(List<MutationRequest> mutationRequests, int domainId) {
return Flux.fromIterable(mutationRequests)
.subscribeOn(Schedulers.boundedElastic())
.flatMap(mutationRequest -> getMutation(mutationRequest.getGameId(), mutationRequest.getTypeId(), domainId));
}
getMutation() with blocking calls, currently wrapped into a Callable:
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return Mono.fromCallable(() -> {
Mutation mutation = mutationProvider.findByGameIdAndTypeId(gameId, typeId).block(); // mutationProvider.findByGameIdAndTypeId() returns Mono<Mutation>
if (mutation == null) {
throw new RuntimeException("Mutation was not found by gameId and typeId");
}
State state = stateService.getStateByIds(mutation.getId()), domainId).blockFirst(); //stateService.getStateByIds() returns Mono<State>
if (state == null || state.getValue() == null) {
log.info("Requested mutation with gameId[%s] typeId[%s] domainId[%s] is disabled. Value is null.".formatted(gameId, typeId, domainId));
return null;
}
mutation.setTemplateId(state.getTemplateId());
return (mutation);
});
}
How do I approach the getMutation() function to use reactive streams, instead of using .block() methods inside a Callable?
Basically, I first need to retrieve Mutation from DB -> then using ID of mutation, get its state from other service -> then if state and its value are not null, set templateId of state to mutation and return, or return null.
I've tried something like this:
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return mutationProvider.findByGameIdAndTypeId(gameId, typeId)
.flatMap(mutation -> {
stateService.getStatesByIds(mutation.getId(), domainId).flatMap(state -> {
if (state != null && state.getValue() != null) {
mutation.setTemplateId(state.getTemplateId());
}
//TODO if state/value is null -> need to propagate further to return null instead of mutation...
return Mono.justOrEmpty(state);
});
return Mono.just(mutation);
});
}
But it's obviously incorrect, nothing is subscribed to stateService.getStatesByIds(mutation.getId()), domainId)
AND
I would like to return a null if the retrieved state of mutation or its value are null.
You are ignoring the value of the inner flatMap hence the warning.
Without trying you need something like this
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return mutationProvider.findByGameIdAndTypeId(gameId, typeId)
.flatMap(mutation -> {
return stateService.getStatesByIds(mutation.getId(), domainId).flatMap(state -> {
if (state != null && state.getValue() != null) {
mutation.setTemplateId(state.getTemplateId());
return Mono.just(mutation);
}
return Mono.empty();
});
});
}
Although not sure if you could rewrite the outer flatMap not to a regular map instead and you might want to use filter and defaultIfEmpty with that as well
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return mutationProvider.findByGameIdAndTypeId(gameId, typeId)
.flatMap(mutation -> {
return stateService.getStatesByIds(mutation.getId(), domainId)
.filter(state -> state != null && state.getValue() != null)
.flatMap(state -> {
mutation.setTemplateId(state.getTemplateId());
return Mono.just(mutation);})
.defaultIfEmpty(Mono.empty());
}
This is just from the top of my head and I have no idea what some of the return types are here (Flux or Mono) for your own APIs.

Not able to filter elements using an enum

protected static double averagePrice( List<ComputerComponent> list ) {
return list.stream()
// .filter( line-> EnumUtils.isValidEnum(ComputerComponentCategory.class, line.getCategory()) )
// .filter( line-> isInEnum( line.getCategory(), ComputerComponentCategory.class) )
// .filter( line-> inEnum(line.getCategory(),EnumUtils.getEnumMap(ComputerComponentCategory.class ).keySet() ))
.filter( line ->
line.getCategory().contains("CPU")
|| line.getCategory().contains("GPU")
|| line.getCategory().contains("Monitor")
|| line.getCategory().contains("Keyboard")
|| line.getCategory().contains("Mouse")
|| line.getCategory().contains("Storage")
|| line.getCategory().contains("Memory"))
.mapToDouble(ComputerComponent::getPrice)
.average()
.orElseThrow(NoSuchElementException:: new);
}
I have an enum as
public enum ComputerComponentCategory {
CPU("CPU"),
MONITOR("Monitor"),
KEYBOARD("Keyboard"),
MOUSE("Mouse"),
GPU("GPU"),
MEMORY("Memory"),
STORAGE("Storage"),
NULL("NOT DEFINED");
private String label;
ComputerComponentCategory(String label) {
this.label = label;
}
public String getLabel() {
return this.label;
}
public static ComputerComponentCategory getValue(String label) {
switch(label) {
case "CPU":
return CPU;
case "Monitor":
return MONITOR;
case "Keyboard":
return KEYBOARD;
case "Mouse":
return MOUSE;
case "GPU":
return GPU;
case "Memory":
return MEMORY;
case "Storage":
return STORAGE;
default:
return NULL ;
}
}
}
I pass a list of ComputerComponent class to the averagePrice() function which has two fields of price
which is of type double and
category which is of type String.
My list has 4 elements with categories as "CPU", "Mouse",
"Keyboard" and "Storage" with their respective prices as 34.0, 155.0, 23.0 and 75.0.
When I try to use inEnum(), isInEnum() or EnumUtils.isValidEnum() functions, I get the average price
as 34.0 which I think that they just return the price of the first element rather than the average.
But when I do filtering using
.filter( line ->
line.getCategory().contains("CPU")
|| line.getCategory().contains("GPU")
|| line.getCategory().contains("Monitor")
|| line.getCategory().contains("Keyboard")
|| line.getCategory().contains("Mouse")
|| line.getCategory().contains("Storage")
|| line.getCategory().contains("Memory"))
I get the correct average value of 71.75.
The implementations that I have used for isInEnum() and inEnum() functions are the following:
public static <E extends Enum<E>> boolean isInEnum(String value, Class<E> enumClass) {
for (E e : enumClass.getEnumConstants()) {
if(e.name().contains(value)) { return true; }
}
return false;
}
public static boolean inEnum ( String category, Set<String> value ) {
for(String s: value ) {
if ( category.contains(s) ) {
return true ;
}
}
return false ;
}
How can I use enums correctly with java streams to filter by valid category names and get the correct
average value of price?
What mistake I am making when using streams and its functions ?
You could simply use your ComputerCategoryValue.getValue method and check for null, given the category of line:
public class EnumTest {
#Test
public void testBothMethods() {
final ComputerComponent c1 = new ComputerComponent(ComputerComponentCategory.CPU.getLabel(), 12.21);
final ComputerComponent c2 = new ComputerComponent(ComputerComponentCategory.MEMORY.getLabel(), 23.45);
final List<ComputerComponent> list = Arrays.asList(c1, c2);
assertEquals(averagePriceWithFilter(list), averagePriceWithInEnum(list), 0.01);
}
protected static double averagePriceWithFilter(final List<ComputerComponent> list) {
return list.stream()
.filter(line -> line.getCategory().contains("CPU")
|| line.getCategory().contains("GPU")
|| line.getCategory().contains("Monitor")
|| line.getCategory().contains("Keyboard")
|| line.getCategory().contains("Mouse")
|| line.getCategory().contains("Storage")
|| line.getCategory().contains("Memory"))
.mapToDouble(ComputerComponent::getPrice)
.average()
.orElseThrow(NoSuchElementException::new);
}
protected static double averagePriceWithInEnum(final List<ComputerComponent> list) {
return list.stream()
.filter(line -> ComputerComponentCategory.getValue(line.getCategory()) != null)
.mapToDouble(ComputerComponent::getPrice)
.average()
.orElseThrow(NoSuchElementException::new);
}
}
EDIT: explaining your mistakes:
EnumUtils.getEnumMap(ComputerComponentCategory.class).keySet()) returns a map of the enum name (not its label), so that the check will only work for CPU as there name and label are the same.
Same for the other method!
You need to use getLabel() instead of name() or use equalsIgnoreCase instead of contains.

Spring Cache Key Using Arguments

I have a class Person with properties id, name and age.
I would like to cache Person object using id and name.
my method is
#Cacheable(value = "person", key = "#p.id + p.name")
getPerson(Person p).
Question is, how do i use cache annotation on getPerson()... something like this.
Using the annotation you could concatenate the values to create a key (I read but have not tested tha the debug symbols may be removed and so the parameter should be referenced as "p0").
#Cacheable(value="person", key="#p0.id.concat(‘:’).concat(#p0.name)")
Otherwise, it will be cached based on Person class equals() and hashCode() just the same way as if you were using the Person object as a key in a Map.
So, for example:
public class Person {
String id;
String name;
Number age;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Person))
return false;
Person other = (Person) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

Silverlight 5 validation issue

i'm trying to validate the data entry of my project using the validation in silverlight
this is the result
http://imageshack.us/photo/my-images/842/immagineleb.png/
as you can see the borders of almost all the textboxes are red, actually, in this case, no one of them should be red! And in all of the tooltips there's the same message.
there are the properties of the object that i use in the data context of the form
private int matricola;
public int Matricola
{
get { return matricola; }
set
{
ValidateRequiredInt("Matricola", value, "Inserire un numero");
matricola = value;
OnNotifyPropertyChanged("Matricola");
}
}
private String cognome;
public String Cognome
{
get { return cognome; }
set
{
ValidateRequiredString("Cognome", value, "Inserire cognome");
cognome = value;
OnNotifyPropertyChanged("Cognome");
}
}
private String nome;
public String Nome
{
get { return nome; }
set
{
ValidateRequiredString("Nome", value, "Inserire nome");
nome = value;
OnNotifyPropertyChanged("Nome");
}
}
private String codiceUtente;
public String CodiceUtente
{
get { return codiceUtente; }
set
{
ValidateRequiredString("CodiceUtente", value, "Inserire codice utente");
codiceUtente = value;
OnNotifyPropertyChanged("CodiceUtente");
}
}
private String password;
public String Password
{
get { return password; }
set
{
ValidateRequiredString("Password", value, "Inserire password");
password = value;
OnNotifyPropertyChanged("Password");
}
}
private int? idOrario;
public int? IdOrario
{
get { return idOrario; }
set { idOrario = value; }
}
private DateTime? dataAssunzione;
public DateTime? DataAssunzione
{
get { return dataAssunzione; }
set
{
if (value != null)
{
ValidateDateTime("DataAssunzione", (DateTime)value, "Immettere una data corretta");
if (((DateTime)value).Year == 1 && ((DateTime)value).Month == 1 && ((DateTime)value).Day == 1)
{
dataAssunzione = null;
}
else
{
dataAssunzione = value;
}
OnNotifyPropertyChanged("DataAssunzione");
}
else
{
dataAssunzione = null;
}
}
}
private DateTime? dataLicenziamento;
public DateTime? DataLicenziamento
{
get { return dataLicenziamento; }
set
{
if (value != null)
{
ValidateDateTime("DataLicenziamento", (DateTime)value, "Immettere una data corretta");
if (((DateTime)value).Year == 1 && ((DateTime)value).Month == 1 && ((DateTime)value).Day == 1)
{
dataLicenziamento = null;
}
else
{
dataLicenziamento = value;
}
OnNotifyPropertyChanged("DataLicenziamento");
}
else
{
dataLicenziamento = null;
}
}
}
private int idGruppoAnag;
public int IdGruppoAnag
{
get { return idGruppoAnag; }
set { idGruppoAnag = value; }
}
private int? costoOrario;
public int? CostoOrario
{
get { return costoOrario; }
set
{
if (value != null)
{
ValidateInt("CostoOrario", (int)value, "Immettere un numero o lasciare il campo vuoto");
if (value == 0 || value == -1)
{
costoOrario = null;
}
else
{
costoOrario = value;
}
OnNotifyPropertyChanged("CostoOrario");
}
else
{
costoOrario = null;
}
}
}
and these are the methods used for the validation
protected bool ValidateRequiredInt(string propName, int value, string errorText)
{
if (DataErrors.ContainsKey(propName))
{
DataErrors[propName].Remove(errorText);
}
if (value == 0 || value == -1)
{
AddError(propName, errorText);
return false;
}
OnErrorsChanged(propName);
return true;
}
//validazione dei campi che richiedono numeri interi nullable
protected bool ValidateInt(string propName, int value, string errorText)
{
if (DataErrors.ContainsKey(propName))
{
DataErrors[propName].Remove(errorText);
}
if (value == 0)
{
AddError(propName, errorText);
return false;
}
OnErrorsChanged(propName);
return true;
}
//validazione dei campi che richiedono stringhe
protected bool ValidateRequiredString(string propName, string value, string errorText)
{
//Clear any existing errors since we're revalidating now
if (DataErrors.ContainsKey(propName))
{
DataErrors[propName].Remove(errorText);
}
if (string.IsNullOrEmpty(value))
{
AddError(propName, errorText);
return false;
}
OnErrorsChanged(propName);
return true;
}
protected bool ValidateDateTime(string propName, DateTime value, string errorText)
{
//Clear any existing errors since we're revalidating now
if (DataErrors.ContainsKey(propName))
{
DataErrors[propName].Remove(errorText);
}
if (value.Year == 9999 && value.Month == 12 && value.Day == 31)
{
AddError(propName, errorText);
return false;
}
OnErrorsChanged(propName);
return true;
}
i'm also using using a dataconverter, in the two "Data" textboxes, and numberconverter, in the matricola and costo textboxes, as locals resources and i can say that they work fine.
i'm missing something?
How about implementing INotifyDataErrorInfo?
In a view-model base class:
public abstract class BaseViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region INotifyDataErrorInfo Members
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public System.Collections.IEnumerable GetErrors(string propertyName)
{
....
}
public System.Collections.IEnumerable GetErrors()
{
...
}
//Plus methods to push errors into an underlying error dictionary used by the above error get methods
}
Expand on this and you will have reusable base class for all view-models.
Validate properties in the appropriate setters. If they fail validation then push an error into the error dictionary keyed by property name. If validation succeeds then remove the validation error (if any) from the the dictionary for the property.
You will need to fire the ErrorsChanged event when you change the dictionary, but this can be achieved by having a protected
SetErrorForProperty(string propName, object error)
method whcih wraps this up.
Clearing an error can be done by passing null to this method and/or by having a separate
ClearErrorsFroProperty(string propName)
method.

SingleOrDefault: How to change the default values?

SingleOrDefault returns null, but what if I want to assign values to represent the object that wasn't found?
you can do something like
myStrings.DefaultIfEmpty("myDefaultString").Single()
check out here
?? operator. If the left argument is null, evaluate and return the second argument.
myCollection.SingleOrDefault() ?? new[]{new Item(...)}
This will only work with reference types (or nullables), but it would do what you're looking for very simply.
You could roll your own.
public static T SingleOrDefault<T>(this IEnumerable<T> enumerable, T defaultValue) {
if ( 1 != enumerable.Count() ) {
return defaultValue;
}
return enumerable.Single();
}
This can be a bit expensive though because Count() requires you to process the entire collection and can be fairly expensive to run. It would be better to either call Single, catch the InvalidOperationException or roll a IsSingle method
public static bool IsSingle<T>(this IEnumerable<T> enumerable) {
using ( var e = enumerable.GetEnumerator() ) {
return e.MoveNext() && !e.MoveNext();
}
}
public static T SingleOrDefault<T>(this IEnumerable<T> enumerable, T defaultValue) {
if ( !enumerable.IsSingle() ) {
if( enumerable.IsEmpty() ) {
return defaultValue;
}
throw new InvalidOperationException("More than one element");
}
return enumerable.Single();
}
You could create your own extension methods -- SingleOrNew.
public static class IEnumerableExtensions
{
public static T SingleOrNew<T>( this IEnumerable<T> enumeration, T newValue )
{
T elem = enumeration.SingleOrDefault();
if (elem == null)
{
return newValue;
}
return elem;
}
public static T SingleOrNew<T>( this IEnumerable<T> enumeration, Func<T,bool> predicate, T newValue )
{
T elem = enumeration.SingleOrDefault( predicate );
if (elem == null)
{
return newValue;
}
return elem;
}
}

Resources