summaryrefslogtreecommitdiff
path: root/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java
blob: 4ac9bd369b9b17fb4ac046326b024d94b7c43c9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package io.dico.dicore.command.parameter;

/**
 * An interface to process tokens such as quotes
 */
public interface IArgumentPreProcessor {

    /**
     * Preprocess the arguments without modifying the array.
     * Might return the same array (in which case no changes were made).
     *
     * @param argStart the index within the array where the given arguments start (the part before that identifies the command)
     * @param args     the arguments
     * @return the arguments after preprocessing
     */
    String[] process(int argStart, String[] args);

    IArgumentPreProcessor NONE = (argStart, args) -> args;

    /**
     * Get an IArgumentPreProcessor that merges arguments between any two tokens
     *
     * @param tokens     The tokens that the merged arguments should be enclosed by, in subsequent pairs.
     *                   Example: []{}""
     *                   This would mean the following would be merged: [ hello this is a merged argument]
     * @param escapeChar the char that can be used to escape the given tokens
     * @return The IArgumentPreProcessor
     */
    static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) {
        if (tokens.isEmpty() || (tokens.length() & 1) != 0) {
            throw new IllegalArgumentException();
        }

        return (argStart, args) -> {
            if (!(0 <= argStart && argStart <= args.length)) {
                throw new IndexOutOfBoundsException();
            }

            args = args.clone();
            int removeCount = 0;
            int closingTokenIdx = 0;
            int sectionStart = -1;

            for (int i = argStart; i < args.length; i++) {
                String arg = args[i];
                if (arg == null || arg.isEmpty()) {
                    continue;
                }

                if (closingTokenIdx != 0) {
                    int idx = tokens.indexOf(arg.charAt(arg.length() - 1));
                    if (idx == closingTokenIdx) {

                        // count escape chars
                        int index = arg.length() - 1;
                        int count = 0;
                        while (index > 0 && arg.charAt(--index) == escapeChar) {
                            count++;
                        }

                        // remove the final char plus half the count, rounding upwards.
                        args[i] = arg.substring(0, args.length - 1 - (count + 1) / 2);

                        if ((count & 1) == 0) {
                            // not escaped
                            StringBuilder concat = new StringBuilder(args[sectionStart].substring(1));
                            for (int j = sectionStart + 1; j <= i; j++) {
                                concat.append(' ').append(args[j]);
                                args[j] = null;
                                removeCount++;
                            }

                            args[sectionStart] = concat.toString();

                            sectionStart = -1;
                            closingTokenIdx = 0;

                        } else {
                            // it's escaped
                            // add final char because it was escaped
                            args[i] += tokens.charAt(closingTokenIdx);

                        }
                    }

                    if (i == args.length - 1) {
                        // if the closing token isn't found, reset state and start from the index subsequent to the one where the opener was found
                        // it should also undo removal of any escapes... it doesn't do that
                        i = sectionStart + 1;
                        closingTokenIdx = 0;
                        sectionStart = -1;
                    }

                    continue;
                }

                int idx = tokens.indexOf(arg.charAt(0));
                if (idx == -1 || (idx & 1) != 0) {
                    continue;
                }

                closingTokenIdx = idx | 1;
                sectionStart = i;

                // make sure to check from the current index for a closer
                i--;
            }

            if (removeCount == 0) {
                return args;
            }

            String[] result = new String[args.length - removeCount];
            int i = 0;
            for (String arg : args) {
                if (arg != null) {
                    result[i++] = arg;
                }
            }

            return result;
        };

    }

}