Last active
December 19, 2015 10:29
-
-
Save sahirshahryar/5940796 to your computer and use it in GitHub Desktop.
Evaluates stuff.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A revamped version of the BooleanLogic class, with recursive parenthetical support. | |
package org.futuredev.tracker.util.lexers.params; | |
import org.futuredev.tracker.Tracker; | |
import org.futuredev.tracker.session.user.PlayerSession; | |
import org.futuredev.tracker.session.user.Session; | |
import org.futuredev.tracker.util.cmd.CommandProcessing; | |
import org.futuredev.tracker.util.enums.Permissions; | |
import org.futuredev.tracker.util.ex.CommandException; | |
import org.futuredev.tracker.util.lexers.SQLLexer; | |
import org.futuredev.tracker.util.math.Levenshtein; | |
import org.bukkit.Bukkit; | |
import org.bukkit.command.CommandSender; | |
import org.bukkit.entity.Player; | |
import org.futuredev.tracker.util.math.Vector; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
/** | |
* Performs a lexical analysis of the PLAYER parameter. | |
* This is the most complex lexer here. Maybe even the | |
* most complex class in this plugin. Well, besides | |
* ParameterProcessor. | |
* | |
* @author afistofirony | |
*/ | |
public class TargetLexer { | |
boolean userIsConsole, allowOffline; | |
ArrayList<String> matches, result, exclusions; | |
private static final char ALL = '*', SELF = '=', NEAR = '#', MOD = '%', | |
EXPRESSION = '^', PARTIAL = '&', STARTS = '>', ENDS = '<', WORLD = '$'; | |
enum Logic { AND, OR } | |
/** | |
* Parses the given player input. | |
* @param user The session sending this input. | |
* @param input The input to parse. | |
* @param allowOffline Whether or not to allow offline players. This allows this class | |
* to have dual functionality for both parameters and regular commands. | |
* @throws CommandException Thrown if an issue is encountered. | |
*/ | |
public TargetLexer (Session user, String input, boolean allowOffline) throws CommandException { | |
this.userIsConsole = !(user instanceof PlayerSession); | |
this.allowOffline = allowOffline; | |
this.result = new ArrayList<String>(); | |
this.matches = new ArrayList<String>(); | |
this.exclusions = new ArrayList<String>(); | |
for (Player player : Bukkit.getOnlinePlayers()) { | |
if (this.parseFor(user, player, input)) { | |
matches.add(player.getName()); | |
continue; | |
} | |
exclusions.add(player.getName()); | |
} | |
for (String match : matches) { | |
if (!exclusions.contains(match)) | |
result.add(match); | |
} | |
} | |
public String get (int index) { | |
return result.get(index); | |
} | |
public Player getPlayer (int index) { | |
return Bukkit.getPlayerExact(this.get(index)); | |
} | |
public PlayerSession getSession (int index) { | |
return (PlayerSession) Tracker.getServices().getSession(this.get(index)); | |
} | |
/** | |
* Recursively parses the input for the given player. | |
* @param sender The user sending the input. | |
* @param evaluate The player to check the conditions for. | |
* @param input The input to parse. | |
* @return True if the player matches the conditions given. | |
* @throws CommandException Thrown if an exception is encountered. | |
*/ | |
private boolean parseFor (Session sender, Player evaluate, String input) | |
throws CommandException { | |
String evaluation = ""; | |
int depth = 0; | |
/* Used to determine how many parentheses are grouped together. | |
* For example, the ~ in (((~))) has a depth of two. | |
*/ | |
loop: for (int i = 0; i < input.length(); ++i) { // Get rid of all parentheses. | |
final char opener = input.charAt(i); | |
switch (opener) { | |
case '(': | |
case '[': | |
case '{': | |
++depth; | |
final char closer = (opener == '(' ? ')' : (opener == '[' ? ']' : '}')); | |
for (int j = i + 1; j < input.length(); ++j) { | |
if (input.charAt(j) == closer) { | |
--depth; | |
if (depth == 0) { | |
evaluation += (parseFor(sender, evaluate, | |
input.substring(i + 1, j - 1)) ? '1' : '0'); | |
i = j; | |
continue loop; | |
} | |
} else switch (input.charAt(j)) { | |
case '(': | |
case '[': | |
case '{': | |
++depth; | |
} | |
} | |
default: | |
evaluation += opener; | |
} | |
} | |
String next = ""; | |
for (int i = 0; i < evaluation.length(); ++i) { | |
switch (evaluation.charAt(i)) { | |
case ',': | |
int j; | |
loop: for (j = i + 1; j < evaluation.length(); ++j) { | |
switch (evaluation.charAt(j)) { | |
case ',': | |
case ';': | |
break loop; | |
} | |
} | |
next += evaluation.substring(i, j - 1); | |
next = Character.toString(processGroup(Logic.OR, sender, next, evaluate)); | |
evaluation = evaluation.substring(0, i - 1) + next + evaluation.substring(j); | |
// The next iteration will increment i anyway. next has become a single character in | |
// the entire string, so we don't need to do anything here. | |
break; | |
case ';': | |
loop: for (j = i + 1; j < evaluation.length(); ++j) { | |
switch (evaluation.charAt(j)) { | |
case ',': | |
case ';': | |
break loop; | |
} | |
} | |
next += evaluation.substring(i, j - 1); | |
if (next.matches("^(!)?[a-zA-Z0-9_]+") && allowOffline) { // Offline match? | |
if (next.charAt(0) != '!') | |
result.add(next); | |
else | |
exclusions.add(next.substring(1)); | |
continue; | |
} | |
next = Character.toString(processGroup(Logic.AND, sender, next, evaluate)); | |
evaluation = evaluation.substring(0, i - 1) + next + evaluation.substring(j); | |
break; | |
default: next += evaluation.charAt(i); | |
} | |
} | |
return evaluation.contains("1"); | |
} | |
private char processGroup (Logic logic, Session user, String statement, Player evaluate) | |
throws CommandException { | |
boolean result = false; | |
ArrayList<String> evaluations = new ArrayList<String>(); | |
switch (logic) { | |
default: | |
case OR: | |
if (!statement.contains(",")) | |
evaluations.add(statement); | |
else evaluations.addAll(Arrays.asList(statement.split(","))); | |
for (String condition : evaluations) { | |
if (condition.matches("^[a-zA-Z0-9]+") && allowOffline) | |
throw new CommandException("Exception.Lexer.OfflineLogic"); | |
boolean not = condition.charAt(0) == '!'; | |
result = result || (not != processLogic(condition.substring(not ? 1 : 0), user, evaluate)); | |
/* Justification for boolean != boolean | |
* If a NOT operator is applied, we need to tell the opposite value, so... | |
* input = !% (not a staff member) | |
* User we are checking for is a staff member and 'not' is true: | |
* true != true -> false, just as intended | |
*/ | |
} | |
break; | |
case AND: | |
result = true; // AND logic needs this to be true | |
if (!statement.contains(";")) | |
throw new CommandException("Exception.Lexer.IncompleteLogic"); | |
else evaluations.addAll(Arrays.asList(statement.split(";"))); | |
for (String condition : evaluations) { | |
if (condition.matches("^[a-zA-Z0-9]+") && allowOffline) | |
throw new CommandException("Exception.Lexer.OfflineLogic"); | |
boolean not = condition.charAt(0) == '!'; | |
result = result && (not != processLogic(condition, user, evaluate)); | |
} | |
} | |
return result ? '1' : '0'; | |
} | |
/** | |
* Processes the logic given. | |
* @param value The input. | |
* @param sender The sender of the input. | |
* @param target The player to evaluate. | |
* @return Whether or not the given player matches the given logic. | |
* @throws CommandException Thrown if an issue is encountered. | |
*/ | |
private boolean processLogic (String value, Session sender, Player target) throws CommandException { | |
if (value.equals("1") || value.equals("!0")) return true; | |
if (value.equals("0") || value.equals("!1")) return false; | |
switch (value.length()) { | |
case 0: | |
return false; | |
case 1: | |
switch (value.charAt(0)) { | |
case ALL: | |
sender.authorise(Permissions.TARGETING_ALL); | |
this.exclusions.clear(); // Clear exclusions, this specifies ALL! | |
return true; | |
case WORLD: | |
sender.authorise(Permissions.TARGETING_WORLD); | |
CommandProcessing.restrict(sender); | |
LocationLexer lexer = new LocationLexer(sender, | |
value.substring(1), null, false, -1); | |
return lexer.withinArea(target); | |
case SELF: | |
return !userIsConsole; | |
// Not evaluating the player var here, so sender needs to be a player. | |
case NEAR: | |
sender.authorise(Permissions.TARGETING_NEAR); | |
CommandProcessing.restrict(sender); | |
PlayerSession player = (PlayerSession) sender; | |
return player.getSurroundingArea(15). | |
contains(Vector.fromBukkitVector(target.getLocation().toVector())); | |
case MOD: // It's funny because modulus. | |
sender.authorise(Permissions.TARGETING_STAFF); | |
return sender.hasPermission(Permissions.MOD); | |
default: | |
return false; | |
} | |
default: | |
switch (value.charAt(0)) { | |
case WORLD: | |
sender.authorise(Permissions.TARGETING_WORLD); | |
LocationLexer lexer = new LocationLexer(sender, | |
value.substring(1), null, false, -1); | |
return lexer.withinArea(target); | |
case NEAR: | |
sender.authorise(Permissions.TARGETING_NEAR); | |
lexer = new LocationLexer(sender, | |
value.substring(1), null, false, -1); | |
return lexer.withinArea(target); | |
case STARTS: | |
sender.authorise(Permissions.TARGETING_STARTS_WITH); | |
return target.getName().toLowerCase().startsWith(value.substring(1).toLowerCase()); | |
case ENDS: | |
sender.authorise(Permissions.TARGETING_ENDS_WITH); | |
return target.getName().toLowerCase().endsWith(value.substring(1).toLowerCase()); | |
case EXPRESSION: | |
sender.authorise(Permissions.TARGETING_REGEX); | |
return target.getName().matches(value.substring(1)); | |
case PARTIAL: | |
if (allowOffline) { | |
sender.authorise(Permissions.TARGETING_PARTIAL); | |
return target.getName().contains(value.substring(1)) || | |
new Levenshtein(value.substring(1), target.getName()).getDistance() <= | |
(Tracker.getSettings().lev ? Tracker.getSettings().levLimit : 0); | |
} else { | |
return target.getName().equalsIgnoreCase(value.substring(1)); | |
} | |
default: | |
return !allowOffline && | |
(target.getName().contains(value) || | |
new Levenshtein(value, target.getName()).getDistance() <= | |
(Tracker.getSettings().lev ? Tracker.getSettings().levLimit : 0)); | |
// We handle offline matching separately anyways. | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment