/* * Copyright (c) CovertJaguar, 2014 http://railcraft.info * * This code is the property of CovertJaguar * and may only be used with explicit written * permission unless otherwise specified on the * license page at http://railcraft.info/wiki/info:license. */ package mods.railcraft.common.blocks.signals; import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier; import mods.railcraft.api.carts.CartTools; import mods.railcraft.api.carts.IPaintedCart; import mods.railcraft.api.carts.IRefuelableCart; import mods.railcraft.api.carts.IRoutableCart; import mods.railcraft.common.carts.EntityLocomotive; import mods.railcraft.common.carts.EnumCart; import mods.railcraft.common.carts.Train; import mods.railcraft.common.gui.tooltips.ToolTip; import mods.railcraft.common.plugins.forge.LocalizationPlugin; import mods.railcraft.common.util.misc.EnumColor; import net.minecraft.entity.item.EntityMinecart; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** * @author CovertJaguar <http://www.railcraft.info/> */ public class RoutingLogic { public static final String REGEX_SYMBOL = "\\?"; private Deque<Condition> conditions; private RoutingLogicException error; private RoutingLogic(LinkedList<String> data) { try { if (data != null) parseTable(data); else throw new RoutingLogicException("railcraft.gui.routing.logic.blank", null); } catch (RoutingLogicException ex) { error = ex; } } public static RoutingLogic buildLogic(LinkedList<String> data) { return new RoutingLogic(data); } public RoutingLogicException getError() { return error; } public boolean isValid() { return conditions != null; } private void parseTable(LinkedList<String> data) throws RoutingLogicException { Deque<Condition> stack = new LinkedList<Condition>(); Iterator<String> it = data.descendingIterator(); while (it.hasNext()) { String line = it.next().trim(); stack.push(parseLine(line, stack)); } conditions = stack; } private EntityMinecart getRoutableCart(EntityMinecart cart) { Train train = Train.getTrain(cart); if (train == null) return null; if (train.size() == 1) return cart; if (train.isTrainEnd(cart)) { if (cart instanceof IRoutableCart) return cart; if (cart instanceof IPaintedCart) return cart; if (cart instanceof IRefuelableCart) return cart; } return train.getLocomotive(); } public boolean matches(IRoutingTile tile, EntityMinecart cart) { if (conditions == null) return false; EntityMinecart controllingCart = getRoutableCart(cart); if (controllingCart == null) return false; for (Condition condition : conditions) { if (condition.matches(tile, controllingCart)) return true; } return false; } private Condition parseLine(String line, Deque<Condition> stack) throws RoutingLogicException { try { if (line.startsWith("Dest")) return new DestCondition(line); if (line.startsWith("Color")) return new ColorCondition(line); if (line.startsWith("Owner")) return new OwnerCondition(line); if (line.startsWith("Name")) return new NameCondition(line); if (line.startsWith("Type")) return new TypeCondition(line); if (line.startsWith("NeedsRefuel")) return new RefuelCondition(line); if (line.startsWith("Ridden")) return new RiddenCondition(line); if (line.startsWith("Riding")) return new RidingCondition(line); if (line.startsWith("Redstone")) return new RedstoneCondition(line); if (line.startsWith("Loco")) return new LocoCondition(line); } catch (RoutingLogicException ex) { throw ex; } catch (Exception ex) { throw new RoutingLogicException("railcraft.gui.routing.logic.malformed.syntax", line); } try { if (line.equals("NOT")) return new NOT(stack.pop()); if (line.equals("AND")) return new AND(stack.pop(), stack.pop()); if (line.equals("OR")) return new OR(stack.pop(), stack.pop()); } catch (NoSuchElementException ex) { throw new RoutingLogicException("railcraft.gui.routing.logic.insufficient.operands", line); } throw new RoutingLogicException("railcraft.gui.routing.logic.unrecognized.keyword", line); } public class RoutingLogicException extends Exception { private final ToolTip tips = new ToolTip(); public RoutingLogicException(String errorTag, String line) { tips.add(EnumChatFormatting.RED + LocalizationPlugin.translate(errorTag)); if (line != null) tips.add("\"" + line + "\""); } public ToolTip getToolTip() { return tips; } } private abstract class Condition { public abstract boolean matches(IRoutingTile tile, EntityMinecart cart); } private abstract class ParsedCondition extends Condition { public final String keyword, line, value; public final boolean isRegex; private ParsedCondition(String keyword, boolean supportsRegex, String line) throws RoutingLogicException { this.keyword = keyword; this.line = line; String keywordMatch = keyword + REGEX_SYMBOL + "?="; if (!line.matches(keywordMatch + ".*")) throw new RoutingLogicException("railcraft.gui.routing.logic.unrecognized.keyword", line); this.isRegex = line.matches(keyword + REGEX_SYMBOL + "=.*"); if (!supportsRegex && isRegex) throw new RoutingLogicException("railcraft.gui.routing.logic.regex.unsupported", line); this.value = line.replaceFirst(keywordMatch, ""); if (isRegex) try { Pattern.compile(value); } catch (PatternSyntaxException ex) { throw new RoutingLogicException("railcraft.gui.routing.logic.regex.invalid", line); } } @Override public abstract boolean matches(IRoutingTile tile, EntityMinecart cart); } private class NOT extends Condition { private final Condition a; public NOT(Condition a) { this.a = a; } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { return !a.matches(tile, cart); } } private class AND extends Condition { private final Condition a, b; public AND(Condition a, Condition b) { this.a = a; this.b = b; } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { return a.matches(tile, cart) && b.matches(tile, cart); } } private class OR extends Condition { private final Condition a, b; public OR(Condition a, Condition b) { this.a = a; this.b = b; } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { return a.matches(tile, cart) || b.matches(tile, cart); } } private class DestCondition extends ParsedCondition { public DestCondition(String line) throws RoutingLogicException { super("Dest", true, line); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { if (cart instanceof IRoutableCart) { String cartDest = ((IRoutableCart) cart).getDestination(); if (value.equals("null")) return cartDest == null || cartDest.equals(""); if (isRegex) return cartDest.matches(value); return cartDest.startsWith(value); } return false; } } private class OwnerCondition extends ParsedCondition { public OwnerCondition(String line) throws RoutingLogicException { super("Owner", false, line); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { return value.equalsIgnoreCase(CartTools.getCartOwner(cart).getName()); } } private class NameCondition extends ParsedCondition { public NameCondition(String line) throws RoutingLogicException { super("Name", true, line); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { String customName = cart.func_95999_t(); if (customName == null) return "null".equals(value); if (isRegex) return customName.matches(value); return value.equals(customName); } } private class TypeCondition extends ParsedCondition { public TypeCondition(String line) throws RoutingLogicException { super("Type", false, line); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { ItemStack stack = cart.getCartItem(); if (stack == null || stack.getItem() == null) return false; UniqueIdentifier itemName = GameRegistry.findUniqueIdentifierFor(stack.getItem()); if (itemName != null) { String nameString = itemName.modId + ":" + itemName.name; return nameString.equalsIgnoreCase(value); } return false; } } private class RefuelCondition extends ParsedCondition { private final boolean needsRefuel; public RefuelCondition(String line) throws RoutingLogicException { super("NeedsRefuel", false, line); this.needsRefuel = Boolean.parseBoolean(value); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { if (cart instanceof IRefuelableCart) { IRefuelableCart rCart = (IRefuelableCart) cart; return needsRefuel == rCart.needsRefuel(); } return false; } } private class RiddenCondition extends ParsedCondition { private final boolean ridden; public RiddenCondition(String line) throws RoutingLogicException { super("Ridden", false, line); this.ridden = Boolean.parseBoolean(value); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { for (EntityMinecart c : Train.getTrain(cart)) { if (c != null && c.riddenByEntity instanceof EntityPlayer) return ridden; } return !ridden; } } private class RidingCondition extends ParsedCondition { public RidingCondition(String line) throws RoutingLogicException { super("Riding", false, line); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { for (EntityMinecart c : Train.getTrain(cart)) { if (c != null && c.riddenByEntity instanceof EntityPlayer) return c.riddenByEntity.getCommandSenderName().equalsIgnoreCase(value); } return false; } } private class RedstoneCondition extends ParsedCondition { private final boolean powered; public RedstoneCondition(String line) throws RoutingLogicException { super("Redstone", false, line); this.powered = Boolean.parseBoolean(value); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { return powered == tile.isPowered(); } } private class ColorCondition extends ParsedCondition { private final EnumColor primary, secondary; public ColorCondition(String line) throws RoutingLogicException { super("Color", false, line); String[] colors = value.split(","); if (colors[0].equals("Any") || colors[0].equals("*")) primary = null; else { primary = EnumColor.fromName(colors[0]); if (primary == null) throw new RoutingLogicException("railcraft.gui.routing.logic.unrecognized.keyword", colors[0]); } if (colors.length == 1 || colors[1].equals("Any") || colors[1].equals("*")) secondary = null; else { secondary = EnumColor.fromName(colors[1]); if (secondary == null) throw new RoutingLogicException("railcraft.gui.routing.logic.unrecognized.keyword", colors[1]); } } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { if (cart instanceof IPaintedCart) { IPaintedCart pCart = (IPaintedCart) cart; return (primary == null || primary.ordinal() == pCart.getPrimaryColor()) && (secondary == null || secondary.ordinal() == pCart.getSecondaryColor()); } return false; } } private class LocoCondition extends ParsedCondition { public LocoCondition(String line) throws RoutingLogicException { super("Loco", false, line); } @Override public boolean matches(IRoutingTile tile, EntityMinecart cart) { if (cart instanceof EntityLocomotive) { EntityLocomotive loco = (EntityLocomotive)cart; if (value.equalsIgnoreCase("Electric")) return loco.getCartType() == EnumCart.LOCO_ELECTRIC; if (value.equalsIgnoreCase("Steam")) return loco.getCartType() == EnumCart.LOCO_STEAM_SOLID; if (value.equalsIgnoreCase("Steam_Magic")) return loco.getCartType() == EnumCart.LOCO_STEAM_MAGIC; if (value.equalsIgnoreCase("None")) return false; } if (value.equalsIgnoreCase("None")) return true; return false; } } }