• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jreleaser / jreleaser / #524

01 Aug 2025 02:16PM UTC coverage: 48.454% (-0.9%) from 49.315%
#524

push

github

aalmiray
fix(hooks): Inherit matrix and environment from named groups

Relates to #1947

56 of 125 new or added lines in 5 files covered. (44.8%)

450 existing lines in 37 files now uncovered.

25680 of 52999 relevant lines covered (48.45%)

0.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.31
/core/jreleaser-engine/src/main/java/org/jreleaser/engine/hooks/HookExecutor.java
1
/*
2
 * SPDX-License-Identifier: Apache-2.0
3
 *
4
 * Copyright 2020-2025 The JReleaser authors.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     https://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
package org.jreleaser.engine.hooks;
19

20
import org.jreleaser.bundle.RB;
21
import org.jreleaser.model.JReleaserException;
22
import org.jreleaser.model.api.hooks.ExecutionEvent;
23
import org.jreleaser.model.internal.JReleaserContext;
24
import org.jreleaser.model.internal.common.Matrix;
25
import org.jreleaser.model.internal.hooks.CommandHook;
26
import org.jreleaser.model.internal.hooks.CommandHookProvider;
27
import org.jreleaser.model.internal.hooks.CommandHooks;
28
import org.jreleaser.model.internal.hooks.Hook;
29
import org.jreleaser.model.internal.hooks.Hooks;
30
import org.jreleaser.model.internal.hooks.NamedCommandHooks;
31
import org.jreleaser.model.internal.hooks.NamedScriptHooks;
32
import org.jreleaser.model.internal.hooks.ScriptHook;
33
import org.jreleaser.model.internal.hooks.ScriptHookProvider;
34
import org.jreleaser.model.internal.hooks.ScriptHooks;
35
import org.jreleaser.mustache.TemplateContext;
36
import org.jreleaser.sdk.command.Command;
37
import org.jreleaser.sdk.command.CommandException;
38
import org.jreleaser.sdk.command.CommandExecutor;
39
import org.jreleaser.util.PlatformUtils;
40

41
import java.io.IOException;
42
import java.nio.file.Files;
43
import java.nio.file.Path;
44
import java.util.ArrayList;
45
import java.util.Collection;
46
import java.util.LinkedHashMap;
47
import java.util.List;
48
import java.util.Locale;
49
import java.util.Map;
50
import java.util.StringTokenizer;
51

52
import static java.lang.System.lineSeparator;
53
import static java.nio.charset.StandardCharsets.UTF_8;
54
import static java.nio.file.StandardOpenOption.WRITE;
55
import static org.jreleaser.model.Constants.KEY_PLATFORM;
56
import static org.jreleaser.mustache.Templates.resolveTemplate;
57
import static org.jreleaser.util.StringUtils.isFalse;
58
import static org.jreleaser.util.StringUtils.isNotBlank;
59

60
/**
61
 * @author Andres Almiray
62
 * @since 1.2.0
63
 */
