java 8 java.util.function.Function downcast - java-8

The below java function is assignable (f(Order) to f(Object))
Function<Order, Order> orderProcessor = (Order order) -> {
System.out.println("Processing Order:"
return order;
};
Function f = orderProcessor;
The question is how do i cast this function back to Function < Order,Order> ?
or better I would like to cast this function back to Function < SomeInterface,SomeInterface>
I am storing these functions in a List< Function> but ideally i would like to store them in a List < SomeInterface,SomeInterface>.
Is it possible ?

you can define a List takes Functions with 2 generic parameters ? extends SomeInterface. then no need casting at all, for example:
List<Function<? extends SomeInterface, ? extends SomeInterface>> functions =
new ArrayList<>();
Function<Order, Order> orderProcessor = (Order order) -> {
System.out.println("Processing Order:" + order);
return order;
};
functions.add(orderProcessor);
IF the result type is same as parameter type, you also can using a UnaryOperator instead, for example:
List<UnaryOperator<? extends SomeInterface>> functions = new ArrayList<>();
UnaryOperator<Order> orderProcessor = (Order order) -> {
System.out.println("Processing Order:" + order);
return order;
};
functions.add(orderProcessor);
THEN you can't accept any SomeInterface at all, since the Function<? extends SomeInterface> can take any other subtypes not only for the Order class.
IF you want your Functions can accept all of its subtypes, you must declare it as write mode ? super SomeInterface, and then you must change the UnaryOperator<Order> definition, for example:
List<UnaryOperator<? super SomeInterface>> functions = new ArrayList<>();
UnaryOperator<SomeInterface> orderProcessor = (SomeInterface item) -> {
if(item instanceof Order){
System.out.println("Processing Order:" + order);
}
return item;
};
functions.add(orderProcessor);
here is a solution wrap a Map in Processors then you don't need any casting or instanceof statement in UnaryOperator body, for example:
Processors processors = new Processors();
UnaryOperator<Order> orderProcessor = (order) -> {
// no need instanceof & casting expression here
System.out.println("Processing Order:" + order);
return order;
};
processors.add(Order.class, orderProcessor);
processors.listing(Order.class).forEach(it -> it.apply(new Order()));
class Processors {
private final Map<Class<?>, List<UnaryOperator<?>>> registry = new HashMap<>();
public <T extends SomeInterface> void add(Class<T> type,
UnaryOperator<T> processor) {
registry.computeIfAbsent(type, aClass -> new ArrayList<>()).add(processor);
}
#SuppressWarnings("unchecked")
public <T extends SomeInterface>
Stream<UnaryOperator<T>> listing(Class<T> type){
return (Stream<UnaryOperator<T>>) lookup(type).stream();
}
private List<?> lookup(Class<?> type) {
if (!SomeInterface.class.isAssignableFrom(type))
return Collections.emptyList();
if (!registry.containsKey(type))
return registry.get(type.getSuperclass());
return registry.get(type);
}
}

Related

Java stream : convert list of one object to other

I am trying to learn map function in Stream
public class EmployeeInformationTest {
public static void main(String args[]) {
List<Employee> employees = Arrays.asList(
new Employee("Jai"),
new Employee("Adithya"),
new Employee("Raja"));
List<String> names = employees.stream()
.map(s -> s.getEmployeeName()) // Lambda Expression
.collect(Collectors.toList());
System.out.println(names);
}
}
we have above code and somehow it is giving us List of String from List of Employee. Say, we have other class Person in which we have field as name
public class Person {
private String name;
}
so is it feasible via map or some other function in stream so that I can get the List of Person rather than List of String in above code
sure thing, just change the map function to:
.map(s -> new Person(s.getEmployeeName()))
or if there is no such constructor:
.map(s -> {
Person p = new Person();
p.setName(s.getEmployeeName());
return p;
})

Paging and sorting over Collection

I want to apply paging and sorting to ArrayList of photos. The list is retrived by rest client. Here is my attempt to paging but returns all 5k elements instead. I try to achive a paging like in JpaRepository. The sorting is done by compareTo() method, and doesn't seem to work properly either.
PhotoServiceImpl.java
private List<Photo> repository;
public PhotoServiceImpl() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Photo>> photoResponse = restTemplate.exchange("https://jsonplaceholder.typicode.com/photos", HttpMethod.GET, null, new ParameterizedTypeReference<List<Photo>>() {
});
this.repository = photoResponse.getBody();
}
#Override
public List<Photo> findAll() {
return repository;
}
#Override
public Page<Photo> findAll(Pageable pageable) {
List<Photo> photos = findAll();
return new PageImpl<Photo>(photos, new PageRequest(pageable.getPageNumber(), pageable.getPageSize()), photos.size());
}
#Override
public Page<Photo> findAll(Pageable pageable, Sort.Direction sortOrder) {
List<Photo> photos = findAll();
return new PageImpl<Photo>(photos, new
PageRequest(pageable.getPageNumber(), pageable.getPageSize(), sortOrder), photos.size());
}
#Override
public List<Photo> findAll(Sort.Direction sortOrder) {
List<Photo> photos = repository
.stream()
.sorted()
.collect(Collectors.toList());
if (sortOrder.isDescending())
Collections.reverse(photos);
return photos;
}
Photo.java implements Comparable
private int id;
private int albumId;
private String title;
private URL url;
private URL thumbnailUrl;
#Override
public int compareTo(Object o) {
Photo out = ((Photo) o);
int c;
c = Integer.compare(this.getId(), out.getId());
if (c == 0)
c = Integer.compare(this.getAlbumId(), out.getAlbumId());
if (c == 0)
c = this.getTitle().compareTo((out.getTitle()));
return c;
}
}
Getting all photos and the wrapping that in a PageRequest instance with the page size and sorting set will not do what you want. The PageRequest class (or PageImpl class) does not perform the slicing of a list of data into a page or perform the sorting. You must do that yourself
List<Photo> photos = findAll();
//
// << Add your page extraction and sorting code here >>
//
return new PageImpl<Photo>(photos,
new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), sortOrder), photos.size());

