##// END OF EJS Templates
Added OS tools to resolve exec path, minor changes
cin -
r20:8e171ef3106f default
parent child
Show More
@@ -0,0 +1,52
1 package org.implab.gradle.common.exec;
2
3 import static java.util.Objects.requireNonNull;
4
5 import java.util.Optional;
6
7 import org.eclipse.jdt.annotation.NonNullByDefault;
8 import org.implab.gradle.common.utils.Values;
9
10 @NonNullByDefault
11 public interface CommandArgumentsBuilder<S extends CommandArgumentsBuilder<S>> {
12
13 S self();
14
15 default S flag(String name, boolean value) {
16 requireNonNull(name, "Parameter name cannot be null");
17 if (value)
18 addArguments(name);
19 return self();
20 }
21
22 default S param(String name, String value) {
23 requireNonNull(name, "Parameter name cannot be null");
24 requireNonNull(value, "Parameter value cannot be null");
25 if (value != null && value.length() > 0)
26 addArguments(name, value);
27 return self();
28 }
29
30 default S param(String name, Optional<String> value) {
31 value.ifPresent(v -> param(name, v));
32 return self();
33 }
34
35 /** Adds the specified arguments to this builder */
36 S addArguments(String... args);
37
38 default S addArguments(Iterable<String> args) {
39 for (String arg : args)
40 addArguments(arg);
41
42 return self();
43 }
44
45 /** Replaces arguments in the builder with the specified one. */
46 default S arguments(String... args) {
47 return arguments(Values.iterable(args));
48 }
49
50 /** Replaces arguments in the builder with the specified one. */
51 S arguments(Iterable<String> args);
52 }
@@ -0,0 +1,40
1 package org.implab.gradle.common.exec;
2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 import org.gradle.api.Action;
5
6 @NonNullByDefault
7 public class ShellTool {
8
9 private final Shell shell;
10
11 private final String executable;
12
13 ShellTool(String executable, Shell shell) {
14 this.shell = shell;
15 this.executable = executable;
16 }
17
18 public ShellExec arguments(String... args) {
19 return shell.create(CommandSpec.builder()
20 .executable(executable)
21 .arguments(args)
22 .build());
23 }
24
25 public ShellExec arguments(Iterable<String> args) {
26 return shell.create(CommandSpec.builder()
27 .executable(executable)
28 .arguments(args)
29 .build());
30 }
31
32 public ShellExec arguments(Action<CommandArgumentsBuilder<?>> args) {
33 var builder = CommandSpec.builder()
34 .executable(executable);
35 args.execute(builder);
36
37 return shell.create(builder.build());
38 }
39
40 }
@@ -0,0 +1,35
1 package org.implab.gradle.common.utils;
2
3 import java.io.File;
4 import java.util.Optional;
5
6 import org.implab.gradle.common.utils.os.SystemResolver;
7
8 public interface OperatingSystem {
9
10 public static OperatingSystem CURRENT = SystemResolver.current();
11
12 public static final String WINDOWS_FAMILY = "windows";
13
14 public static final String LINUX_FAMILY = "linux";
15
16 public static final String FREE_BSD_FAMILY = "freebsd";
17
18 public static final String MAC_OS_FAMILY = "os x";
19
20 public static final String UNKNOWN_FAMILY = "unknown";
21
22 String family();
23
24 String name();
25
26 String version();
27
28 Optional<File> which(String cmd);
29
30 Optional<File> which(String cmd, Iterable<? extends File> paths);
31
32 public static OperatingSystem current() {
33 return CURRENT;
34 }
35 }
@@ -0,0 +1,16
1 package org.implab.gradle.common.utils.os;
2
3 import org.implab.gradle.common.utils.OperatingSystem;
4
5 public class FreeBsd extends GenericSystem{
6
7 FreeBsd(String name, String version) {
8 super(name, version);
9 }
10
11 @Override
12 public String family() {
13 return OperatingSystem.FREE_BSD_FAMILY;
14 }
15
16 }
@@ -0,0 +1,64
1 package org.implab.gradle.common.utils.os;
2
3 import java.io.File;
4 import java.util.Arrays;
5 import java.util.Optional;
6 import java.util.function.Function;
7 import java.util.regex.Pattern;
8 import java.util.stream.Stream;
9
10 import org.implab.gradle.common.utils.OperatingSystem;
11 import org.implab.gradle.common.utils.Values;
12
13 class GenericSystem implements OperatingSystem {
14
15 private final String name;
16
17 private final String version;
18
19 GenericSystem(String name, String version) {
20 this.name = name;
21 this.version = version;
22 }
23
24 public String getPathVar() {
25 return "PATH";
26 }
27
28 @Override
29 public String family() {
30 return OperatingSystem.UNKNOWN_FAMILY;
31 }
32
33 @Override
34 public String name() {
35 return name;
36 }
37
38 @Override
39 public String version() {
40 return version;
41 }
42
43 protected Stream<File> getPath() {
44 String path = System.getenv(getPathVar());
45 return path == null
46 ? Stream.empty()
47 : Arrays.stream(path.split(Pattern.quote(File.pathSeparator)))
48 .map(File::new);
49 }
50
51 protected Function<File, Stream<File>> candidates(String cmd) {
52 return base -> Stream.of(new File(base, cmd));
53 }
54
55 @Override
56 public Optional<File> which(String cmd) {
57 return getPath().flatMap(candidates(cmd)).filter(File::isFile).findAny();
58 }
59
60 @Override
61 public Optional<File> which(String cmd, Iterable<? extends File> paths) {
62 return Values.stream(paths.iterator()).flatMap(candidates(cmd)).filter(File::isFile).findAny();
63 }
64 }
@@ -0,0 +1,16
1 package org.implab.gradle.common.utils.os;
2
3 import org.implab.gradle.common.utils.OperatingSystem;
4
5 public class Linux extends GenericSystem {
6
7 Linux(String name, String version) {
8 super(name, version);
9 }
10
11 @Override
12 public String family() {
13 return OperatingSystem.LINUX_FAMILY;
14 }
15
16 }
@@ -0,0 +1,15
1 package org.implab.gradle.common.utils.os;
2
3 import org.implab.gradle.common.utils.OperatingSystem;
4
5 public class MacOs extends GenericSystem {
6
7 MacOs(String name, String version) {
8 super(name, version);
9 }
10
11 @Override
12 public String family() {
13 return OperatingSystem.MAC_OS_FAMILY;
14 }
15 }
@@ -0,0 +1,29
1 package org.implab.gradle.common.utils.os;
2
3 import org.implab.gradle.common.utils.OperatingSystem;
4
5 public class SystemResolver {
6 public static OperatingSystem current() {
7 var osName = System.getProperty("os.name");
8 var osVersion = System.getProperty("os.version");
9
10 return forName(osName, osVersion);
11 }
12
13 public static OperatingSystem forName(String os, String version) {
14 var osName = os.toLowerCase();
15
16 if (osName.contains("windows")) {
17 return new Windows(osName, version);
18 } else if (osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx")) {
19 return new MacOs(osName, version);
20 } else if (osName.contains("linux")) {
21 return new Linux(osName, version);
22 } else if (osName.contains("freebsd")) {
23 return new FreeBsd(osName, version);
24 } else {
25 // Not strictly true
26 return new GenericSystem(osName, version);
27 }
28 }
29 }
@@ -0,0 +1,26
1 package org.implab.gradle.common.utils.os;
2
3 import java.io.File;
4 import java.util.function.Function;
5 import java.util.stream.Stream;
6
7 import org.implab.gradle.common.utils.OperatingSystem;
8
9 class Windows extends GenericSystem {
10
11 private Stream<String> exeSuffixes = Stream.of(".cmd", ".bat", ".exe");
12
13 Windows(String name, String version) {
14 super(name, version);
15 }
16
17 @Override
18 public String family() {
19 return OperatingSystem.WINDOWS_FAMILY;
20 }
21
22 @Override
23 protected Function<File, Stream<File>> candidates(String cmd) {
24 return base -> exeSuffixes.map(suffix -> new File(base, cmd + suffix));
25 }
26 }
@@ -1,85 +1,63
1 1 package org.implab.gradle.common.exec;
2 2
3 import java.util.function.Consumer;
3 import java.io.File;
4 4
5 5 import org.eclipse.jdt.annotation.NonNullByDefault;
6 6 import org.implab.gradle.common.utils.Values;
7 7
8 8 /** Command builder interface, used to specify the executable and parameters */
9 9 @NonNullByDefault
10 public interface CommandBuilder {
10 public interface CommandBuilder extends CommandArgumentsBuilder<CommandBuilder> {
11
12 @Override
13 default CommandBuilder self() {
14 return this;
15 }
11 16
12 17 /** Sets the executable, the parameters are left intact. */
13 18 CommandBuilder executable(String executable);
14 19
20 default CommandBuilder executable(File executable) {
21 executable(executable.toString());
22 return this;
23 }
24
15 25 /**
16 26 * Sets the specified executable and parameters, old executable and parameters
17 27 * are discarded.
18 28 */
19 29 default CommandBuilder commandLine(String executable, String... args) {
20 30 return executable(executable)
21 31 .arguments(args);
22 32 }
23 33
24 34 /**
25 35 * Sets the specified executable and parameters, old executable and parameters
26 36 * are discarded.
27 37 *
28 38 * @param command The command line. Must contain at least one element
29 39 * (executable).
30 40 *
31 41 */
32 42 default CommandBuilder commandLine(Iterable<? extends String> command) {
33 43 var iterator = command.iterator();
34 44
35 45 // set executable
36 46 executable(Values.take(iterator).orElseThrow());
37 47 // cleat arguments
38 48 arguments();
39 49
40 50 // set new arguments
41 51 while (iterator.hasNext())
42 52 addArguments(iterator.next());
43 53
44 54 return this;
45 55 }
46 56
47 default Consumer<Boolean> flag(String name) {
48 return f -> {
49 if (f)
50 addArguments(name);
51 };
52 }
53
54 default Consumer<String> param(String name) {
55 return v -> {
56 if (v != null && v.length() > 0)
57 addArguments(name, v);
58 };
59 }
60
61 /** Adds the specified arguments to this builder */
62 CommandBuilder addArguments(String... args);
63
64 default CommandBuilder addArguments(Iterable<String> args) {
65 for (String arg : args)
66 addArguments(arg);
67
68 return this;
69 }
70
71 /** Replaces arguments in the builder with the specified one. */
72 default CommandBuilder arguments(String... args) {
73 return arguments(Values.iterable(args));
74 }
75
76 /** Replaces arguments in the builder with the specified one. */
77 CommandBuilder arguments(Iterable<String> args);
78
79 57 default CommandBuilder from(CommandSpec commandSpec) {
80 58 return executable(commandSpec.executable())
81 59 .arguments(commandSpec.arguments());
82 60 }
83 61
84 62 CommandSpec build();
85 63 }
@@ -1,20 +1,23
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.util.List;
4 4 import java.util.stream.Stream;
5 5
6 import org.eclipse.jdt.annotation.NonNullByDefault;
7
8 @NonNullByDefault
6 9 public interface CommandSpec {
7 10
8 11 String executable();
9 12
10 13 List<String> arguments();
11 14
12 15 default List<String> commandLine() {
13 16 return Stream.concat(Stream.of(executable()), arguments().stream()).toList();
14 17 }
15 18
16 19 public static CommandBuilder builder() {
17 20 return new CommandSpecRecord.Builder();
18 21 }
19 22
20 23 }
@@ -1,47 +1,51
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.util.List;
4
5 import org.eclipse.jdt.annotation.NonNullByDefault;
6
4 7 import static java.util.Objects.requireNonNull;
5 8
6 9 import java.util.ArrayList;
7 10
11 @NonNullByDefault
8 12 public record CommandSpecRecord(String executable, List<String> arguments) implements CommandSpec {
9 13
10 14 static class Builder implements CommandBuilder {
11 15
12 16 private String executable;
13 17
14 18 private List<String> arguments = new ArrayList<>();
15 19
16 20 @Override
17 21 public CommandBuilder executable(String executable) {
18 22 requireNonNull(executable, "cmd can't be null");
19 23 this.executable = executable;
20 24 return this;
21 25 }
22 26
23 27 @Override
24 28 public CommandBuilder arguments(Iterable<String> args) {
25 29 requireNonNull(args, "Args must not be null");
26 30 arguments.clear();
27 31 for (var arg : args)
28 32 arguments.add(arg);
29 33 return this;
30 34 }
31 35
32 36 @Override
33 37 public CommandBuilder addArguments(String... args) {
34 38 for (var arg : args)
35 39 arguments.add(requireNonNull(arg, "arguments element shouldn't be null"));
36 40
37 41 return this;
38 42 }
39 43
40 44 @Override
41 45 public CommandSpec build() {
42 46 requireNonNull(executable, "Executable must be specified");
43 47 return new CommandSpecRecord(executable, arguments);
44 48 }
45 49
46 50 }
47 51 }
@@ -1,40 +1,43
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.io.ByteArrayInputStream;
4 4 import java.io.IOException;
5 5 import java.nio.charset.StandardCharsets;
6 6 import java.util.concurrent.CompletableFuture;
7 7 import java.util.stream.Collectors;
8 8
9 import org.eclipse.jdt.annotation.NonNullByDefault;
10
11 @NonNullByDefault
9 12 class EchoExecBuilder extends AbstractExecBuilder<CommandSpec> {
10 13
11 14 private final boolean echoToStderr;
12 15
13 16 public EchoExecBuilder(CommandSpec command, boolean echoToStderr) {
14 17 super(command);
15 18 this.echoToStderr = echoToStderr;
16 19 }
17 20
18 21 @Override
19 22 protected CompletableFuture<Integer> startInternal(
20 23 CommandSpec command,
21 24 EnvironmentSpec environment,
22 25 PipeSpec redirect) throws IOException {
23 26
24 27 var outputRedirect = echoToStderr ? redirect.stderr() : redirect.stdout();
25 28
26 29 return outputRedirect
27 30 .map(to -> {
28 31 var bytes = String
29 32 .format(
30 33 "exec: %s",
31 34 command.commandLine().stream().collect(Collectors.joining(" ")))
32 35 .getBytes(StandardCharsets.UTF_8);
33 36
34 37 return to.redirect(new ByteArrayInputStream(bytes))
35 38 .thenApply((x) -> 0);
36 39 })
37 40 .orElse(CompletableFuture.completedFuture(0));
38 41 }
39 42
40 43 }
@@ -1,14 +1,17
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.io.File;
4 4 import java.util.Map;
5 5 import java.util.Optional;
6 6
7 import org.eclipse.jdt.annotation.NonNullByDefault;
8
9 @NonNullByDefault
7 10 public interface EnvironmentSpec {
8 11
9 12 boolean inheritEnvironment();
10 13
11 14 Map<String,String> environment();
12 15
13 16 Optional<File> workingDirectory();
14 17 }
@@ -1,53 +1,56
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.io.File;
4 4 import java.io.FileInputStream;
5 5 import java.io.InputStream;
6 6 import java.io.OutputStream;
7 7 import java.util.concurrent.CompletableFuture;
8 8
9 import org.eclipse.jdt.annotation.NonNullByDefault;
10
9 11 /**
10 12 * Describes how to redirect input streams. This interface is used to configure
11 13 * lazy redirection. {@link #redirect(OutputStream)} is called when the process
12 14 * is started. Before the process is started the redirection isn't invoked and
13 15 * no resources are allocated or used.
14 16 */
17 @NonNullByDefault
15 18 public interface RedirectFrom {
16 19 CompletableFuture<Void> redirect(OutputStream to);
17 20
18 21 /**
19 22 * Read file contents and redirect it to the output stream.
20 23 */
21 24 public static RedirectFrom file(final File file) {
22 25 return to -> CompletableFuture.runAsync(() -> {
23 26 try (var from = new FileInputStream(file); to) {
24 27 from.transferTo(to);
25 28 } catch (Exception e) {
26 29 // silence!
27 30 }
28 31 });
29 32 }
30 33
31 34 public static RedirectFrom stream(final InputStream from) {
32 35 return to -> CompletableFuture.runAsync(() -> {
33 36 try (from; to) {
34 37 from.transferTo(to);
35 38 } catch (Exception e) {
36 39 // silence!
37 40 }
38 41 });
39 42 }
40 43
41 44 public static RedirectFrom any(final Object output) {
42 45 if (output instanceof File f) {
43 46 return file(f);
44 47 } else if (output instanceof InputStream stm) {
45 48 return stream(stm);
46 49 } else if (output instanceof RedirectFrom self) {
47 50 return self;
48 51 } else {
49 52 throw new IllegalArgumentException("The specified argument type isn't supported: " + output.getClass());
50 53 }
51 54 }
52 55
53 56 }
@@ -1,22 +1,29
1 1 package org.implab.gradle.common.exec;
2 2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4
5 @NonNullByDefault
3 6 public interface Shell {
4 7
5 ShellExec of(CommandSpec spec);
8 ShellExec create(CommandSpec spec);
9
10 default ShellTool tool(String executable) {
11 return new ShellTool(executable, this);
12 }
6 13
7 14 /** Creates a new shell to start processes using ProcessBuilder. */
8 15 static Shell system() {
9 16 return spec -> new SystemExecBuilder(spec);
10 17 }
11 18
12 19 /** Creates a stub shell which echoes the specified command line. */
13 20 static Shell echo() {
14 21 return spec -> new EchoExecBuilder(spec, false);
15 22 }
16 23
17 24 /** Creates a stub shell which echoes the specified command line. */
18 25 static Shell echoStderr() {
19 26 return spec -> new EchoExecBuilder(spec, false);
20 27 }
21 28
22 29 }
@@ -1,36 +1,38
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.io.IOException;
4 4 import java.util.ArrayList;
5 5 import java.util.List;
6 6 import java.util.concurrent.CompletableFuture;
7 7
8 import org.eclipse.jdt.annotation.NonNullByDefault;
8 9 import org.implab.gradle.common.utils.Exceptions;
9 10
11 @NonNullByDefault
10 12 public interface ShellExec extends PipeBuilder, EnvironmentBuilder {
11 13
12 14 default CompletableFuture<List<String>> readAllLines() throws IOException {
13 15 List<String> lines = new ArrayList<>();
14 16 stdout(RedirectTo.consumer(lines::add));
15 17
16 18 return exec()
17 19 .thenAccept(Exceptions.unchecked(this::assertExitCode))
18 20 .thenApply(v -> lines);
19 21 }
20 22
21 23 default CompletableFuture<String> readAllText() throws IOException {
22 24 List<String> lines = new ArrayList<>();
23 25 stdout(RedirectTo.consumer(lines::add));
24 26
25 27 return exec()
26 28 .thenAccept(Exceptions.unchecked(this::assertExitCode))
27 29 .thenApply(v -> String.join("\n", lines));
28 30 }
29 31
30 32 default void assertExitCode(Integer code) throws IOException {
31 33 if (code != 0)
32 34 throw new IOException(String.format("The process is terminated with code %d", code));
33 35 }
34 36
35 37 CompletableFuture<Integer> exec() throws IOException;
36 38 }
@@ -1,49 +1,52
1 1 package org.implab.gradle.common.exec;
2 2
3 3 import java.io.IOException;
4 4 import java.lang.ProcessBuilder.Redirect;
5 5 import java.util.ArrayList;
6 6 import java.util.concurrent.CompletableFuture;
7 7
8 import org.eclipse.jdt.annotation.NonNullByDefault;
9
10 @NonNullByDefault
8 11 class SystemExecBuilder extends AbstractExecBuilder<CommandSpec> {
9 12 SystemExecBuilder(CommandSpec command) {
10 13 super(command);
11 14 }
12 15
13 16 @Override
14 17 protected CompletableFuture<Integer> startInternal(
15 18 CommandSpec command,
16 19 EnvironmentSpec environment,
17 20 PipeSpec redirect) throws IOException {
18 21
19 22 var builder = new ProcessBuilder(command.commandLine());
20 23
21 24 environment.workingDirectory().ifPresent(builder::directory);
22 25
23 26 // if the env isn't inherited we need to clear it
24 27 if (!environment.inheritEnvironment())
25 28 builder.environment().clear();
26 29
27 30 builder.environment().putAll(environment.environment());
28 31
29 32 var tasks = new ArrayList<CompletableFuture<?>>();
30 33
31 34 if (!redirect.stdout().isPresent())
32 35 builder.redirectOutput(Redirect.DISCARD);
33 36 if (!redirect.stderr().isPresent())
34 37 builder.redirectError(Redirect.DISCARD);
35 38
36 39 // run process
37 40 var proc = builder.start();
38 41
39 42 tasks.add(proc.onExit());
40 43
41 44 redirect.stdin().map(from -> from.redirect(proc.getOutputStream())).ifPresent(tasks::add);
42 45 redirect.stdout().map(to -> to.redirect(proc.getInputStream())).ifPresent(tasks::add);
43 46 redirect.stderr().map(to -> to.redirect(proc.getErrorStream())).ifPresent(tasks::add);
44 47
45 48 return CompletableFuture
46 49 .allOf(tasks.toArray(new CompletableFuture<?>[0]))
47 50 .thenApply(t -> proc.exitValue());
48 51 }
49 52 }
@@ -1,31 +1,31
1 1 package org.implab.gradle.common.tasks;
2 2
3 3 import org.gradle.api.provider.ListProperty;
4 4 import org.gradle.api.provider.Property;
5 5 import org.gradle.api.tasks.Internal;
6 6 import org.implab.gradle.common.dsl.TaskCommandSpecMixin;
7 7 import org.implab.gradle.common.exec.CommandSpec;
8 8 import org.implab.gradle.common.exec.Shell;
9 9 import org.implab.gradle.common.exec.ShellExec;
10 10
11 11 public abstract class ShellExecTask
12 12 extends AbstractShellExecTask
13 13 implements TaskCommandSpecMixin {
14 14
15 15 @Internal
16 16 public abstract Property<Shell> getShell();
17 17
18 18 @Internal
19 19 @Override
20 20 public abstract ListProperty<String> getCommandLine();
21 21
22 22
23 23 @Override
24 24 protected ShellExec execBuilder() {
25 25 return getShell().get()
26 .of(CommandSpec.builder()
26 .create(CommandSpec.builder()
27 27 .commandLine(getCommandLine().get())
28 28 .build());
29 29 }
30 30
31 31 }
@@ -1,72 +1,87
1 1 package org.implab.gradle.common.utils;
2 2
3 import java.text.MessageFormat;
3 4 import java.util.Iterator;
4 5 import java.util.Spliterators;
5 6 import java.util.Optional;
7 import java.util.Set;
8 import java.util.function.Function;
6 9 import java.util.function.Supplier;
10 import java.util.stream.Collectors;
7 11 import java.util.stream.Stream;
8 12 import java.util.stream.StreamSupport;
9 13
10 14 import org.gradle.api.provider.Provider;
11 15
12 16 public final class Values {
13 17
14 18 private Values() {
15 19 }
16 20
17 21 /**
18 22 * Converts the supplied value to a string.
19 23 */
20 24 public static String toString(Object value) {
21 25 if (value == null) {
22 26 return null;
23 27 } else if (value instanceof String string) {
24 28 return string;
25 29 } else if (value instanceof Provider<?> provider) {
26 30 return toString(provider.getOrNull());
27 31 } else if (value instanceof Supplier<?> supplier) {
28 32 return toString(supplier.get());
29 33 } else {
30 34 return value.toString();
31 35 }
32 36 }
33 37
34 38 public static <T> Stream<T> stream(Iterator<T> remaining) {
35 39 return StreamSupport.stream(
36 40 Spliterators.spliteratorUnknownSize(remaining, 0),
37 41 false);
38 42 }
39 43
40 44 public static <T> Optional<T> take(Iterator<T> iterator) {
41 45 return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.empty();
42 46 }
43 47
44 48 public static <T> Iterable<T> iterable(T[] values) {
45 49 return () -> new ArrayIterator<>(values);
46 50 }
47 51
48 52 public static <T> Optional<T> optional(Provider<T> provider) {
49 53 return provider.isPresent() ? Optional.of(provider.get()) : Optional.empty();
50 54 }
51 55
56 public static <T> T required(Provider<T> provider, String providerName) {
57 if (!provider.isPresent())
58 throw new IllegalStateException(
59 MessageFormat.format("The value for the '{0}' provider must be specified", providerName));
60 return provider.get();
61 }
62
63 public static <U,V> Set<V> mapSet(Set<U> values, Function<U,V> mapper) {
64 return values.stream().map(mapper).collect(Collectors.toUnmodifiableSet());
65 }
66
52 67 private static class ArrayIterator<T> implements Iterator<T> {
53 68 private final T[] data;
54 69
55 70 private int pos = 0;
56 71
57 72 ArrayIterator(T[] data) {
58 73 this.data = data;
59 74 }
60 75
61 76 @Override
62 77 public boolean hasNext() {
63 78 return pos < data.length;
64 79 }
65 80
66 81 @Override
67 82 public T next() {
68 83 return data[pos++];
69 84 }
70 85 }
71 86
72 87 }
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now