64
public final class HookExecutor {
65
    private static final String JRELEASER_OUTPUT = "JRELEASER_OUTPUT:";
66
    private final JReleaserContext context;
67

68
    public HookExecutor(JReleaserContext context) {
1✔
69
        this.context = context;
1✔
70
    }
1✔
71

72
    public void execute(String step, Runnable runnable) {
73
        executeHooks(ExecutionEvent.before(step));
1✔
74

75
        try {
76
            runnable.run();
1✔
UNCOV
77
        } catch (RuntimeException e) {
×
UNCOV
78
            executeHooks(ExecutionEvent.failure(step, e));
×
UNCOV
79
            throw e;
×
80
        }
1✔
81
        executeHooks(ExecutionEvent.success(step));
1✔
82
    }
1✔
83

84
    public void executeHooks(ExecutionEvent event) {
85
        Hooks hooks = context.getModel().getHooks();
1✔
86
        if (!hooks.isEnabled() || evaluateCondition(hooks.getCondition())) {
1✔
87
            return;
1✔
88
        }
89

90
        Map<String, String> rootEnv = resolveEnvironment(hooks.getEnvironment());
1✔
91
        executeScriptHooks(event, rootEnv);
1✔
92
        executeCommandHooks(event, rootEnv);
1✔
93
    }
1✔
94

95
    private boolean evaluateCondition(String condition) {
96
        return isNotBlank(condition) && isFalse(context.eval(condition));
1✔
97
    }
98

99
    private Map<String, String> resolveEnvironment(Map<String, String> src) {
100
        return resolveEnvironment(src, null);
1✔
101
    }
102

103
    private Map<String, String> resolveEnvironment(Map<String, String> src, TemplateContext additionalContext) {
104
        Map<String, String> env = new LinkedHashMap<>();
1✔
105
        TemplateContext props = context.props().setAll(additionalContext);
1✔
106
        src.forEach((k, v) -> {
1✔
107
            String value = resolveTemplate(v, props);
1✔
108
            if (isNotBlank(value)) env.put(k, value);
1✔
109
        });
1✔
110
        return env;
1✔
111
    }
112

113
    private Map<String, String> mergeEnvironment(Map<String, String> src, Map<String, String>... others) {
114
        Map<String, String> tmp = new LinkedHashMap<>(src);
1✔
115
        if (null != others && others.length > 0) {
1✔
116
            for (Map<String, String> env : others) {
1✔
117
                tmp.putAll(env);
1✔
118
            }
119
        }
120
        return tmp;
1✔
121
    }
122

123
    private void executeScriptHooks(ExecutionEvent event, Map<String, String> rootEnv) {
124
        ScriptHooks scriptHooks = context.getModel().getHooks().getScript();
1✔
125
        if (!scriptHooks.isEnabled() || evaluateCondition(scriptHooks.getCondition())) {
1✔
126
            return;
×
127
        }
128

129
        final List<ScriptHook> hooks = collectScriptHooks(event, scriptHooks);
1✔
130
        if (!hooks.isEmpty()) {
1✔
131
            context.getLogger().info(RB.$("hooks.script.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
1✔
132
        }
133

134
        context.getLogger().setPrefix("hooks");
1✔
135
        context.getLogger().increaseIndent();
1✔
136

137
        try {
138
            for (ScriptHook hook : hooks) {
1✔
139
                String prefix = "hooks";
1✔
140
                if (isNotBlank(hook.getName())) {
1✔
141
                    prefix += "." + hook.getName();
×
142
                }
143
                context.getLogger().replacePrefix(prefix);
1✔
144

145
                if (!hook.getMatrix().isEmpty()) {
1✔
146
                    for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
1✔
147
                        if (matrixRow.containsKey(KEY_PLATFORM)) {
1✔
148
                            String srcPlatform = matrixRow.get(KEY_PLATFORM);
1✔
149
                            if (!context.isPlatformSelected(srcPlatform)) {
1✔
150
                                continue;
×
151
                            }
152
                        }
153

154
                        executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment()), matrixRow);
1✔
155
                    }
1✔
156
                } else {
157
                    executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment()), null);
1✔
158
                }
159
            }
1✔
160
        } finally {
161
            context.getLogger().decreaseIndent();
1✔
162
            context.getLogger().restorePrefix();
1✔
163
        }
164

165
        executeNamedScriptHooks(event, rootEnv, scriptHooks);
1✔
166
    }
1✔
167

