Groovy : Convert java arrow operator "->" and "::" in Groovy

Hi Kataloners,

I would like to convert java arrow operator “->” in Groovy and the “::” :

folderId = result.getFiles().stream()
					.filter(file **->** file.getName().equals(__folderName))
                        .findFirst()
                        .map(File**::**getId)
                        .orElse(null);

Thank you

are you sure ‘::’ is a java operator?

It is, as of Java 9

Welcome to Katalon!

Here we use Groovy, which can be thought of as a dialect of Java.

I see you’re using functional programming. Here’s a couple things to take note of, right away:

  • Groovy has no lambdas. Instead, they have Closures. Their syntax is like this:
{ file -> return file.getName().equals(__folderName) }
1 Like

@moustick

Please provide a complete & runnable Java code which you want to translate into Groovy, including all import statements. If provided, I will try to translate it into Groovy and verify if it runs OK.

I ask this because I wonder what class the File in your code snippet is. I believe that java.io.File class does not implement getId() method. So I guess that your File class is something special, other than java.io.File. So what is it? I am puzzled. If your File class is a special one, then it would be difficult for me to write and test a Groovy script that runs on my side.

Ah … din’t knew that, looks like I am stuck in (pre) Java 8 ‘era’

Found some reference of such method into the File class for Google Drive.

@moustik will be a better idea to explain what do you intend to achieve (and provide
the reference to the script you found)
Sometime is faster / better to write a script from scratch instead of translating a certain snippet found on www.

If I understand the code right, the purpose of it is to filter a list of files and get only the first ocurence matching the criteria.

The equivalent in groovy will be find. Note that, with groovy, we can skip the usage of stream()

list.find {it.getName() == __folderName}

More examles about filtering in groovy are here:
Filtering

worth to read also:

so, asuming result.getFiles() produces a valid list, we can write a groovish example like:

folderId = result.files.find { it.name == __folderName }?.id

Note some groovy goodness:

  • getSomething() can be written in groovy as .something
  • we have to use the null safe operator when attempting to retrieve the id, e.g ?. since, find will return null if the result of it is empty
1 Like

I stand corrected, it is a Java 8 thing

Java example with the Double Collon Operator:

package my;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.Arrays;
import java.util.List;

public class StreamTest {
    @Test
    public void testSmoke() {
        File[] files = new File(".").listFiles();
        List<File> result = Arrays.asList(files);
        result.stream()
                .map(File::getName)
                .forEach(System.out::println);
    }
}

this emitted the following:

> Task :compileJava NO-SOURCE
> Task :compileGroovy NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileTestJava
> Task :compileTestGroovy NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
gradle
gradlew
build.gradle
.gradle
build
gradlew.bat
settings.gradle
.idea
src
BUILD SUCCESSFUL in 8s
2 actionable tasks: 2 executed
8:42:58: Execution finished ':test --tests "my.StreamTest.testSmoke"'.

Equivalent Groovy in a Katalon Studio Test Case:

// in Groovy
File dir = new File(".")
List<File> result = dir.listFiles() as List
result.stream()
      .map({File file -> file.getName() })
      .forEach({String name -> println name})

this emitted the following:

2023-02-12 08:40:20.039 INFO  c.k.katalon.core.main.TestCaseExecutor   - --------------------
2023-02-12 08:40:20.043 INFO  c.k.katalon.core.main.TestCaseExecutor   - START Test Cases/TC1
2023-02-12 08:40:20.751 DEBUG testcase.TC1                             - 1: dir = new java.io.File(.)
2023-02-12 08:40:20.761 DEBUG testcase.TC1                             - 2: result = dir.listFiles()
2023-02-12 08:40:20.768 DEBUG testcase.TC1                             - 3:  }).forEach({ java.lang.String name -> ... })
viewport.png
settings
page.html
.DS_Store
Drivers
server.groovy
bin
Test Listeners
Test Cases
Plugins
test.prj
Include
Checkpoints
console.properties
Test Suites
.classpath
docs
Libs
.gitignore
.settings
Object Repository
.project
Scripts
fullpage.png
build.gradle
.gradle
Profiles
.git
Keywords
tmp
.cache
Data Files
Reports
2023-02-12 08:40:20.898 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

