Converting nested for loop with if conditions to Java 8 - java

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);

Related

Aggregation and grouping using Hazelcast jet

I am trying to do grouping and aggregation using Hazelcast Jet but it is getting little bit slow as I have to loop through data twice one for creating groupingKey and after that aggregating all data so is there any better and feasible way to it, Please help
Here is my code first I am creating a groupingKey from using my data as grouping is done by multiple keys:
// Fields with which I want to do grouping.
List<String> fields1 = {"Field1", "Field4"};
List<String> cAggCount = {"CountFiled"};
List<String> sumField = {"SumFiled"};
BatchStage<Map<Object, List<Object>>> aggBatchStageDataGroupBy = batchStage
.aggregate(AggregateOperations.groupingBy(jdbcData -> {
Map<String, Object> m = ((Map<String, Object>)jdbcData);
Set<String> jset = m.keySet();
StringBuilder stringBuilder = new StringBuilder("");
fields1.stream().forEach(dataValue -> {
if (!jset.contains(dataValue.toString())) {
stringBuilder.append(null+"").append(",");
} else {
Object k = m.get(dataValue.toString());
if (k == null) {
stringBuilder.append("").append(",");
} else {
stringBuilder.append(k).append(",");
}
}
});
return stringBuilder.substring(0, stringBuilder.length() - 1);
}));
And after that I am doing aggregation on it as below:
BatchStage<List<Map<String, Object>>> aggBatchStageData = aggBatchStageDataGroupBy
.map(data -> {
data.entrySet().stream().forEach(v -> {
Map<String, Object> objectMap = new HashMap<>();
IntStream.range(0, cAggCount).forEach(k -> {
objectMap.put(countAlias.get(k), v.getValue().stream().mapToLong(dataMap -> {
return new BigDecimal(1).intValue();
}).count());
});
}
return mapList;
});
So can we do this whole process in one go instead of doing loop twice like groupigByKey first and than aggregating it.

Can we pass value and annotation of method parameters

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.

How to rewrite code using stream and optional

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));

How to set multiple conditional opearator in java8

I am trying to convert the below code in java 8, but not sure where I am going wrong. I have 2 code snippets which I want to convert. This is the first one:
for (WebElement value :values) {
WebElement dateElement = SharedWebDriver.getInstance()
.findOptionalElement(By.className("text"), value);
WebElement groupElement =
SharedWebDriver.getInstance().findOptionalElement(By.id("label"),
value);
WebElement typeElement =
SharedWebDriver.getInstance().findOptionalElement(By.id("type"),
value);
if (dateElement != null) {
dateValue = dateElement.getText().trim();
}
if (groupElement != null) {
groupValue = groupElement.getText().trim();
}
if(typeElement!= null){
typeValue = typeElement.getText().trim();
}
}
And here I want to set value using java 8. I tried it with using the filter option, but it's not working.
for (WebElement header : headers) {
if (header != null) {
if (header.getText().equals("A")) {
entry.setDate(dateValue);
} else if (header.getText().equals("B")) {
entry.setGroup(groupValue);
} else if (header.getText().equals("C")) {
entry.setType(typeValue);
}
}
}
Can anyone help me?
The problem with those code snippets is that they modifiy variables defined outside of the loop (dateValue, groupValue and typeValue for the first one, and entry for the second one).
But lambda expressions are not really supposed to alter variables that are not defined in their scope event though you can achieve that throught methods.
For example, inside a lambda expression :
word = "hello" will not work whereas website.setTitle("title") will
I converted your code snippets in Java 8, I didn't take the time to test it but if I am if i am not mistaken, the first one will not work whereas the second one will, for the reason explained above.
values.stream()
.map(value -> new WebElement[] {
SharedWebDriver.getInstance().findOptionalElement(By.className("text"), value),
SharedWebDriver.getInstance().findOptionalElement(By.id("label"), value)),
SharedWebDriver.getInstance().findOptionalElement(By.id("type"), value) })
.forEach(webElements[] -> {
if (webElements[0] != null) {
dateValue = webElements[0].getText().trim();
}
if (webElements[1] != null) {
groupValue = webElements[1].getText().trim();
}
if(webElements[2] != null){
typeValue = webElements[2].getText().trim();
}
});
headers.stream()
.filter(Objects::nonNull)
.forEach(header -> {
if (header.getText().equals("A")) {
entry.setDate(dateValue);
} else if (header.getText().equals("B")) {
entry.setGroup(groupValue);
} else if (header.getText().equals("C")) {
entry.setType(typeValue);
}
});

Best way to get first not null value using optionals in Java

We have code like this:
String tempDir = SwingInstanceManager.getInstance().getTempFolderPath(clientId);
if (tempDir == null) {
tempDir = System.getProperty(Constants.TEMP_DIR_PATH);
if (tempDir == null) {
tempDir = new File(System.getProperty("java.io.tmpdir")).toURI().toString();
}
}
I want to remove brackets, so if it was only 2 values I'd write like this:
String tempDir = Optional.ofNullable(SwingInstanceManager.getInstance().getTempFolderPath(clientId)).orElse(System.getProperty(Constants.TEMP_DIR_PATH));
But is there any way to write such chain for 3+ values?(withount using second optional in orElse call)
Since your second option is actually a property, you can rely on the getProperty(String, String) method rather than just getProperty(String):
String tempDir = Optional.ofNullable(SwingInstanceManager.getInstance().getTempFolderPath(clientId))
.orElse(System.getProperty(Constants.TEMP_DIR_PATH,
new File(System.getProperty("java.io.tmpdir")).toURI().toString());
Though I'd recommend using Path rather than File in that latter part (Paths.get(System.getProperty("java.io.tmpdir")).toURI().toString())
You could use an ordered List and pick the first non-null item from it.
String[] tempSourcesArray = {null, "firstNonNull", null, "otherNonNull"};
List<String> tempSourcesList = Arrays.asList(tempSourcesArray);
Optional firstNonNullIfAny = tempSourcesList.stream().filter(i -> i != null).findFirst();
System.out.println(firstNonNullIfAny.get()); // displays "firstNonNull"
Try this.
public static <T> T firstNotNull(Supplier<T>... values) {
for (Supplier<T> e : values) {
T value = e.get();
if (value != null)
return value;
}
return null;
}
and
String tempDir = firstNotNull(
() -> SwingInstanceManager.getInstance().getTempFolderPath(clientId),
() -> System.getProperty(Constants.TEMP_DIR_PATH),
() -> new File(System.getProperty("java.io.tmpdir")).toURI().toString());

Categories

Resources