168
    private void executeNamedScriptHooks(ExecutionEvent event, Map<String, String> rootEnv, ScriptHooks scriptHooks) {
169
        if (!scriptHooks.isEnabled() || evaluateCondition(scriptHooks.getCondition())) {
1✔
NEW
170
            return;
×
171
        }
172

173
        for (NamedScriptHooks group : scriptHooks.getGroups().values()) {
1✔
NEW
174
            if (!group.isEnabled() || evaluateCondition(group.getCondition())) {
×
NEW
175
                continue;
×
176
            }
177

NEW
178
            final List<ScriptHook> hooks = collectScriptHooks(event, group);
×
NEW
179
            if (!hooks.isEmpty()) {
×
NEW
180
                context.getLogger().info(RB.$("hooks.script.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
×
181
            }
182

NEW
183
            context.getLogger().setPrefix("hooks");
×
NEW
184
            context.getLogger().increaseIndent();
×
185

186
            try {
NEW
187
                for (ScriptHook hook : hooks) {
×
NEW
188
                    String prefix = "hooks";
×
NEW
189
                    if (isNotBlank(hook.getName())) {
×
NEW
190
                        prefix += "." + hook.getName();
×
191
                    }
NEW
192
                    context.getLogger().replacePrefix(prefix);
×
193

NEW
194
                    if (!hook.getMatrix().isEmpty()) {
×
NEW
195
                        for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
NEW
196
                            if (matrixRow.containsKey(KEY_PLATFORM)) {
×
NEW
197
                                String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
NEW
198
                                if (!context.isPlatformSelected(srcPlatform)) {
×
NEW
199
                                    continue;
×
200
                                }
201
                            }
202

NEW
203
                            executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment(), group.getEnvironment()), matrixRow);
×
NEW
204
                        }
×
205
                    } else {
NEW
206
                        executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment(), group.getEnvironment()), null);
×
207
                    }
NEW
208
                }
×
209
            } finally {
NEW
210
                context.getLogger().decreaseIndent();
×
NEW
211
                context.getLogger().restorePrefix();
×
212
            }
NEW
213
        }
×
214
    }
1✔
215

216
    private List<ScriptHook> collectScriptHooks(ExecutionEvent event, ScriptHookProvider scriptHookProvider) {
217
        final List<ScriptHook> hooks = new ArrayList<>();
1✔
218

219
        switch (event.getType()) {
1✔
220
            case BEFORE:
221
                hooks.addAll((Collection<ScriptHook>) filter(scriptHookProvider.getBefore(), event));
1✔
222
                break;
1✔
223
            case SUCCESS:
224
                hooks.addAll((Collection<ScriptHook>) filter(scriptHookProvider.getSuccess(), event));
1✔
225
                break;
1✔
226
            case FAILURE:
227
                hooks.addAll((Collection<ScriptHook>) filter(scriptHookProvider.getFailure(), event));
1✔
228
                break;
229
        }
230
        return hooks;
1✔
231
    }
232

233
    private void executeScriptHook(ExecutionEvent event, ScriptHook hook, Map<String, String> env, Map<String, String> matrixRow) {
234
        TemplateContext additionalContext = Matrix.asTemplateContext(matrixRow);
1✔
235
        Map<String, String> localEnv = new LinkedHashMap<>(env);
1✔
236
        localEnv = resolveEnvironment(localEnv, additionalContext);
1✔
237
        Path scriptFile = null;
1✔
238

239
        try {
240
            scriptFile = createScriptFile(context, hook, additionalContext, event);
1✔
NEW
241
        } catch (IOException e) {
×
NEW
242
            throw new JReleaserException(RB.$("ERROR_script_hook_create_error"), e);
×
243
        }
1✔
244

245
        String resolvedCmd = hook.getShell().expression().replace("{{script}}", scriptFile.toAbsolutePath().toString());
1✔
246
        executeCommandLine(localEnv, additionalContext, hook, resolvedCmd, resolvedCmd, "ERROR_script_hook_unexpected_error");
1✔
247
    }
1✔
248

249
    private Path createScriptFile(JReleaserContext context, ScriptHook hook, TemplateContext additionalContext, ExecutionEvent event) throws IOException {
250
        String scriptContents = hook.getResolvedRun(context, additionalContext, event);
1✔
251
        Path scriptFile = Files.createTempFile("jreleaser", hook.getShell().extension());
1✔
252

253
        if (hook.getShell() == org.jreleaser.model.api.hooks.ScriptHook.Shell.PWSH ||
1✔
254
            hook.getShell() == org.jreleaser.model.api.hooks.ScriptHook.Shell.POWERSHELL) {
1✔
255
            scriptContents = "$ErrorActionPreference = 'stop'" + lineSeparator() + scriptContents;
×
256
            scriptContents += lineSeparator() + "if ((Test-Path -LiteralPath variable:\\LASTEXITCODE)) { exit $LASTEXITCODE }";
×
257
        }
258

259
        Files.write(scriptFile, scriptContents.getBytes(UTF_8), WRITE);
1✔
260
        return scriptFile;
1✔
261
    }