Yeah, I spend some time and read about it. Shame for me for not knowing this up to now.
Well, I suppose, for the rest, it is only a matter of what the user like.
To use it in groovy (at least for the current 2.x katalon uses), it has to be first ‘translated’ as a lambda and furher translated as a closure.

I preffer to ‘groovyfi’ everything … most probably because, since some (long) time ago, I am mostly a Pyhton user.
So, speaking about myself, I found the groovy syntax more appealing. Is more ‘pythonic’ :slight_smile:
Strong typing is evil …
and yeah, i do shortcuts. sue me :stuck_out_tongue:

@kazurayam : I can’t get Groovy conversion to work in my code

This is my class : GoogleDriveService

package technicalFunctions

import static com.kms.katalon.core.checkpoint.CheckpointFactory.findCheckpoint
import static com.kms.katalon.core.testcase.TestCaseFactory.findTestCase
import static com.kms.katalon.core.testdata.TestDataFactory.findTestData
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import static com.kms.katalon.core.testobject.ObjectRepository.findWindowsObject

import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.checkpoint.Checkpoint
import com.kms.katalon.core.cucumber.keyword.CucumberBuiltinKeywords as CucumberKW
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as Mobile
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testcase.TestCase
import com.kms.katalon.core.testdata.TestData
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WS
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.windows.keyword.WindowsBuiltinKeywords as Windows

