/* Copyright (c) 2008-2010, developers of the Ascension Log Visualizer * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package com.googlecode.logVisualizer.parser.lineParsers; import java.io.BufferedReader; import java.io.IOException; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; import net.java.dev.spellcast.utilities.DataUtilities; import net.java.dev.spellcast.utilities.UtilityConstants; import com.googlecode.logVisualizer.logData.LogDataHolder; import com.googlecode.logVisualizer.logData.turn.turnAction.EquipmentChange; import com.googlecode.logVisualizer.parser.UsefulPatterns; /** * A parser for the equipment change notation in mafia session logs. * <p> * The format looks like this: * <p> * {@code equip _slotName_ _itemName_} * <p> * OR * <p> * {@code unequip _slotName_ _itemName_} * <p> * OR * <p> * {@code outfit _outfitName_} * <p> * OR * <p> * {@code custom outfit _outfitName_} */ public final class EquipmentLineParser extends AbstractLineParser { private static final Map<String, EquipmentSetup> outfitsMap; static { outfitsMap = new HashMap<>(150); final Pattern outfitPattern = Pattern .compile(".+\\|.+\\|.+\\|.+\\|.+\\|.+\\|.+\\|.+\\|.+"); final String splitPattern = "\\s*\\|\\s*"; String tmpLine; final BufferedReader br = DataUtilities.getReader( UtilityConstants.KOL_DATA_DIRECTORY, "outfits.txt"); try { while ((tmpLine = br.readLine()) != null) { if (!tmpLine.startsWith("//") && (tmpLine.length() > 15) && outfitPattern.matcher(tmpLine).matches()) { final String[] result = tmpLine.split(splitPattern); final String outfitName = result[0] .toLowerCase(Locale.ENGLISH); final boolean isHat = Boolean.parseBoolean(result[1]); final boolean isWeapon = Boolean.parseBoolean(result[2]); final boolean isOffhand = Boolean.parseBoolean(result[3]); final boolean isShirt = Boolean.parseBoolean(result[4]); final boolean isPants = Boolean.parseBoolean(result[5]); final boolean isAcc1 = Boolean.parseBoolean(result[6]); final boolean isAcc2 = Boolean.parseBoolean(result[7]); final boolean isAcc3 = Boolean.parseBoolean(result[8]); EquipmentLineParser.outfitsMap.put(outfitName, new EquipmentSetup(isHat, isWeapon, isOffhand, isShirt, isPants, isAcc1, isAcc2, isAcc3)); } } br.close(); } catch (final IOException e) { e.printStackTrace(); } } private static final String EQUIP_STRING = "equip"; private static final String UNEQUIP_STRING = "unequip"; private static final String OUTFIT_STRING = "outfit"; private static final String CUSTOM_OUTFIT_STRING = "custom outfit"; private EquipmentChange lastUsedEquipment = new EquipmentChange(0, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING); /** * {@inheritDoc} */ @Override public boolean parseLine(final String line, final LogDataHolder logData) { // Sometimes the action classifier strings (equip/unequip/outfit) are // written with a capital first letter, sometimes not. This side-steps // that problem. return super.parseLine(line.toLowerCase(Locale.ENGLISH), logData); } /** * {@inheritDoc} */ @Override protected void doParsing(final String line, final LogDataHolder logData) { // Outfit handling if (line.startsWith(EquipmentLineParser.OUTFIT_STRING)) { final EquipmentSetup outfit = EquipmentLineParser.outfitsMap .get(line.substring(line .indexOf(UsefulPatterns.WHITE_SPACE) + 1)); if (outfit != null) { final EquipmentChange lastChange = logData .getLastEquipmentChange(); this.lastUsedEquipment = lastChange; final String hat = outfit.isHat() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getHat(); final String weapon = outfit.isWeapon() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getWeapon(); final String offhand = outfit.isOffhand() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getOffhand(); final String shirt = outfit.isShirt() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getShirt(); final String pants = outfit.isPants() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getPants(); final String acc1 = outfit.isAcc1() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getAcc1(); final String acc2 = outfit.isAcc2() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getAcc2(); final String acc3 = outfit.isAcc3() ? EquipmentChange.NO_EQUIPMENT_STRING : lastChange.getAcc3(); logData.addEquipmentChange(new EquipmentChange(logData .getTurnsSpent().last().getEndTurn(), hat, weapon, offhand, shirt, pants, acc1, acc2, acc3, lastChange .getFamEquip())); } return; } else if (line.startsWith(EquipmentLineParser.CUSTOM_OUTFIT_STRING)) { if (line.equals("custom outfit backup") || line.equals("custom outfit your previous outfit")) { // Those two custom outfits roll the equipment back to the last // used one, so we do the same. logData.addEquipmentChange(new EquipmentChange(logData .getTurnsSpent().last().getEndTurn(), this.lastUsedEquipment.getHat(), this.lastUsedEquipment .getWeapon(), this.lastUsedEquipment .getOffhand(), this.lastUsedEquipment .getShirt(), this.lastUsedEquipment.getPants(), this.lastUsedEquipment.getAcc1(), this.lastUsedEquipment.getAcc2(), this.lastUsedEquipment.getAcc3(), this.lastUsedEquipment.getFamEquip())); } else { // We cannot guarantee for anything as far as custom outfits are // concerned, so the only sensible thing is to assume nothing in // the way of character equipment. final EquipmentChange lastChange = logData .getLastEquipmentChange(); this.lastUsedEquipment = lastChange; logData.addEquipmentChange(new EquipmentChange(logData .getTurnsSpent().last().getEndTurn(), EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, EquipmentChange.NO_EQUIPMENT_STRING, lastChange .getFamEquip())); } return; } // Equip/Unequip handling final String tmp = line.substring(line .indexOf(UsefulPatterns.WHITE_SPACE) + 1); final int whiteSpaceIndex = tmp.indexOf(UsefulPatterns.WHITE_SPACE); // Strings that don't fit the format should be ignored. if (whiteSpaceIndex < 0) { return; } final String slotName = tmp.substring(0, whiteSpaceIndex); final String itemName = tmp.substring(whiteSpaceIndex + 1); // Act depending on equip or unequip. if (line.startsWith(EquipmentLineParser.EQUIP_STRING)) { this.addEquipmentChange(slotName, itemName, logData); } else { this.addEquipmentChange(slotName, EquipmentChange.NO_EQUIPMENT_STRING, logData); } } private void addEquipmentChange(final String slotName, final String itemName, final LogDataHolder logData) { final EquipmentChange lastChange = logData.getLastEquipmentChange(); this.lastUsedEquipment = lastChange; // Switch constructs are ugly, but there isn't really a better way to do // this since at some point which equipment slot is used has to be // checked. final EquipmentChange equipmentChange; switch (EquipmentSlot.fromString(slotName)) { case HAT: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), itemName, lastChange.getWeapon(), lastChange.getOffhand(), lastChange.getShirt(), lastChange.getPants(), lastChange.getAcc1(), lastChange.getAcc2(), lastChange.getAcc3(), lastChange.getFamEquip()); break; case WEAPON: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), itemName, lastChange.getOffhand(), lastChange.getShirt(), lastChange.getPants(), lastChange.getAcc1(), lastChange.getAcc2(), lastChange.getAcc3(), lastChange.getFamEquip()); break; case OFFHAND: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), lastChange.getWeapon(), itemName, lastChange.getShirt(), lastChange.getPants(), lastChange.getAcc1(), lastChange.getAcc2(), lastChange.getAcc3(), lastChange.getFamEquip()); break; case SHIRT: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), lastChange.getWeapon(), lastChange.getOffhand(), itemName, lastChange.getPants(), lastChange.getAcc1(), lastChange.getAcc2(), lastChange.getAcc3(), lastChange.getFamEquip()); break; case PANTS: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), lastChange.getWeapon(), lastChange.getOffhand(), lastChange.getShirt(), itemName, lastChange.getAcc1(), lastChange.getAcc2(), lastChange.getAcc3(), lastChange.getFamEquip()); break; case ACC1: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), lastChange.getWeapon(), lastChange.getOffhand(), lastChange.getShirt(), lastChange.getPants(), itemName, lastChange.getAcc2(), lastChange.getAcc3(), lastChange.getFamEquip()); break; case ACC2: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), lastChange.getWeapon(), lastChange.getOffhand(), lastChange.getShirt(), lastChange.getPants(), lastChange.getAcc1(), itemName, lastChange.getAcc3(), lastChange.getFamEquip()); break; case ACC3: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), lastChange.getWeapon(), lastChange.getOffhand(), lastChange.getShirt(), lastChange.getPants(), lastChange.getAcc1(), lastChange.getAcc2(), itemName, lastChange.getFamEquip()); break; case FAM_EQUIP: equipmentChange = new EquipmentChange(logData.getTurnsSpent() .last().getEndTurn(), lastChange.getHat(), lastChange.getWeapon(), lastChange.getOffhand(), lastChange.getShirt(), lastChange.getPants(), lastChange.getAcc1(), lastChange.getAcc2(), lastChange.getAcc3(), itemName); break; default: equipmentChange = null; break; } // Add the equipment change. if (equipmentChange != null) { logData.addEquipmentChange(equipmentChange); } } /** * {@inheritDoc} */ @Override protected boolean isCompatibleLine(final String line) { return line.startsWith(EquipmentLineParser.EQUIP_STRING) || line.startsWith(EquipmentLineParser.UNEQUIP_STRING) || line.startsWith(EquipmentLineParser.OUTFIT_STRING) || line.startsWith(EquipmentLineParser.CUSTOM_OUTFIT_STRING); } private static class EquipmentSetup { private final boolean isHat; private final boolean isWeapon; private final boolean isOffhand; private final boolean isShirt; private final boolean isPants; private final boolean isAcc1; private final boolean isAcc2; private final boolean isAcc3; EquipmentSetup(final boolean isHat, final boolean isWeapon, final boolean isOffhand, final boolean isShirt, final boolean isPants, final boolean isAcc1, final boolean isAcc2, final boolean isAcc3) { this.isHat = isHat; this.isWeapon = isWeapon; this.isOffhand = isOffhand; this.isShirt = isShirt; this.isPants = isPants; this.isAcc1 = isAcc1; this.isAcc2 = isAcc2; this.isAcc3 = isAcc3; } public boolean isHat() { return this.isHat; } public boolean isWeapon() { return this.isWeapon; } public boolean isOffhand() { return this.isOffhand; } public boolean isShirt() { return this.isShirt; } public boolean isPants() { return this.isPants; } public boolean isAcc1() { return this.isAcc1; } public boolean isAcc2() { return this.isAcc2; } public boolean isAcc3() { return this.isAcc3; } } /** * This enumeration represents all equipment slots. */ private static enum EquipmentSlot { HAT("hat"), WEAPON("weapon"), OFFHAND("off-hand"), SHIRT("shirt"), PANTS( "pants"), ACC1("acc1"), ACC2("acc2"), ACC3("acc3"), FAM_EQUIP( "familiar"), NOT_DEFINED("not defined"); private static final Map<String, EquipmentSlot> stringToEnum = new HashMap<>(); static { for (final EquipmentSlot op : EquipmentSlot.values()) { EquipmentSlot.stringToEnum.put(op.toString(), op); } } private final String slotName; EquipmentSlot(final String slotName) { this.slotName = slotName; } @Override public String toString() { return this.slotName; } /** * @return The enum whose toString method returns a string which is * equal to the given string. If no match is found this method * will return {@code NOT_DEFINED}. */ public static EquipmentSlot fromString(final String slotName) { if (slotName == null) { throw new NullPointerException("Slot name must not be null."); } final EquipmentSlot equipmentSlot = EquipmentSlot.stringToEnum .get(slotName); return equipmentSlot != null ? equipmentSlot : NOT_DEFINED; } } }