262

263
    private void executeCommandHooks(ExecutionEvent event, Map<String, String> rootEnv) {
264
        CommandHooks commandHooks = context.getModel().getHooks().getCommand();
1✔
265
        if (!commandHooks.isEnabled() || evaluateCondition(commandHooks.getCondition())) {
1✔
266
            return;
×
267
        }
268

269
        final List<CommandHook> hooks = collectCommandHooks(event, commandHooks);
1✔
270
        if (!hooks.isEmpty()) {
1✔
271
            context.getLogger().info(RB.$("hooks.command.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
1✔
272
        }
273

274
        context.getLogger().setPrefix("hooks");
1✔
275
        context.getLogger().increaseIndent();
1✔
276

277
        try {
278
            for (CommandHook hook : hooks) {
1✔
279
                String prefix = "hooks";
1✔
280
                if (isNotBlank(hook.getName())) {
1✔
281
                    prefix += "." + hook.getName();
×
282
                }
283
                context.getLogger().replacePrefix(prefix);
1✔
284

285
                if (!hook.getMatrix().isEmpty()) {
1✔
286
                    for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
287
                        if (matrixRow.containsKey(KEY_PLATFORM)) {
×
288
                            String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
289
                            if (!context.isPlatformSelected(srcPlatform)) {
×
290
                                continue;
×
291
                            }
292
                        }
293

NEW
294
                        executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment()), matrixRow);
×
295
                    }
×
296
                } else {
297
                    executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment()), null);
1✔
298
                }
299
            }
1✔
300
        } finally {
301
            context.getLogger().decreaseIndent();
1✔
302
            context.getLogger().restorePrefix();
1✔
303
        }
304

305
        executeNamedCommandHooks(event, rootEnv, commandHooks);
1✔
306
    }
1✔
307

308
    private void executeNamedCommandHooks(ExecutionEvent event, Map<String, String> rootEnv, CommandHooks commandHooks) {
309
        if (!commandHooks.isEnabled() || evaluateCondition(commandHooks.getCondition())) {
1✔
NEW
310
            return;
×
311
        }
312

313
        for (NamedCommandHooks group : commandHooks.getGroups().values()) {
1✔
NEW
314
            if (!group.isEnabled() || evaluateCondition(group.getCondition())) {
×
NEW
315
                continue;
×
316
            }
317

NEW
318
            final List<CommandHook> hooks = collectCommandHooks(event, group);
×
NEW
319
            if (!hooks.isEmpty()) {
×
NEW
320
                context.getLogger().info(RB.$("hooks.command.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
×
321
            }
322

NEW
323
            context.getLogger().setPrefix("hooks");
×
NEW
324
            context.getLogger().increaseIndent();
×
325

326
            try {
NEW
327
                for (CommandHook hook : hooks) {
×
NEW
328
                    String prefix = "hooks";
×
NEW
329
                    if (isNotBlank(hook.getName())) {
×
NEW
330
                        prefix += "." + hook.getName();
×
331
                    }
NEW
332
                    context.getLogger().replacePrefix(prefix);
×
333

NEW
334
                    if (!hook.getMatrix().isEmpty()) {
×
NEW
335
                        for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
NEW
336
                            if (matrixRow.containsKey(KEY_PLATFORM)) {
×
NEW
337
                                String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
NEW
338
                                if (!context.isPlatformSelected(srcPlatform)) {
×
NEW
339
                                    continue;
×
340
                                }
341
                            }
342

NEW
343
                            executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment(), group.getEnvironment()), matrixRow);
×
NEW
344
                        }
×
345
                    } else {
NEW
346
                        executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment(), group.getEnvironment()), null);
×
347
                    }
NEW
348
                }
×
349
            } finally {
NEW
350
                context.getLogger().decreaseIndent();
×
NEW
351
                context.getLogger().restorePrefix();
×
352
            }
NEW
353
        }
×
354
    }
1✔
355