How to select several properties for specific name

I am working on a web project using Spring and Spring MVC.
I have a feature that is the same for 3 different elements (which are available in dropdown in view). Only two parameters change for each item. I decided to put these elements and parameters in a .properties file to permit the user change them. So for example in my .properties I have the following:
FC
fcUuid=11111111111111111
fcTag=tag1
AC
itUuid=22222222222222222
itTag=tag2
IT
acUuid=333333333333333333
acTag=tag3
For the moment I am able to retrieve each element separately.
For example:
String communityUuid = SpringPropertiesUtil.getProperty("fcUuid");
(SpringPropertiesUtil extends PropertyPlaceholderConfigurer)
But my question is: how can I retrieve all the parameters relative to one element?
For example the user selects "FC", how in my service layer can I retrieve both fcUuid and fcTag parameters?
Of course I can do something like:
if(param="FC"){
String communityUuid = SpringPropertiesUtil.getProperty("fcUuid");
String communityTag = SpringPropertiesUtil.getProperty("fcTag");
} else if (param="AC"){...}
But I don't want to do that because the user can add elements so I would have to modify the code each time.
I would like something like:
String communityUuid = SpringPropertiesUtil.getProperties(param[0]);
String tagUuid = SpringPropertiesUtil.getProperties(param[1]);
Or even better:
String communityUuid = SpringPropertiesUtil.getProperties(param[uuid]);
String tagUuid = SpringPropertiesUtil.getProperties(param[tag]);
You need customize how to handle properties into map that you need. You can do like :
#group your properites
uiValues=\
FC={fcUuid:11111111111111111},{fcTag : tag1}&&\
AC={itUuid : 22222222222222222},{itTag : tag2}&&\
IT={acUuid:333333333333333333},{acTag:tag3}
#Component
public class ConfigProperties {
//FC=...&&AC=....&&IT=....
private static final String GROUP_SPLITTER = "&&";
private static final String GROUP_VALUES_MARKER = "=";
private static final String START_VALUES_IN_GROUP = "{";
private static final String END_VALUES_IN_GROUP = "}";
private static final String VALUES_SPLITTER= ",";
private static final String KEY_VALUE_SPLITTER= ":";
#Value("#{T(current current package .ConfigProperties).
decodeMap('${uiValues}')}")
private Map<String,Values> map;
/**
if(param="FC"){
String communityUuid = SpringPropertiesUtil.getProperty("fcUuid");
String communityTag = SpringPropertiesUtil.getProperty("fcTag");
}
#Autowired
ConfigProperties configProperties;
String communityUuid = configProperties.getValue("FC","fcUuid");
String communityTag = configProperties.getValue("FC","fcTag");
*/
public String getValue(String key , String property){
//add check for null
Values values= map.get(key);
if (values == null){
return "";
}
for (Tuple tuple : values.tuples){
if (tuple.key.equals(property)){
return tuple.value;
}
}
return "";
}
public List<String> getProperties(String key){
//add check for null
List<String> properties = new ArrayList<>();
Values values= map.get(key);
//add check for null
for (Tuple tuple : values.tuples){
properties.add(tuple.key);
}
return properties;
}
public static Map<String, Values> decodeMap(String value) {
//add validator for value format
boolean isValid = true;
if(!isValid){
return new HashMap<>();
}
Map<String, Values> map = new LinkedHashMap<>();
String[] groups = value.split(GROUP_SPLITTER);
for (String group : groups) {
String[] values = splitToKeyAndValues(group.split(GROUP_VALUES_MARKER)[1]);
String key = group.substring(0,group.indexOf(GROUP_VALUES_MARKER));
map.put(key, getValues(values));
}
return map;
}
private static Values getValues(String[] parts) {
Values values = new Values();
for (int i=0;i<parts.length;i++){
values.tuples.add(getTuple(parts[i]));
}
return values;
}
private static Tuple getTuple(String parts) {
Tuple tuple = new Tuple();
parts = parts.substring(1,parts.length()-1);
tuple.key= parts.split(KEY_VALUE_SPLITTER)[0];
tuple.value= parts.split(KEY_VALUE_SPLITTER)[1];
return tuple;
}
static String[] splitToKeyAndValues(String valuesInGroup) {
return valuesInGroup.split(VALUES_SPLITTER);
}
}
class Values{
List<Tuple> tuples = new ArrayList<>();
}
class Tuple{
String key;
String value;
}
With the help of one of my colleagues I managed to realize that. This is how I proceeded:
In my .properties file I changed the data format, now it looks like:
#FC
clientApplications[0].name=FC
clientApplications[0].communityId=00000000000000
clientApplications[0].tag=tag0
#AC
clientApplications[1].name=AC
clientApplications[1].communityId=11111111111111
clientApplications[1].tag=tag1
etc...
I created a bean named ClientApplication (FC, AC and IT are applications) with 3 attributes (name, communityId and tag)
I created a class named ApplicationStore that stores all the applications present in the propertiesfile in the form of ClientApplication objects and that provides a get method which returns a ClientApplication according to the name of the app.
#Component("applicationStore")
public class ApplicationStore {
private Map<String, ClientApplication> map;
public void put(String key, ClientApplication value) {
map.put(key, value);
}
public ClientApplication get(String key) {
return map.get(key);
}
public ApplicationStore() {
int i = 0;
map = new HashMap<String, ClientApplication>();
while (SpringPropertiesUtil.getProperty("clientApplications[" + i + "].name") != null) {
ClientApplication ca = new ClientApplication();
ca.setName(SpringPropertiesUtil.getProperty("clientApplications[" + i + "].name"));
ca.setCommunityId(SpringPropertiesUtil.getProperty("clientApplications[" + i + "].communityId"));
ca.setTag(SpringPropertiesUtil.getProperty("clientApplications[" + i + "].tag"));
map.put(ca.getName(), ca);
i++;
}
}
}
With that I only have to add this to my service layer:
#Service("aService")
public class AServiceImpl implements AService {
#Autowired
private ApplicationStore apps;
private String communityUuid;
private String communityTag;
#Override
public void aMethod(String appName) trhows Exception {
ClientApplication ca = new ClientApplication();
ca = apps.get(appName);
communityUuid = ca.getCommunityId();
communityTag = ca.getTag();
System.out.println("Application for key " + app + " : " + ca);
System.out.println("communityUuid: " + communityUuid);
System.out.println("communityTag:" + communityTag);
}
}