import internal.GlobalVariable

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
import technicalFunctions.GoogleDriveService;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class GoogleDriveService {

	private final String applicationName;
	private final String credentialsFilePath;
	private final String tokensDirectoryPath;
	private Drive driveService;

	private static final List<String> scopes = Arrays.asList(DriveScopes.DRIVE_READONLY);
	private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();

	private static final String fields = "nextPageToken, files(id, name, mimeType)";

	public GoogleDriveService(String applicationName, String credentialsFilePath, String tokensDirectoryPath) {
		this.applicationName = applicationName;
		this.credentialsFilePath = credentialsFilePath;
		this.tokensDirectoryPath = tokensDirectoryPath;
	}

	public void init(){

		try{

			final HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

			InputStream inp = new FileInputStream(credentialsFilePath);

			if(inp == null){
				System.out.println("Credentials file not found");
				return;
			}

			GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(inp));

			java.io.File tokensDirectory = new java.io.File(tokensDirectoryPath);

			if(!tokensDirectory.exists()){
				if(!tokensDirectory.mkdirs()){
					System.out.println("Unable to create tokens directory");
					return;
				}
			}

			// Build flow and trigger user authorization request.
			GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
			httpTransport, JSON_FACTORY, clientSecrets, scopes)
			.setDataStoreFactory(new FileDataStoreFactory(tokensDirectory))
			.setAccessType("offline")
			.build();

			LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();

			Credential authorizationCodeInstalledApp = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");

			driveService = new Drive.Builder(httpTransport, JSON_FACTORY, authorizationCodeInstalledApp)
			.setApplicationName(applicationName)
			.build();

		}catch(IOException | GeneralSecurityException e){
			e.printStackTrace();
		}
	}

	public void downloadFolder ( String folderPath, String destinationPath) {

		if (driveService == null) {
			System.out.println("Drive service not initialized");
			return;
		}

		try {

			List<String> folderTree = tokenize(folderPath);
			int size = folderTree.size();

			if (size == 0) {
				System.out.println("Invalid folder path");
				return;
			}

			String baseQ = "mimeType='application/vnd.google-apps.folder' and trashed = false and name=";

			String folderId;
			String folderName = folderTree.get(0);

			FileList result = driveService.files().list()
			.setQ(baseQ + "'" + folderName + "'")
			.setFields(fields)
			.execute();

			String _folderName = folderName;
			
			folderId = result.getFiles().stream().filter(file -> file.getName().equals(__folderName)).findFirst().map(File::getId).orElse(null);
			
			if (folderId == null) {
				System.out.println("Folder not found");
				return;
			}

			for (int i = 1; i < size; i++) {

				folderName = folderTree.get(i);

				result = driveService.files().list()
				.setQ(baseQ + "'" + folderName + "' and '" + folderId + "' in parents")
				.setFields(fields)
				.execute();

				String __folderName = folderName;
				//folderId = result.stream().map({File file -> file.getName() }).forEach({String name -> println name});
				folderId = result.getFiles().stream().filter(file -> file.getName().equals(__folderName)).findFirst().map(File::getId).orElse(null);

				if (folderId == null) {
					System.out.println("Folder not found");
					return;
				}
			}

			Path path = Paths.get(destinationPath, folderName);
			Files.createDirectories(path);

			result = driveService.files().list()
			.setQ("'" + folderId + "' in parents")
			.setFields(fields)
			.execute();

			List<File> files = result.getFiles();

			for (File file : files) {
				download(driveService, file, path);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static void download(Drive service, File file, Path destinationPath) {

		String mimeType = file.getMimeType();
		if (mimeType != null && mimeType.equals("application/vnd.google-apps.folder")) {
			FileList result;
			try {
				result = service.files().list()
				.setQ("'" + file.getId() + "' in parents")
				.setFields(fields)
				.execute();

				if (result != null) {

					//create Folder
					Path path = Paths.get(destinationPath.toString(), file.getName());
					Files.createDirectories(path);

					List<File> files = result.getFiles();

					for (File f : files) {
						download(service, f, path);
					}
				}

			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		else {
			try {

				InputStream inst = service.files().get(file.getId()).executeMediaAsInputStream();
				Path out = Paths.get(destinationPath.toString(), file.getName());
				Files.copy(inst, out, REPLACE_EXISTING);
				System.out.println("file downloaded: " + file.getName());

			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private static List<String> tokenize(String str) {
		return Arrays.asList(str.split("/"));
	}
}

and my Main class :

import static com.kms.katalon.core.checkpoint.CheckpointFactory.findCheckpoint
import static com.kms.katalon.core.testcase.TestCaseFactory.findTestCase
import static com.kms.katalon.core.testdata.TestDataFactory.findTestData
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import static com.kms.katalon.core.testobject.ObjectRepository.findWindowsObject
import com.kms.katalon.core.checkpoint.Checkpoint as Checkpoint
import com.kms.katalon.core.cucumber.keyword.CucumberBuiltinKeywords as CucumberKW
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as Mobile
import com.kms.katalon.core.model.FailureHandling as FailureHandling
import com.kms.katalon.core.testcase.TestCase as TestCase
import com.kms.katalon.core.testdata.TestData as TestData
import com.kms.katalon.core.testng.keyword.TestNGBuiltinKeywords as TestNGKW
import com.kms.katalon.core.testobject.TestObject as TestObject
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WS
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.windows.keyword.WindowsBuiltinKeywords as Windows
import internal.GlobalVariable as GlobalVariable
import org.openqa.selenium.Keys as Keys
import technicalFunctions.GoogleDriveService;

//String id = "1lXeG5nWa9uPyPqSPUQKYrVch7JiQvcjBtOMbohieick";
//result6 = DownloadFile.getdownloadFile(id);

result6 = MainGDS.main();
public class MainGDS {
	

	static final String applicationName = "gcp-nefertiti";
	static final String credentialsFilePath = "C:/Users/soukna/nefertiti_auto/client_secret.json";
	static final String tokensDirectoryPath = "tokens";

	public static void main(String[] args) {

		GoogleDriveService googleDriveService = new GoogleDriveService(applicationName, credentialsFilePath,
				tokensDirectoryPath);

		googleDriveService.init();

		String folder = "Nefertiti";
		String destinationPath = "C:\\Users\\soukna\\Desktop";
		googleDriveService.downloadFolder(folder, destinationPath);
			
	}
}


This function works with Eclipse but not in Katalon because it’s with Groovy.
I would like to convert this line in Groovy :

folderId = result.getFiles().stream().filter(file -> file.getName().equals(__folderName)).findFirst().map(File::getId).orElse(null);
			

Ah … chh, looks like we have a language barrier here and the OP is using a certain translate feature.

@moustik have you tried my 'groovy` approach?

folderId = result.files.find { it.name == __folderName }?.id

Yep i Try :

And i have the error :
“Folder not found”

bcause folderId is null

and what do you think this means?

if (folderId == null) {
System.out.println(“Folder not found”);
return;
}

This part come by the template of GoogleDrive which are in my older post

you have a problem there

I made a Java code in other IDE as this:

package moustik;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class StreamTest {

    @Test
    public void testSmoke() throws IOException {
        Path dir;
        dir = Paths.get(".");
        String firstFolderName;
        firstFolderName =
                Files.list(dir)
                        .filter(path -> Files.isDirectory(path))
                        .filter(path -> path.getFileName().toString().equals("src"))
                        .findFirst()
                        .map(Path::getFileName)
                        .map(Path::toString)
                        .orElse(null);
        System.out.println(firstFolderName);
    }
}

This Java code worked fine as this:


> Task :compileJava NO-SOURCE
> Task :compileGroovy NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileTestJava
> Task :compileTestGroovy NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
src
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed
9:14:45: Execution finished ':test --tests "moustik.StreamTest.testSmoke"'.

I mad a Test Case in Katalon Studio, which is a translation into Groovy:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path dir
dir = Paths.get(".")
String firstFolderName
firstFolderName =
		Files.list(dir)
				.filter({path -> Files.isDirectory(path)})
				.filter({path -> path.getFileName().toString().equals("Test Cases")})
				.findFirst()
				.map({Path p -> p.getFileName()})
				.map({Path p -> p.toString()})
				.orElse(null)
System.out.println(firstFolderName)

It worked fine as this:

2023-02-15 09:11:40.909 INFO  c.k.katalon.core.main.TestCaseExecutor   - --------------------
2023-02-15 09:11:40.913 INFO  c.k.katalon.core.main.TestCaseExecutor   - START Test Cases/moustik
2023-02-15 09:11:41.534 DEBUG testcase.moustik                         - 1: dir = <not implemented yet for class: org.codehaus.groovy.ast.expr.EmptyExpression>
2023-02-15 09:11:41.539 DEBUG testcase.moustik                         - 2: dir = Paths.get(".")
2023-02-15 09:11:41.554 DEBUG testcase.moustik                         - 3: firstFolderName = <not implemented yet for class: org.codehaus.groovy.ast.expr.EmptyExpression>
2023-02-15 09:11:41.559 DEBUG testcase.moustik                         - 4: firstFolderName =  }).orElse(null)
2023-02-15 09:11:41.722 DEBUG testcase.moustik                         - 5: out.println(firstFolderName)
Test Cases
2023-02-15 09:11:41.772 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/moustik

This experiment proves that there is no problem in translating a Java with Lambdas and Double Colon operators into Groovy with Closures.

Your code is designed to print a “Folder not found” message when the folderId variable is null.
Your code worked fine (as designed) because it reported “Filder not fond”. No longer you have a programming problem how to translate Java to Groovy.

Now your problem is simply why the folderId variable got null.

There must be some factors, other than “Java vs Groovy”, that make your Test Case to find the folderId being null. For example, the test fixture (data in your Google Drive folder) is not as clean as it should be; or you possibly gave wrong path information to your Test Case; etc.

Only you can investigate this issue further; the guys in this forum would not be able to debug your environment remotely.

To debug it, you will have to:
( i will use my example here)

result.files.find { it.name == __folderName }

inspect the result variable - what kind of object is? does it have a getFiles() method? what is the result of that method applied to this object?

if yes

result.files - it is a list of files? a certain file object have a getName() method?

also, a certain File object have a getId method?

and also check the __folderName variable, it has a propper value? it is matching to a certain file in the list?

For Kazurayam’s version you will need more steps, but the same principle apply.

My best guess, one of:

  • result.files (result.getFiles()) is an empty list
    or
  • the value of __folderName variable does not match the name of any file in the list, therefore the result of find is null

In any other case the code should break and throw an exception, unless the failure is “hidden” by a generic catch (awfull to use such, it is also known as bug hidding)

Alternate, you can translate the portion of code you mentioned as per Kazurayam guidance.
Replace the lambda’s and the :: occurences with closures, for the rest keep the code as it is.