356
    private List<CommandHook> collectCommandHooks(ExecutionEvent event, CommandHookProvider commandHookProvider) {
357
        final List<CommandHook> hooks = new ArrayList<>();
1✔
358

359
        switch (event.getType()) {
1✔
360
            case BEFORE:
361
                hooks.addAll((Collection<CommandHook>) filter(commandHookProvider.getBefore(), event));
1✔
362
                break;
1✔
363
            case SUCCESS:
364
                hooks.addAll((Collection<CommandHook>) filter(commandHookProvider.getSuccess(), event));
1✔
365
                break;
1✔
366
            case FAILURE:
367
                hooks.addAll((Collection<CommandHook>) filter(commandHookProvider.getFailure(), event));
1✔
368
                break;
369
        }
370
        return hooks;
1✔
371
    }
372

373
    private void executeCommandHook(ExecutionEvent event, CommandHook hook, Map<String, String> env, Map<String, String> matrixRow) {
374
        TemplateContext additionalContext = Matrix.asTemplateContext(matrixRow);
1✔
375
        Map<String, String> localEnv = new LinkedHashMap<>(env);
1✔
376
        localEnv = resolveEnvironment(localEnv, additionalContext);
1✔
377
        String resolvedCmd = hook.getResolvedCmd(context, additionalContext, event);
1✔
378
        executeCommandLine(localEnv, additionalContext, hook, hook.getCmd(), resolvedCmd, "ERROR_command_hook_unexpected_error");
1✔
379
    }
1✔
380

381
    private void executeCommandLine(Map<String, String> localEnv, TemplateContext additionalContext, Hook hook, String cmd, String resolvedCmd, String errorKey) {
382
        List<String> commandLine = null;
1✔
383

384
        Map<String, String> hookEnv = new LinkedHashMap<>(localEnv);
1✔
385
        hookEnv.putAll(hook.getEnvironment());
1✔
386
        hookEnv = resolveEnvironment(hookEnv, additionalContext);
1✔
387

388
        try {
389
            commandLine = parseCommand(resolvedCmd);
1✔
390
        } catch (IllegalStateException e) {
×
391
            throw new JReleaserException(RB.$("ERROR_command_hook_parser_error", cmd), e);
×
392
        }
1✔
393

394
        try {
395
            Command command = new Command(commandLine);
1✔
396
            processOutput(executeCommand(context.getBasedir(), command, hookEnv, hook.isVerbose()));
1✔
397
        } catch (CommandException e) {
1✔
398
            if (!hook.isContinueOnError()) {
1✔
399
                throw new JReleaserException(RB.$(errorKey), e);
1✔
400
            } else {
401
                if (null != e.getCause()) {
×
402
                    context.getLogger().warn(e.getCause().getMessage());
×
403
                } else {
404
                    context.getLogger().warn(e.getMessage());
×
405
                }
406
                context.getLogger().trace(RB.$(errorKey), e);
×
407
            }
408
        }
1✔
409
    }
1✔
410

411
    private void processOutput(Command.Result result) {
412
        if (!result.getOut().contains(JRELEASER_OUTPUT)) return;
1✔
413
        for (String line : result.getOut().split(lineSeparator())) {
×
414
            if (!line.startsWith(JRELEASER_OUTPUT)) continue;
×
415
            line = line.substring(JRELEASER_OUTPUT.length());
×
416
            int p = line.indexOf("=");
×
417
            String key = line.substring(0, p);
×
418
            String value = line.substring(p + 1);
×
419
            context.getModel().getEnvironment().getProperties().put(key, value);
×
420
        }
421
    }
×
422

423
    private Collection<? extends Hook> filter(List<? extends Hook> hooks, ExecutionEvent event) {
424
        List<Hook> tmp = new ArrayList<>();
1✔
425

426
        for (Hook hook : hooks) {
1✔
427
            if (!hook.isEnabled() || evaluateCondition(hook.getCondition())) {
1✔
428
                continue;
×
429
            }
430

431
            if (!hook.getFilter().getResolvedIncludes().isEmpty()) {
1✔
432
                if (hook.getFilter().getResolvedIncludes().contains(event.getName()) && filterByPlatform(hook)) {
1✔
433
                    tmp.add(hook);
1✔
434
                }
435
            } else if (filterByPlatform(hook)) {
1✔
436
                tmp.add(hook);
1✔
437
            }
438

439
            if (hook.getFilter().getResolvedExcludes().contains(event.getName())) {
1✔
440
                tmp.remove(hook);
×
441
            }
442
        }
1✔
443

444
        return tmp;
1✔
445
    }
