I'm trying to rewrite code with nested conditions using Optional and Stream. That's how he looked:
if (transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION") != null) {
editObj = (EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION");
} else {
if (editObj != null) {
editObj = editObj.getEditInstance(transaction);
} else {
editObj = HOME.newEmployeeWorkstation(compId);
}
}
I tried to rewrite so:
editObj =
ofNullable(
(EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.orElse(
editObj != null
? editObj.getEditInstance(transaction)
: HOME.newEmployeeWorkstation(compId));
And it works fine but my mentor said that it can be simplified
then I tried so:
editObj =
Optional.ofNullable(
(EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.map(obj -> obj.getEditInstance(transaction))
.orElse(HOME.newEmployeeWorkstation(compId));
I understand that my .map() does not work as described above in the first versions. How can I rewrite .map so that it works as described above?
You can use a nested Optional:
EmployeeWorkstation edit = Optional.ofNullable((EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.orElseGet(() -> Optional.ofNullable(editObj)
.map(e -> e.getEditInstance(transaction))
.orElseGet(() -> HOME.newEmployeeWorkstation(compId)));
If you are using Java 9 or higher you can use Optional.or():
EmployeeWorkstation edit = Optional.ofNullable((EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.or(() -> Optional.ofNullable(editObj).map(edit -> edit.getEditInstance(transaction)))
.orElseGet(() -> HOME.newEmployeeWorkstation(compId));
Related
how can i write this in java 8 please :
ListeTiers tiers = wsTiers.rechercheTiers(serviceContext, params);
if(null != tiers) {
List<Tiers> listeTiers = tiers.getTiers();
for(TiersP t : listeTiers) {
if(id.equals(t.getI())) {
if(t.getActivite != null) {
if(t.getActivite.getCode() != null)
result.setCode(t.getActivite().getCode());
}
}
}
}
You wouldn't - the operation isn't functional (you're calling a setter and mutating state).
You can hack it, but this shouldn't be considered an improvement compared to what you already have. Also, you have a field named getActivite? And also a getter with the same name? That's... crazy.
public void updateResultCode(Something wsTiers) {
ListeTiers tiers = wsTiers.rechercheTiers(serviceContext, params);
if (null != tiers) {
tiers.getTiers().stream()
.filter(t -> id.equals(t.getI())
.filter(t -> t.getActivite != null)
.map(t -> t.getActivite.getCode())
.filter(c -> c != null)
.findFirst()
.ifPresent(c -> result.setCode(c);
}
}
I'd write it like this:
public void updateResultCode(Something wsTiers) {
ListeTiers tiers = wsTiers.rechercheTiers(serviceContext, params);
if (tiers == null) return;
for(TiersP t : tiers.getTiers()) {
if(!id.equals(t.getI())) continue;
if(t.getActivite == null || t.getActivite.getCode() == null) continue;
result.setCode(t.getActivite().getCode());
break;
}
}
Something like the following:
tiers.getTiers()
.stream()
.filter(t -> id.equals(t.getI()))
.filter(t -> t.getActivite().getCode() != null)
.forEach(t -> result.setCode(t.getActivite().getCode()));
Maybe like that
ListeTiers tiers = wsTiers.rechercheTiers(serviceContext, params);
if(null != tiers) {
List<Tiers> listeTiers = tiers.getTiers()
.stream()
.filter(t -> id.equals(t.getI()))
.map(TiersP::getActivite)
.filter(Objects::nonNull)
.map(Activite::getCode) // Or however your class is named
.filter(Objects::nonNull)
.foreach(c -> result.setCode(c));
}
I need to convert the code below from java 7 to java 8.
I tried using the class 'Optional' to simplify it but haven't succeeded implementing it.
if(replacementResponse.getGroupResponse() != null)
{
for(GroupCommand groupCommand : replacementResponse.getGroupResponse().getValues())
{
String groupQuery = groupCommand.getName();
for(Group group : groupCommand.getValues())
{
if(!group.getResult().isEmpty())
{
SolrDocumentList solrDocuments = group.getResult();
List<SolrDocument> documentList = null;
if(result.get(groupQuery) != null)
documentList = result.get(groupQuery);
else
documentList = new LinkedList<>();
for(SolrDocument solrDocument : solrDocuments)
{
documentList.add(solrDocument);
}
result.put(groupQuery, documentList);
}
}
}
return result;
}
return null;
I tried splitting the method into 2 methods but I don't know how to implement 'Optional' inside streaming.
return Optional.ofNullable(replacementResponse.getGroupResponse())
.map(replacementGroupResponse -> getGroupResponse(replacementGroupResponse.getValues())).orElse(null);
documentList = Optional.ofNullable(result.get(groupCommand.getName())).orElse(new LinkedList<>());
replacementGroupResponse.stream()
.map(groupCommand -> groupCommand.getValues().stream()
.filter(group -> !group.getResult().isEmpty()).
You can try as following to migrate your java 7 code to java 8. There might be few issues because I don't know about the implementation of many classes like SolrDocumentList,GroupCommand,Group, etc..
return Optional.ofNullable(replacementResponse.getGroupResponse()).map(a -> a.getValues().stream()
.collect(Collectors.toMap(GroupCommand::getName,groupCommand -> groupCommand.getValues().stream()
.flatMap(group -> group.getResult().stream()).collect(Collectors.toCollection(LinkedList::new))))).orElse(null);
To manage swagger documentations I am using custom annotations for the methods which call the API
#SwagRef(method = POST, url = "/my/api/{pathParam1}")
public Response callMyAPI(
#MyParam(name = "pathParam1", required = true, in = PATH) String p1,
#MyParam(name = "param2", required = false, in = QUERY) String p2) {
return given()
.pathParam("pathParam1", p1)
.queryParam("param2", p2)
.get();
}
There is a separate piece of code which validates the Swagger/api/docs vs the annotations.
However I'm wondering is it possible to somehow use all this already presented data in the annotations and have a common code where I can pass the method reference or the parameter reference and the RequestSpecification can be built using the annotations.
I tried with reflection, but I'm unable to fetch the value of parameters using reflection from method
I was only able to deduce the method type and API since it's constant using the methodName and stackTrace
private SwagRef defineSwaggerInfo() {
List<StackTraceElement> stackTrace = asList(currentThread().getStackTrace());
return stackTrace.stream()
.map(tryOrNull(element -> Pair.with(element.getMethodName(), forName(element.getClassName()))))
.filter(Objects::nonNull)
.filter(pair -> MyAPI.class.isAssignableFrom(pair.getValue1()))
.map(pair -> with(pair.getValue0(), asList(pair.getValue1().getDeclaredMethods())))
.flatMap(
tryOrNull(
pair ->
pair.getValue1().stream()
.filter(method -> Objects.equals(method.getName(), pair.getValue0()))
.peek(method -> method.setAccessible(true))
.map(method -> method.getAnnotation(SwagRef.class))))
.filter(Objects::nonNull)
.findFirst()
.orElseThrow();
}
But I'm not able to come up with a generic function for Building the request spec using method parameters
I tried looking at AspectJ but wasn't able to embed it properly
There is no way to get the actual parameter values from the stack via Reflection. In fact, there’s not even a guaranty that the parameter values of an ongoing invocation are still on the stack at that point.
The closest you can get to perform automated parameter processing, is to declare the methods in an interface and generate a proxy:
interface FrontEnd {
public static FrontEnd get() {
return (FrontEnd)Proxy.newProxyInstance(FrontEnd.class.getClassLoader(),
new Class<?>[]{FrontEnd.class}, (proxy, method, args) -> {
if(method.getDeclaringClass() == Object.class) {
switch(method.getName()) {
case "toString": return
FrontEnd.class.getName()+'#'+System.identityHashCode(proxy);
case "equals": return proxy == args[0];
case "hashCode": return System.identityHashCode(proxy);
default: throw new AssertionError();
}
}
SwagRef swagRef = method.getAnnotation(SwagRef.class);
if(swagRef == null) throw new IncompatibleClassChangeError();
MyParam[] p = Arrays.stream(method.getParameterAnnotations())
.map(pa -> Arrays.stream(pa)
.filter(a -> a.annotationType() == MyParam.class)
.findFirst().orElseThrow(
() -> new IllegalStateException("missing required #MyParam")))
.toArray(MyParam[]::new);
Map<String,String> map = IntStream.range(0, args.length).boxed()
.filter(i -> p[i].required() || args[i] != null)
.collect(Collectors.toMap(i -> p[i].name(), i -> args[i].toString()));
// do actual invocation logic here
System.out.println(
"operation: "+swagRef.method()+' '+swagRef.url()+", "+map);
return null;
});
}
#SwagRef(method = POST, url = "/my/api/{pathParam1}")
public Response callMyAPI(
#MyParam(name = "pathParam1", required = true, in = PATH) String p1,
#MyParam(name = "param2", required = false, in = QUERY) String p2);
}
You may add more methods to that interface, to be handled the same way, assuming that they all have the necessary annotations.
Starting with Java 9, you can use a private method in the interface, which I would prefer here.
interface FrontEnd {
public static FrontEnd get() {
return (FrontEnd)Proxy.newProxyInstance(FrontEnd.class.getClassLoader(),
new Class<?>[]{FrontEnd.class}, FrontEnd::callImpl);
}
#SwagRef(method = POST, url = "/my/api/{pathParam1}")
public Response callMyAPI(
#MyParam(name = "pathParam1", required = true, in = PATH) String p1,
#MyParam(name = "param2", required = false, in = QUERY) String p2);
private static Object callImpl(Object proxy, Method method, Object[] args) {
if(method.getDeclaringClass() == Object.class) {
switch(method.getName()) {
case "toString": return
FrontEnd.class.getName()+'#'+System.identityHashCode(proxy);
case "equals": return proxy == args[0];
case "hashCode": return System.identityHashCode(proxy);
default: throw new AssertionError();
}
}
SwagRef swagRef = method.getAnnotation(SwagRef.class);
if(swagRef == null) throw new IncompatibleClassChangeError();
MyParam[] p = Arrays.stream(method.getParameterAnnotations())
.map(pa -> Arrays.stream(pa)
.filter(a -> a.annotationType() == MyParam.class)
.findFirst().orElseThrow(
() -> new IllegalStateException("missing required #MyParam")))
.toArray(MyParam[]::new);
Map<String,String> map = IntStream.range(0, args.length).boxed()
.filter(i -> p[i].required() || args[i] != null)
.collect(Collectors.toMap(i -> p[i].name(), i -> args[i].toString()));
// do actual invocation logic here
System.out.println("operation: "+swagRef.method()+' '+swagRef.url()+", "+map);
return null;
}
}
Alternatively, you may split up the logic between the interface and a, possibly non-public, helper class.
I have a basic SpringBoot app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file
I have this piece of code to compare POJOs, but the compartor seems not to work because lastDeviceEvent and firstDeviceEvent are the same object with the same ID
DeviceEvent lastDeviceEvent = null;
DeviceEvent firstDeviceEvent = null;
try {
lastDeviceEvent = deviceEvents
.stream()
.filter (o -> o.getId().equals(deviceId))
.sorted(comparing((DeviceEvent de) -> de.getId()).reversed())
.findFirst().get();
firstDeviceEvent = deviceEvents
.stream()
.filter (o -> o.getId().equals(deviceId))
.sorted(comparing((DeviceEvent de) -> de.getId()))
.findFirst().get();
LOG.info("lastDeviceEvent --> " + lastDeviceEvent.getId());
LOG.info("firstDeviceEvent -> " + firstDeviceEvent.getId());
} catch (NoSuchElementException nse) {
throw new AccessDeniedException("403 Forbidden");
}
The comparator seems correct. The problem seems to be in your filter clause, where you compare the event id to the device id
lastDeviceEvent = deviceEvents
.stream()
.filter (o -> o.getDeviceId().equals(deviceId)) // Original code used getId()
.sorted(comparing((DeviceEvent de) -> de.getId()).reversed())
.findFirst()
.get();
Someone can tell me how can I reuse rootOpt object inside of my forEach. Is there any way to reuse this variable? I have the following message "Can not resolve symbol rootOpt" when I write rootOpt.getChildOptions() inside my forEach. Please find below what I did:
I have tried to rewrite the for loop below by using stream. Thank you
opts.stream()
.flatMap(rootOpt -> rootOpt.getChildOptions().stream())
.forEach(subOpt -> {
if (subOpt.getOptLogic() != null && subOpt.getOptLogic().getCant() != null && !"".equals(subOpt.getOptLogic().getCant())) {
String[] oldCHs = subOpt.getOptLogic().getCant().split("( )");
OptionList samePriceSibs = getSamePriceS(rootOpt.getChildOptions(), subOpt);
for (String ch : oldCHs) {
Option chRootOpt = childOptCodeToParentOptMap.get(ch.toUpperCase());
if (chRootOpt != null) {
if (!DoesVariableOptionsCompletelyExcludeOther(samePriceSibs, chRootOpt.getChildOptions())) {
List<OptionList> tmp = new ArrayList<OptionList>();
tmp.add(samePriceSibs);
tmp.add(chRootOpt.getChildOptions());
optionsPairsToRemoveCHs.add(tmp);
}
}
}
}
});
for (Option rootOpt : opts) {
for (Option subOpt : rootOpt.getChildOptions()) {
if (subOpt.getOptLogic() != null && subOpt.getOptLogic().getCant() != null && !"".equals(subOpt.getOptLogic().getCant())) {
String[] oldCHs = subOpt.getOptLogic().getCant().split("( )");
OptionList samePriceSibs = getSamePriceS(rootOpt.getChildOptions(), subOpt);
for (String ch : oldCHs) {
Option chRootOpt = childOptCodeToParentOptMap.get(ch.toUpperCase());
if (chRootOpt != null) {
if (!DoesVariableOptionsCompletelyExcludeOther(samePriceSibs, chRootOpt.getChildOptions())) {
List<OptionList> tmp = new ArrayList<OptionList>();
tmp.add(samePriceSibs);
tmp.add(chRootOpt.getChildOptions());
optionsPairsToRemoveCHs.add(tmp);
}
}
}
}
}
}
The scope of rootOpt ends at the closing parenthesis.
You could write it like this instead
opts.stream().forEach(rootOpt ->
rootOpt.getChildOptions().stream().forEach(subOpt -> {
...
});
);
However streams were not really intended to simply replace for loops. A more canonical way of using them would be something like this.
Stream<List<OptionList>> optionsPairsToRemoveCHs = opts.stream()
.flatMap(rootOpt ->
rootOpt.getChildOptions().stream()
.filter(subOpt -> subOpt.getOptLogic() != null && subOpt.getOptLogic().getCant() != null && !"".equals(subOpt.getOptLogic().getCant()))
.flatMap(subOpt -> {
String[] oldCHs = subOpt.getOptLogic().getCant().split("( )");
OptionList samePriceSibs = getSamePriceS(rootOpt.getChildOptions(), subOpt);
return Stream.of(oldCHs)
.map(ch -> childOptCodeToParentOptMap.get(ch.toUpperCase()))
.filter(chRootOpt -> chRootOpt != null && !DoesVariableOptionsCompletelyExcludeOther(samePriceSibs, chRootOpt.getChildOptions()))
.map(chRootOpt -> Arrays.asList(samePriceSibs, chRootOpt.getChildOptions()));
})
);
I didn't test that code though. Also refactoring it into several methods as mike suggested would help making it easier to read.