| @@ -1,34 +1,62 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.util.stream.Stream; |
|
3 | import java.util.stream.Stream; | |
| 4 |
|
4 | |||
| 5 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 6 | import org.implab.gradle.common.utils.Values; |
|
6 | import org.implab.gradle.common.utils.Values; | |
| 7 |
|
7 | |||
|
|
8 | /** Command builder interface, used to specify the executable and parameters */ | |||
| 8 | @NonNullByDefault |
|
9 | @NonNullByDefault | |
| 9 | public interface CommandBuilder { |
|
10 | public interface CommandBuilder { | |
| 10 |
|
11 | |||
|
|
12 | /** Sets the executable, the parameters are left intact. */ | |||
| 11 | CommandBuilder executable(String executable); |
|
13 | CommandBuilder executable(String executable); | |
| 12 |
|
14 | |||
|
|
15 | /** Sets the specified executable and parameters, old executable and parameters | |||
|
|
16 | * are discarded. | |||
|
|
17 | */ | |||
| 13 | default CommandBuilder commandLine(String executable, String... args) { |
|
18 | default CommandBuilder commandLine(String executable, String... args) { | |
| 14 | return executable(executable) |
|
19 | return executable(executable) | |
| 15 | .arguments(args); |
|
20 | .arguments(args); | |
| 16 | } |
|
21 | } | |
| 17 |
|
22 | |||
| 18 | default CommandBuilder arg(String arg0, String... args) { |
|
23 | /** Sets the specified executable and parameters, old executable and parameters | |
|
|
24 | * are discarded. | |||
|
|
25 | * | |||
|
|
26 | * @param command The command line. Must contain at least one element (executable). | |||
|
|
27 | * | |||
|
|
28 | */ | |||
|
|
29 | default CommandBuilder commandLine(Iterable<? extends String> command) { | |||
|
|
30 | var iterator = command.iterator(); | |||
|
|
31 | ||||
|
|
32 | // set executable | |||
|
|
33 | executable(Values.take(iterator).orElseThrow()); | |||
|
|
34 | // cleat arguments | |||
|
|
35 | arguments(); | |||
|
|
36 | // set new arguments | |||
|
|
37 | while (iterator.hasNext()) | |||
|
|
38 | addArguments(iterator.next()); | |||
|
|
39 | ||||
|
|
40 | return this; | |||
|
|
41 | } | |||
|
|
42 | ||||
|
|
43 | /** Adds the specified arguments to this builder */ | |||
|
|
44 | default CommandBuilder addArguments(String arg0, String... args) { | |||
| 19 | return arguments(() -> Stream |
|
45 | return arguments(() -> Stream | |
| 20 | .concat(Stream.of(arg0), Stream.of(args)) |
|
46 | .concat(Stream.of(arg0), Stream.of(args)) | |
| 21 | .iterator()); |
|
47 | .iterator()); | |
| 22 | } |
|
48 | } | |
| 23 |
|
49 | |||
|
|
50 | /** Replaces arguments in the builder with the specified one. */ | |||
| 24 | default CommandBuilder arguments(String... args) { |
|
51 | default CommandBuilder arguments(String... args) { | |
| 25 | return arguments(Values.iterable(args)); |
|
52 | return arguments(Values.iterable(args)); | |
| 26 | } |
|
53 | } | |
| 27 |
|
54 | |||
|
|
55 | /** Replaces arguments in the builder with the specified one. */ | |||
| 28 | CommandBuilder arguments(Iterable<String> args); |
|
56 | CommandBuilder arguments(Iterable<String> args); | |
| 29 |
|
57 | |||
| 30 | default CommandBuilder from(CommandSpec commandSpec) { |
|
58 | default CommandBuilder from(CommandSpec commandSpec) { | |
| 31 | return executable(commandSpec.executable()) |
|
59 | return executable(commandSpec.executable()) | |
| 32 | .arguments(commandSpec.arguments()); |
|
60 | .arguments(commandSpec.arguments()); | |
| 33 | } |
|
61 | } | |
| 34 | } |
|
62 | } | |
| @@ -1,208 +1,199 | |||||
| 1 | package org.implab.gradle.common.exec; |
|
1 | package org.implab.gradle.common.exec; | |
| 2 |
|
2 | |||
| 3 | import java.util.ArrayList; |
|
3 | import java.util.ArrayList; | |
| 4 | import java.util.HashMap; |
|
4 | import java.util.HashMap; | |
| 5 | import java.util.List; |
|
5 | import java.util.List; | |
| 6 | import java.util.Map; |
|
6 | import java.util.Map; | |
| 7 | import java.util.Optional; |
|
7 | import java.util.Optional; | |
| 8 | import java.util.concurrent.CompletableFuture; |
|
8 | import java.util.concurrent.CompletableFuture; | |
| 9 |
|
9 | |||
| 10 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
10 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 11 | import org.eclipse.jdt.annotation.Nullable; |
|
11 | import org.eclipse.jdt.annotation.Nullable; | |
| 12 |
|
12 | |||
| 13 | import java.io.File; |
|
13 | import java.io.File; | |
| 14 | import java.io.IOException; |
|
14 | import java.io.IOException; | |
| 15 |
|
15 | |||
| 16 | import static java.util.Objects.requireNonNull; |
|
16 | import static java.util.Objects.requireNonNull; | |
| 17 |
|
17 | |||
| 18 | /** Command line builder */ |
|
18 | /** Command line builder */ | |
| 19 | @NonNullByDefault |
|
19 | @NonNullByDefault | |
| 20 | public abstract class ExecBuilder implements |
|
20 | public abstract class ExecBuilder implements | |
| 21 | CommandBuilder, |
|
21 | CommandBuilder, | |
| 22 | PipeBuilder, |
|
22 | PipeBuilder, | |
| 23 | EnvironmentBuilder, |
|
23 | EnvironmentBuilder, | |
| 24 | Runnable |
|
24 | Runnable | |
| 25 | { |
|
25 | { | |
| 26 | private String executable; |
|
26 | private String executable; | |
| 27 |
|
27 | |||
| 28 | private final List<String> arguments = new ArrayList<>(); |
|
28 | private final List<String> arguments = new ArrayList<>(); | |
| 29 |
|
29 | |||
| 30 | private final Map<String, String> environment = new HashMap<>(); |
|
30 | private final Map<String, String> environment = new HashMap<>(); | |
| 31 |
|
31 | |||
| 32 | private @Nullable File directory; |
|
32 | private @Nullable File directory; | |
| 33 |
|
33 | |||
| 34 | private RedirectFrom inputRedirect; |
|
34 | private RedirectFrom inputRedirect; | |
| 35 |
|
35 | |||
| 36 | private RedirectTo outputRedirect; |
|
36 | private RedirectTo outputRedirect; | |
| 37 |
|
37 | |||
| 38 | private RedirectTo errorRedirect; |
|
38 | private RedirectTo errorRedirect; | |
| 39 |
|
39 | |||
| 40 | @Override |
|
40 | @Override | |
| 41 | public ExecBuilder executable(String executable) { |
|
41 | public ExecBuilder executable(String executable) { | |
| 42 | requireNonNull(executable, "cmd can't be null"); |
|
42 | requireNonNull(executable, "cmd can't be null"); | |
| 43 | this.executable = executable; |
|
43 | this.executable = executable; | |
| 44 | return this; |
|
44 | return this; | |
| 45 | } |
|
45 | } | |
| 46 |
|
46 | |||
| 47 | @Override |
|
47 | @Override | |
| 48 | public ExecBuilder arguments(Iterable<String> args) { |
|
48 | public ExecBuilder arguments(Iterable<String> args) { | |
| 49 | requireNonNull(args, "Args must not be null"); |
|
49 | requireNonNull(args, "Args must not be null"); | |
| 50 | arguments.clear(); |
|
50 | arguments.clear(); | |
| 51 | for(var arg: args) |
|
51 | for(var arg: args) | |
| 52 | arguments.add(arg); |
|
52 | arguments.add(arg); | |
| 53 | return this; |
|
53 | return this; | |
| 54 | } |
|
54 | } | |
| 55 |
|
55 | |||
| 56 | public ExecBuilder commandLine(Iterable<? extends String> cmd) { |
|
|||
| 57 | requireNonNull(cmd, "cmd can't be null"); |
|
|||
| 58 |
|
||||
| 59 | this.arguments.clear(); |
|
|||
| 60 | for (var arg : cmd) |
|
|||
| 61 | this.arguments.add(arg); |
|
|||
| 62 | return this; |
|
|||
| 63 | } |
|
|||
| 64 |
|
||||
| 65 | @Override |
|
56 | @Override | |
| 66 | public ExecBuilder arg(String arg0, String... args) { |
|
57 | public ExecBuilder addArguments(String arg0, String... args) { | |
| 67 | requireNonNull(arg0, "arg0 parameter can't be null"); |
|
58 | requireNonNull(arg0, "arg0 parameter can't be null"); | |
| 68 | arguments.add(arg0); |
|
59 | arguments.add(arg0); | |
| 69 |
|
60 | |||
| 70 | for (var arg : args) |
|
61 | for (var arg : args) | |
| 71 | arguments.add(arg); |
|
62 | arguments.add(arg); | |
| 72 |
|
63 | |||
| 73 | return this; |
|
64 | return this; | |
| 74 | } |
|
65 | } | |
| 75 |
|
66 | |||
| 76 | /** Sets the working directory */ |
|
67 | /** Sets the working directory */ | |
| 77 | @Override |
|
68 | @Override | |
| 78 | public ExecBuilder workingDirectory(File directory) { |
|
69 | public ExecBuilder workingDirectory(File directory) { | |
| 79 | requireNonNull(directory, "directory parameter can't be null"); |
|
70 | requireNonNull(directory, "directory parameter can't be null"); | |
| 80 |
|
71 | |||
| 81 | this.directory = directory; |
|
72 | this.directory = directory; | |
| 82 | return this; |
|
73 | return this; | |
| 83 | } |
|
74 | } | |
| 84 |
|
75 | |||
| 85 | @Override |
|
76 | @Override | |
| 86 | public ExecBuilder workingDirectory(Optional<? extends File> directory) { |
|
77 | public ExecBuilder workingDirectory(Optional<? extends File> directory) { | |
| 87 | requireNonNull(directory, "directory parameter can't be null"); |
|
78 | requireNonNull(directory, "directory parameter can't be null"); | |
| 88 |
|
79 | |||
| 89 | this.directory = directory.orElse(null); |
|
80 | this.directory = directory.orElse(null); | |
| 90 | return this; |
|
81 | return this; | |
| 91 | } |
|
82 | } | |
| 92 |
|
83 | |||
| 93 | /** |
|
84 | /** | |
| 94 | * Sets the environment value. The value cannot be null. |
|
85 | * Sets the environment value. The value cannot be null. | |
| 95 | * |
|
86 | * | |
| 96 | * @param envVar The name of the environment variable |
|
87 | * @param envVar The name of the environment variable | |
| 97 | * @param value The value to set, |
|
88 | * @param value The value to set, | |
| 98 | */ |
|
89 | */ | |
| 99 | @Override |
|
90 | @Override | |
| 100 | public ExecBuilder setVariable(String envVar, String value) { |
|
91 | public ExecBuilder setVariable(String envVar, String value) { | |
| 101 | requireNonNull(value, "Value can't be null"); |
|
92 | requireNonNull(value, "Value can't be null"); | |
| 102 | requireNonNull(envVar, "envVar parameter can't be null"); |
|
93 | requireNonNull(envVar, "envVar parameter can't be null"); | |
| 103 |
|
94 | |||
| 104 | environment.put(envVar, value); |
|
95 | environment.put(envVar, value); | |
| 105 | return this; |
|
96 | return this; | |
| 106 | } |
|
97 | } | |
| 107 |
|
98 | |||
| 108 | @Override |
|
99 | @Override | |
| 109 | public ExecBuilder environment(Map<String,? extends String> env) { |
|
100 | public ExecBuilder environment(Map<String,? extends String> env) { | |
| 110 | requireNonNull(env, "env parameter can't be null"); |
|
101 | requireNonNull(env, "env parameter can't be null"); | |
| 111 |
|
102 | |||
| 112 | environment.clear(); |
|
103 | environment.clear(); | |
| 113 | environment.putAll(env); |
|
104 | environment.putAll(env); | |
| 114 | return this; |
|
105 | return this; | |
| 115 | } |
|
106 | } | |
| 116 |
|
107 | |||
| 117 | /** |
|
108 | /** | |
| 118 | * Sets redirection for the stdin, {@link RedirectFrom} will be applied |
|
109 | * Sets redirection for the stdin, {@link RedirectFrom} will be applied | |
| 119 | * every time the process is started. |
|
110 | * every time the process is started. | |
| 120 | * |
|
111 | * | |
| 121 | * <p> |
|
112 | * <p> | |
| 122 | * If redirection |
|
113 | * If redirection | |
| 123 | */ |
|
114 | */ | |
| 124 | @Override |
|
115 | @Override | |
| 125 | public ExecBuilder stdin(RedirectFrom from) { |
|
116 | public ExecBuilder stdin(RedirectFrom from) { | |
| 126 | requireNonNull(from, "from parameter can't be null"); |
|
117 | requireNonNull(from, "from parameter can't be null"); | |
| 127 |
|
118 | |||
| 128 | inputRedirect = from; |
|
119 | inputRedirect = from; | |
| 129 | return this; |
|
120 | return this; | |
| 130 | } |
|
121 | } | |
| 131 |
|
122 | |||
| 132 | @Override |
|
123 | @Override | |
| 133 | public PipeBuilder stdin(Optional<? extends RedirectFrom> from) { |
|
124 | public PipeBuilder stdin(Optional<? extends RedirectFrom> from) { | |
| 134 | requireNonNull(from, "from parameter can't be null"); |
|
125 | requireNonNull(from, "from parameter can't be null"); | |
| 135 | inputRedirect = from.orElse(null); |
|
126 | inputRedirect = from.orElse(null); | |
| 136 | return this; |
|
127 | return this; | |
| 137 | } |
|
128 | } | |
| 138 |
|
129 | |||
| 139 | /** |
|
130 | /** | |
| 140 | * Sets redirection for the stdout, {@link RedirectTo} will be applied |
|
131 | * Sets redirection for the stdout, {@link RedirectTo} will be applied | |
| 141 | * every time the process is started. |
|
132 | * every time the process is started. | |
| 142 | */ |
|
133 | */ | |
| 143 | @Override |
|
134 | @Override | |
| 144 | public ExecBuilder stdout(RedirectTo out) { |
|
135 | public ExecBuilder stdout(RedirectTo out) { | |
| 145 | requireNonNull(out, "out parameter can't be null"); |
|
136 | requireNonNull(out, "out parameter can't be null"); | |
| 146 | outputRedirect = out; |
|
137 | outputRedirect = out; | |
| 147 | return this; |
|
138 | return this; | |
| 148 | } |
|
139 | } | |
| 149 |
|
140 | |||
| 150 | @Override |
|
141 | @Override | |
| 151 | public PipeBuilder stdout(Optional<? extends RedirectTo> to) { |
|
142 | public PipeBuilder stdout(Optional<? extends RedirectTo> to) { | |
| 152 | requireNonNull(to, "from parameter can't be null"); |
|
143 | requireNonNull(to, "from parameter can't be null"); | |
| 153 | outputRedirect = to.orElse(null); |
|
144 | outputRedirect = to.orElse(null); | |
| 154 | return this; |
|
145 | return this; | |
| 155 | } |
|
146 | } | |
| 156 |
|
147 | |||
| 157 | /** |
|
148 | /** | |
| 158 | * Sets redirection for the stderr, {@link RedirectTo} will be applied |
|
149 | * Sets redirection for the stderr, {@link RedirectTo} will be applied | |
| 159 | * every time the process is started. |
|
150 | * every time the process is started. | |
| 160 | */ |
|
151 | */ | |
| 161 | @Override |
|
152 | @Override | |
| 162 | public ExecBuilder stderr(RedirectTo err) { |
|
153 | public ExecBuilder stderr(RedirectTo err) { | |
| 163 | requireNonNull(err, "err parameter can't be null"); |
|
154 | requireNonNull(err, "err parameter can't be null"); | |
| 164 | errorRedirect = err; |
|
155 | errorRedirect = err; | |
| 165 | return this; |
|
156 | return this; | |
| 166 | } |
|
157 | } | |
| 167 |
|
158 | |||
| 168 | @Override |
|
159 | @Override | |
| 169 | public PipeBuilder stderr(Optional<? extends RedirectTo> to) { |
|
160 | public PipeBuilder stderr(Optional<? extends RedirectTo> to) { | |
| 170 | requireNonNull(to, "from parameter can't be null"); |
|
161 | requireNonNull(to, "from parameter can't be null"); | |
| 171 | errorRedirect = to.orElse(null); |
|
162 | errorRedirect = to.orElse(null); | |
| 172 | return this; |
|
163 | return this; | |
| 173 | } |
|
164 | } | |
| 174 |
|
165 | |||
| 175 | /** Implement this function to */ |
|
166 | /** Implement this function to */ | |
| 176 | protected abstract CompletableFuture<Integer> startInternal( |
|
167 | protected abstract CompletableFuture<Integer> startInternal( | |
| 177 | Map<String, String> environment, |
|
168 | Map<String, String> environment, | |
| 178 | Optional<File> directory, |
|
169 | Optional<File> directory, | |
| 179 | List<String> commandLine, |
|
170 | List<String> commandLine, | |
| 180 | Optional<RedirectFrom> stdinRedirect, |
|
171 | Optional<RedirectFrom> stdinRedirect, | |
| 181 | Optional<RedirectTo> stdoutRedirect, |
|
172 | Optional<RedirectTo> stdoutRedirect, | |
| 182 | Optional<RedirectTo> stderrRedirect) throws IOException; |
|
173 | Optional<RedirectTo> stderrRedirect) throws IOException; | |
| 183 |
|
174 | |||
| 184 | /** |
|
175 | /** | |
| 185 | * Creates and starts new process and returns {@link CompletableFuture}. The |
|
176 | * Creates and starts new process and returns {@link CompletableFuture}. The | |
| 186 | * process may be a remote process, or a shell process or etc. |
|
177 | * process may be a remote process, or a shell process or etc. | |
| 187 | * |
|
178 | * | |
| 188 | * @return |
|
179 | * @return | |
| 189 | * @throws IOException |
|
180 | * @throws IOException | |
| 190 | */ |
|
181 | */ | |
| 191 | public CompletableFuture<Integer> run() throws IOException { |
|
182 | public CompletableFuture<Integer> run() throws IOException { | |
| 192 | if (executable == null || executable.isEmpty()) |
|
183 | if (executable == null || executable.isEmpty()) | |
| 193 | throw new IllegalStateException("The executable isn't set"); |
|
184 | throw new IllegalStateException("The executable isn't set"); | |
| 194 |
|
185 | |||
| 195 | var commandLine = new ArrayList<String>(); |
|
186 | var commandLine = new ArrayList<String>(); | |
| 196 | commandLine.add(executable); |
|
187 | commandLine.add(executable); | |
| 197 | commandLine.addAll(arguments); |
|
188 | commandLine.addAll(arguments); | |
| 198 |
|
189 | |||
| 199 | return startInternal( |
|
190 | return startInternal( | |
| 200 | environment, |
|
191 | environment, | |
| 201 | Optional.ofNullable(directory), |
|
192 | Optional.ofNullable(directory), | |
| 202 | commandLine, |
|
193 | commandLine, | |
| 203 | Optional.ofNullable(inputRedirect), |
|
194 | Optional.ofNullable(inputRedirect), | |
| 204 | Optional.ofNullable(outputRedirect), |
|
195 | Optional.ofNullable(outputRedirect), | |
| 205 | Optional.ofNullable(errorRedirect)); |
|
196 | Optional.ofNullable(errorRedirect)); | |
| 206 | } |
|
197 | } | |
| 207 |
|
198 | |||
| 208 | } |
|
199 | } | |
| @@ -1,73 +1,88 | |||||
| 1 | package org.implab.gradle.common.utils; |
|
1 | package org.implab.gradle.common.utils; | |
| 2 |
|
2 | |||
| 3 | import java.util.Iterator; |
|
3 | import java.util.Iterator; | |
| 4 | import java.util.Map; |
|
4 | import java.util.Map; | |
|
|
5 | import java.util.Spliterator; | |||
|
|
6 | import java.util.Spliterators; | |||
| 5 | import java.util.Map.Entry; |
|
7 | import java.util.Map.Entry; | |
|
|
8 | import java.util.Optional; | |||
| 6 | import java.util.function.Function; |
|
9 | import java.util.function.Function; | |
| 7 | import java.util.function.Supplier; |
|
10 | import java.util.function.Supplier; | |
| 8 | import java.util.stream.Collectors; |
|
11 | import java.util.stream.Collectors; | |
|
|
12 | import java.util.stream.Stream; | |||
|
|
13 | import java.util.stream.StreamSupport; | |||
| 9 |
|
14 | |||
| 10 | import org.gradle.api.provider.Provider; |
|
15 | import org.gradle.api.provider.Provider; | |
| 11 |
|
16 | |||
| 12 | public final class Values { |
|
17 | public final class Values { | |
| 13 |
|
18 | |||
| 14 | private Values() { |
|
19 | private Values() { | |
| 15 | } |
|
20 | } | |
| 16 |
|
21 | |||
| 17 | /** |
|
22 | /** | |
| 18 | * Converts values in the specified map |
|
23 | * Converts values in the specified map | |
| 19 | * @param <K> |
|
24 | * @param <K> | |
| 20 | * @param <V> |
|
25 | * @param <V> | |
| 21 | * @param <U> |
|
26 | * @param <U> | |
| 22 | * @param map |
|
27 | * @param map | |
| 23 | * @param mapper |
|
28 | * @param mapper | |
| 24 | * @return |
|
29 | * @return | |
| 25 | */ |
|
30 | */ | |
| 26 | public static <K, V, U> Map<K, U> mapValues(Map<K, V> map, Function<V, U> mapper) { |
|
31 | public static <K, V, U> Map<K, U> mapValues(Map<K, V> map, Function<V, U> mapper) { | |
| 27 | Function<Entry<K, V>, V> getter = Entry::getValue; |
|
32 | Function<Entry<K, V>, V> getter = Entry::getValue; | |
| 28 |
|
33 | |||
| 29 | return map.entrySet().stream() |
|
34 | return map.entrySet().stream() | |
| 30 | .collect(Collectors.toMap(Entry::getKey, getter.andThen(mapper))); |
|
35 | .collect(Collectors.toMap(Entry::getKey, getter.andThen(mapper))); | |
| 31 | } |
|
36 | } | |
| 32 |
|
37 | |||
| 33 | /** Converts the supplied value to a string. |
|
38 | /** Converts the supplied value to a string. | |
| 34 | */ |
|
39 | */ | |
| 35 | public static String toString(Object value) { |
|
40 | public static String toString(Object value) { | |
| 36 | if (value == null) { |
|
41 | if (value == null) { | |
| 37 | return null; |
|
42 | return null; | |
| 38 | } else if (value instanceof String string) { |
|
43 | } else if (value instanceof String string) { | |
| 39 | return string; |
|
44 | return string; | |
| 40 | } else if (value instanceof Provider<?> provider) { |
|
45 | } else if (value instanceof Provider<?> provider) { | |
| 41 | return toString(provider.getOrNull()); |
|
46 | return toString(provider.getOrNull()); | |
| 42 | } else if (value instanceof Supplier<?> supplier) { |
|
47 | } else if (value instanceof Supplier<?> supplier) { | |
| 43 | return toString(supplier.get()); |
|
48 | return toString(supplier.get()); | |
| 44 | } else { |
|
49 | } else { | |
| 45 | return value.toString(); |
|
50 | return value.toString(); | |
| 46 | } |
|
51 | } | |
| 47 | } |
|
52 | } | |
| 48 |
|
53 | |||
|
|
54 | public static <T> Stream<T> stream(Iterator<T> remaining) { | |||
|
|
55 | var spliterator = Spliterators.spliteratorUnknownSize(remaining, 0); | |||
|
|
56 | ||||
|
|
57 | return StreamSupport.stream(spliterator, false); | |||
|
|
58 | } | |||
|
|
59 | ||||
|
|
60 | public static <T> Optional<T> take(Iterator<T> iterator) { | |||
|
|
61 | return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.empty(); | |||
|
|
62 | } | |||
|
|
63 | ||||
| 49 | public static <T> Iterable<T> iterable(T[] values) { |
|
64 | public static <T> Iterable<T> iterable(T[] values) { | |
| 50 | return () -> new ArrayIterator<>(values); |
|
65 | return () -> new ArrayIterator<>(values); | |
| 51 | } |
|
66 | } | |
| 52 |
|
67 | |||
| 53 | private static class ArrayIterator<T> implements Iterator<T> { |
|
68 | private static class ArrayIterator<T> implements Iterator<T> { | |
| 54 | private final T[] data; |
|
69 | private final T[] data; | |
| 55 |
|
70 | |||
| 56 | private int pos = 0; |
|
71 | private int pos = 0; | |
| 57 |
|
72 | |||
| 58 | ArrayIterator(T[] data) { |
|
73 | ArrayIterator(T[] data) { | |
| 59 | this.data = data; |
|
74 | this.data = data; | |
| 60 | } |
|
75 | } | |
| 61 |
|
76 | |||
| 62 | @Override |
|
77 | @Override | |
| 63 | public boolean hasNext() { |
|
78 | public boolean hasNext() { | |
| 64 | return pos < data.length; |
|
79 | return pos < data.length; | |
| 65 | } |
|
80 | } | |
| 66 |
|
81 | |||
| 67 | @Override |
|
82 | @Override | |
| 68 | public T next() { |
|
83 | public T next() { | |
| 69 | return data[pos++]; |
|
84 | return data[pos++]; | |
| 70 | } |
|
85 | } | |
| 71 | } |
|
86 | } | |
| 72 |
|
87 | |||
| 73 | } |
|
88 | } | |
General Comments 0
You need to be logged in to leave comments.
Login now