446

447
    private boolean filterByPlatform(Hook hook) {
448
        if (hook.getPlatforms().isEmpty()) return true;
1✔
449

450
        boolean success = true;
1✔
451
        for (String platform : hook.getPlatforms()) {
1✔
452
            boolean exclude = false;
1✔
453
            if (platform.startsWith("!")) {
1✔
454
                exclude = true;
1✔
455
                platform = platform.substring(1);
1✔
456
            }
457

458
            success &= exclude != PlatformUtils.isCompatible(PlatformUtils.getCurrentFull(), platform);
1✔
459
        }
1✔
460

461
        return success;
1✔
462
    }
463

464
    private Command.Result executeCommand(Path directory, Command command, Map<String, String> env, boolean verbose) throws CommandException {
465
        Command.Result result = new CommandExecutor(context.getLogger(), verbose ? CommandExecutor.Output.VERBOSE : CommandExecutor.Output.DEBUG)
1✔
466
            .environment(env)
1✔
467
            .executeCommand(directory, command);
1✔
468
        if (result.getExitValue() != 0) {
1✔
469
            throw new CommandException(RB.$("ERROR_command_execution_exit_value", result.getExitValue()));
1✔
470
        }
471
        return result;
1✔
472
    }
473

474
    // adjusted from org.apache.tools.ant.types.Commandline#translateCommandLine
475
    public static List<String> parseCommand(String str) {
476
        final int normal = 0;
1✔
477
        final int inQuote = 1;
1✔
478
        final int inDoubleQuote = 2;
1✔
479
        int state = normal;
1✔
480
        final StringTokenizer tok = new StringTokenizer(str, "\"' ", true);
1✔
481
        final ArrayList<String> result = new ArrayList<>();
1✔
482
        final StringBuilder current = new StringBuilder();
1✔
483
        boolean lastTokenHasBeenQuoted = false;
1✔
484

485
        while (tok.hasMoreTokens()) {
1✔
486
            String nextTok = tok.nextToken();
1✔
487
            switch (state) {
1✔
488
                case inQuote:
489
                    if ("'".equals(nextTok)) {
×
490
                        lastTokenHasBeenQuoted = true;
×
491
                        state = normal;
×
492
                    } else {
493
                        current.append(nextTok);
×
494
                    }
495
                    break;
×
496
                case inDoubleQuote:
497
                    if ("\"".equals(nextTok)) {
1✔
498
                        lastTokenHasBeenQuoted = true;
1✔
499
                        state = normal;
1✔
500
                    } else {
501
                        current.append(nextTok);
1✔
502
                    }
503
                    break;
1✔
504
                default:
505
                    if ("'".equals(nextTok)) {
1✔
506
                        state = inQuote;
×
507
                    } else if ("\"".equals(nextTok)) {
1✔
508
                        state = inDoubleQuote;
1✔
509
                    } else if (" ".equals(nextTok)) {
1✔
510
                        if (lastTokenHasBeenQuoted || current.length() > 0) {
1✔
511
                            result.add(current.toString());
1✔
512
                            current.setLength(0);
1✔
513
                        }
514
                    } else {
515
                        current.append(nextTok);
1✔
516
                    }
517
                    lastTokenHasBeenQuoted = false;
1✔
518
                    break;
519
            }
520
        }
1✔
521

522
        if (lastTokenHasBeenQuoted || current.length() > 0) {
1✔
523
            result.add(current.toString());
1✔
524
        }
525

526
        if (state == inQuote || state == inDoubleQuote) {
1✔
527
            throw new IllegalStateException(RB.$("ERROR_unbalanced_quotes", str));
×
528
        }
529

530
        return result;
1✔
531
    }
532
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc