001package fr.aumgn.bukkitutils.command.args;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.HashSet;
006import java.util.Map;
007import java.util.Map.Entry;
008import java.util.Set;
009
010import fr.aumgn.bukkitutils.command.CommandsMessages;
011import fr.aumgn.bukkitutils.command.exception.CommandUsageError;
012
013public class CommandArgsParser {
014
015    private CommandsMessages messages;
016    private String[] args;
017    private Set<Character> flags;
018    private Map<Character, String> argsFlags;
019
020    public CommandArgsParser(CommandsMessages messages, String[] tokens) {
021        this.messages = messages;
022        parse(tokens);
023    }
024
025    private void parse(String[] tokens) {
026        flags = new HashSet<Character>();
027        argsFlags = new HashMap<Character, String>();
028        ArrayList<String> argsList = new ArrayList<String>();
029        boolean quoted = false;
030        StringBuilder current = null;
031
032        for (String token : tokens) {
033
034            if (quoted) {
035                if (!token.isEmpty()
036                        && token.charAt(token.length() - 1) == '\"') {
037                    current.append(" ");
038                    current.append(token.substring(0, token.length() - 1));
039                    argsList.add(current.toString());
040                    quoted = false;
041                } else {
042                    current.append(" ");
043                    current.append(token);
044                }
045            } else {
046                if (token.isEmpty()) {
047                    continue;
048                }
049
050                if (token.charAt(0) == '"') {
051                    if (token.charAt(token.length() - 1) == '\"') {
052                        argsList.add(token.substring(1, token.length() - 1));
053                    } else {
054                        quoted = true;
055                        current = new StringBuilder();
056                        current.append(token.substring(1));
057                    }
058                } else if (token.charAt(0) == '-' && token.length() > 1
059                        && Character.isLetter(token.charAt(1))) {
060                    parseFlags(token.substring(1));
061                } else {
062                    argsList.add(token);
063                }
064            }
065        }
066
067        if (quoted) {
068            throw new CommandUsageError(
069                    messages.missingEndingQuote(current.toString()));
070        }
071
072        args = argsList.toArray(new String[argsList.size()]);
073    }
074
075    private void parseFlags(String flagsString) {
076        int equal = flagsString.indexOf("=");
077        if (equal == -1) {
078            parseRegularFlags(flagsString);
079            return;
080        }
081
082        if (equal > 1) {
083            parseRegularFlags(flagsString.substring(0, equal -1));
084        }
085
086        argsFlags.put(
087                flagsString.charAt(equal - 1),
088                flagsString.substring(equal + 1));
089    }
090
091    private void parseRegularFlags(String flagsString) {
092        for (char flag : flagsString.toCharArray()) {
093            flags.add(flag);
094        }
095    }
096
097    public void validate(Set<Character> allowedFlags,
098            Set<Character> allowedArgsFlags, int min, int max) {
099        StringBuilder invalidFlags = new StringBuilder();
100        for (char flag : flags) {
101            if (!allowedFlags.contains(flag)) {
102                invalidFlags.append(flag);
103            }
104        }
105
106        for (char flag : argsFlags.keySet()) {
107            if (!allowedArgsFlags.contains(flag)) {
108                invalidFlags.append(flag);
109            }
110        }
111
112        if (invalidFlags.length() > 0) {
113            throw new CommandUsageError(
114                    messages.invalidFlag(invalidFlags.toString()));
115        }
116
117        if (args.length < min) {
118            throw new CommandUsageError(
119                    messages.missingArguments(args.length, min));
120        }
121        if (max != -1 && args.length > max) {
122            throw new CommandUsageError(
123                    messages.tooManyArguments(args.length, max));
124        }
125    }
126
127    public Set<Character> getFlags() {
128        return flags;
129    }
130
131    public Map<Character, String> getArgsFlags() {
132        return argsFlags;
133    }
134
135    public String[] getArgs() {
136        return args;
137    }
138
139    public String[] linearize() {
140        int length = args.length + flags.size() + argsFlags.size();
141        String[] linearized = new String[length];
142        System.arraycopy(args, 0, linearized, 0, args.length);
143        int i = args.length;
144        for (char flag : flags) {
145            linearized[i] = "-" + flag;
146            i++;
147        }
148        for (Entry<Character, String> argFlag : argsFlags.entrySet()) {
149            linearized[i] = "-" + argFlag.getKey() + "=" + argFlag.getValue();
150            i++;
151        }
152
153        return linearized;
154    }
155}