/* * Bonus.java * Copyright 2002 (C) Greg Bingleman <byngl@hotmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on December 13, 2002, 9:19 AM * * Current Ver: $Revision$ * */ package pcgen.core.bonus; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import pcgen.base.formula.Formula; import pcgen.base.text.ParsingSeparator; import pcgen.cdom.base.Constants; import pcgen.core.bonus.BonusObj.StackType; import pcgen.persistence.PersistenceLayerException; import pcgen.persistence.lst.LstUtils; import pcgen.persistence.lst.prereq.PreParserFactory; import pcgen.rules.context.LoadContext; import pcgen.rules.persistence.TokenLibrary; import pcgen.util.Logging; /** * {@code Bonus} * * @author Greg Bingleman <byngl@hotmail.com> */ public final class Bonus { static final String BONUS_UNDEFINED = "*UNDEFINED"; private Bonus() { // Constructor } /** * Sorts a list of <tt>BonusObj</tt> objects so that dependant bonuses come * after the bonuses they depend on. * * @param listToSort The <tt>List</tt> of bonuses to sort. * @return The sorted list. */ public static List<BonusObj> sortBonusList(List<BonusObj> listToSort) { final List<BonusObj> tempList = new ArrayList<>(); // 'BONUS:blah|blah|Foo' depends on // 'BONUS:VAR|Foo|MyGoo' which depends on // 'BONUS:VAR|MyGoo|2' // BONUS: type | info | value // BONUS:COMBAT |TOHIT |STR // BONUS:STAT |STR |rage // BONUS:VAR |rage |2 for ( final BonusObj bonus : listToSort ) { int iFound = 0; String bonusInfo = bonus.getBonusInfo(); for (int ii = 0; ii < tempList.size(); ii++) { final BonusObj tempBonus = tempList.get(ii); if (tempBonus.getDependsOn(bonusInfo)) { iFound = ii; } } tempList.add(iFound, bonus); } int iCount = tempList.size(); for (int i = 0; i < iCount; ) { final BonusObj bonus = tempList.get(i); // // Move to end of list // if (bonus.getDependsOn("JEPFORMULA")) //$NON-NLS-1$ { tempList.remove(i); tempList.add(bonus); --iCount; } else { ++i; } } listToSort = tempList; final ArrayList<BonusObj> tempList2 = new ArrayList<>(); // go through and move all the static bonuses to the front final int aSize = listToSort.size(); for (int i = 0; i < aSize; i++) { final BonusObj bonus = listToSort.get(i); if (bonus.isValueStatic()) { tempList2.add(0, bonus); } else { tempList2.add(bonus); } } return tempList2; } /** * Create a new Bonus * @param context TODO * @param bonusString * @return BonusObj * * TODO - This is doing all manner of string parsing. It really belongs in * the persistence layer. */ public static BonusObj newBonus(LoadContext context, final String bonusString) { ParsingSeparator sep = new ParsingSeparator(bonusString, '|'); sep.addGroupingPair('[', ']'); sep.addGroupingPair('(', ')'); if ((bonusString.indexOf(Constants.PIPE) == bonusString .lastIndexOf(Constants.PIPE)) && bonusString.indexOf('%') < 0) { Logging.errorPrint("Illegal bonus format: " + bonusString); return null; } String bonusName = sep.next(); try { //Throw away old level value if present Integer.parseInt(bonusName); bonusName = sep.next().toUpperCase(); } catch (NumberFormatException exc) { bonusName = bonusName.toUpperCase(); } int equalOffset = -1; Class<? extends BonusObj> bEntry = TokenLibrary.getBonus(bonusName); String typeOfBonus = bonusName; if (bEntry == null) { equalOffset = bonusName.indexOf('='); if (equalOffset >= 0) { typeOfBonus = bonusName.substring(0, equalOffset + 1); bEntry = TokenLibrary.getBonus(typeOfBonus); } if (bEntry == null) { typeOfBonus = Bonus.BONUS_UNDEFINED; Logging.errorPrint("Unrecognized bonus: " + bonusString); return null; } } String bonusInfo = sep.next(); String bValue = "0"; if (sep.hasNext()) { bValue = sep.next(); } if (bValue.startsWith("PRE") || bValue.startsWith("!PRE")) { Logging.errorPrint("Invalid BONUS has no value: " + bonusString); return null; } bValue = bValue.toUpperCase(); BonusObj aBonus = null; try { aBonus = bEntry.newInstance(); } catch (Exception exc) { Logging.errorPrint("Could not create bonusObj for:" + bonusString); return null; } aBonus.putOriginalString(bonusString.intern()); aBonus.setBonusName(bonusName.intern()); aBonus.setTypeOfBonus(typeOfBonus.intern()); Formula val = aBonus.setValue(bValue.intern()); if (!val.isValid()) { Logging.errorPrint("Could not create bonusObj for:" + bonusString + " since Formula " + bValue + " is not valid: " + val.toString()); return null; } while (sep.hasNext()) { final String aString = sep.next().toUpperCase(); if (PreParserFactory.isPreReqString(aString)) { // Logging.errorPrint("Why is this not parsed in loading: " + // aString + " rather than in Bonus.newBonus()"); try { final PreParserFactory factory = PreParserFactory .getInstance(); aBonus.addPrerequisite(factory.parse(aString)); } catch (PersistenceLayerException ple) { Logging.errorPrint(ple.getMessage(), ple); Logging.reportSource(Logging.ERROR, context); return null; } } else if (aString.startsWith("TYPE=") || aString.startsWith("TYPE.")) { String bonusType = aString.substring(5); int dotLoc = bonusType.indexOf('.'); if (dotLoc != -1) { final String stackingFlag = bonusType.substring(dotLoc + 1); // TODO - Need to reset bonusType to exclude this but // there is too much dependancy on it being there // built into the code. if (stackingFlag.startsWith("REPLACE")) //$NON-NLS-1$ { aBonus.setStackingFlag(StackType.REPLACE); } else if (stackingFlag.startsWith("STACK")) //$NON-NLS-1$ { aBonus.setStackingFlag(StackType.STACK); } } final boolean result = aBonus.addType(bonusType.intern()); if (!result) { Logging.log(Logging.LST_ERROR, new StringBuilder().append( "Could not add type ").append(aString.substring(5)) .append(" to bonusType ").append(typeOfBonus) .append(" in Bonus.newBonus").toString()); Logging.reportSource(Logging.LST_ERROR, context); return null; } } } if (equalOffset >= 0) { aBonus.setVariable(bonusName.substring(equalOffset + 1).intern()); } if (!aBonus.requiresRealCaseTarget()) { bonusInfo = bonusInfo.toUpperCase(); } StringTokenizer aTok = new StringTokenizer(bonusInfo, ","); if (!aTok.hasMoreTokens()) { Logging.log(Logging.LST_ERROR, new StringBuilder().append( "Could not parse empty target ").append( " from BONUS:").append(bonusString).toString()); Logging.reportSource(Logging.LST_ERROR, context); return null; } LstUtils.deprecationCheck(aBonus, bonusName, bonusString); while (aTok.hasMoreTokens()) { final String token = aTok.nextToken(); final boolean result = aBonus.parseToken(context, token); if (!result) { Logging.log(Logging.LST_ERROR, new StringBuilder().append( "Could not parse token ").append(token).append( " from BONUS:").append(bonusString).toString()); Logging.reportSource(Logging.LST_ERROR, context); return null; } } return aBonus; } }