Get list of properties from collection Into unified list using Guava

My company requires that we use Guava's functions & predicates whenever we loop through a collection of objects. While I don't necessarily agree that it should be this way, it's my companies standard so please don't give me comments about how it's not efficient or things like that :)
I need, using Guava in Java 1.7 specifically, to find a way to take a collection of objects, and get all of the inner fields into a unified list. This is as far as I've gotten but I have not been able to get inner fields:
public static final Function<Group, String> GROUP_TO_GROUP_NAME =
new Function<Group, String>()
{
#Override
public String apply(Group group)
{
return group.getGroupName();
}
};
public static List<String> transformToGroupNames(List<GroupCollection> groupCollections)
{
List<Group> groups = new ArrayList<>();
for (GroupCollection groupCollection: groupCollections)
{
groups.addAll(groupCollection.getGroups());
}
return FluentIterable.from(groups)
.transform(GROUP_TO_GROUP_NAME)
.filter(Predicates.notNull())
.toList();
}
My goal is to replace the loop through the groupCollections
To flatten the group collections to a single group collection you can use FluentIterable.transformAndConcat(Function):
public static final Function<GroupCollection, List<Group>> GROUP_COLLECTIONS_TO_GROUP_COLLECTION =
new Function<GroupCollection, List<Group>>() {
#Override
public List<Group> apply(GroupCollection input) {
return input.getGroups();
}
};
public static final Function<Group, String> GROUP_TO_GROUP_NAME =
new Function<Group, String>() {
#Override
public String apply(Group group) {
return group.getGroupName();
}
};
public static List<String> transformToGroupNames(List<GroupCollection> groupCollections) {
return FluentIterable.from(groupCollections)
.transformAndConcat(GROUP_COLLECTIONS_TO_GROUP_COLLECTION)
.transform(GROUP_TO_GROUP_NAME)
.filter(Predicates.notNull())
.toList();
}

Spring -Mongodb storing/retrieving enums as int not string

My enums are stored as int in mongodb (from C# app). Now in Java, when I try to retrieve them, it throws an exception (it seems enum can be converted from string value only). Is there any way I can do it?
Also when I save some collections into mongodb (from Java), it converts enum values to string (not their value/cardinal). Is there any override available?
This can be achieved by writing mongodb-converter on class level but I don't want to write mondodb-converter for each class as these enums are in many different classes.
So do we have something on the field level?
After a long digging in the spring-mongodb converter code,
Ok i finished and now it's working :) here it is (if there is simpler solution i will be happy see as well, this is what i've done ) :
first define :
public interface IntEnumConvertable {
public int getValue();
}
and a simple enum that implements it :
public enum tester implements IntEnumConvertable{
vali(0),secondvali(1),thirdvali(5);
private final int val;
private tester(int num)
{
val = num;
}
public int getValue(){
return val;
}
}
Ok, now you will now need 2 converters , one is simple ,
the other is more complex. the simple one (this simple baby is also handling the simple convert and returns a string when cast is not possible, that is great if you want to have enum stored as strings and for enum that are numbers to be stored as integers) :
public class IntegerEnumConverters {
#WritingConverter
public static class EnumToIntegerConverter implements Converter<Enum<?>, Object> {
#Override
public Object convert(Enum<?> source) {
if(source instanceof IntEnumConvertable)
{
return ((IntEnumConvertable)(source)).getValue();
}
else
{
return source.name();
}
}
}
}
the more complex one , is actually a converter factory :
public class IntegerToEnumConverterFactory implements ConverterFactory<Integer, Enum> {
#Override
public <T extends Enum> Converter<Integer, T> getConverter(Class<T> targetType) {
Class<?> enumType = targetType;
while (enumType != null && !enumType.isEnum()) {
enumType = enumType.getSuperclass();
}
if (enumType == null) {
throw new IllegalArgumentException(
"The target type " + targetType.getName() + " does not refer to an enum");
}
return new IntegerToEnum(enumType);
}
#ReadingConverter
public static class IntegerToEnum<T extends Enum> implements Converter<Integer, Enum> {
private final Class<T> enumType;
public IntegerToEnum(Class<T> enumType) {
this.enumType = enumType;
}
#Override
public Enum convert(Integer source) {
for(T t : enumType.getEnumConstants()) {
if(t instanceof IntEnumConvertable)
{
if(((IntEnumConvertable)t).getValue() == source.intValue()) {
return t;
}
}
}
return null;
}
}
}
and now for the hack part , i personnaly didnt find any "programmitacly" way to register a converter factory within a mongoConverter , so i digged in the code and with a little casting , here it is (put this 2 babies functions in your #Configuration class)
#Bean
public CustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new IntegerEnumConverters.EnumToIntegerConverter());
// this is a dummy registration , actually it's a work-around because
// spring-mongodb doesnt has the option to reg converter factory.
// so we reg the converter that our factory uses.
converters.add(new IntegerToEnumConverterFactory.IntegerToEnum(null));
return new CustomConversions(converters);
}
#Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setApplicationContext(appContext);
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mappingContext);
mongoConverter.setCustomConversions(customConversions());
ConversionService convService = mongoConverter.getConversionService();
((GenericConversionService)convService).addConverterFactory(new IntegerToEnumConverterFactory());
mongoConverter.afterPropertiesSet();
return mongoConverter;
}
You will need to implement your custom converters and register it with spring.
http://static.springsource.org/spring-data/data-mongo/docs/current/reference/html/#mongo.custom-converters
Isn't it easier to use plain constants rather than an enum...
int SOMETHING = 33;
int OTHER_THING = 55;
or
public class Role {
public static final Stirng ROLE_USER = "ROLE_USER",
ROLE_LOOSER = "ROLE_LOOSER";
}
String yourRole = Role.ROLE_LOOSER

Resources