/* * Copyright (c) 2008 Tom Parker <thpr@users.sourceforge.net> * * This program 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 program 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package plugin.lsttokens.race; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.StringTokenizer; import pcgen.cdom.base.AssociatedPrereqObject; import pcgen.cdom.base.CDOMReference; import pcgen.cdom.base.Constants; import pcgen.cdom.enumeration.AssociationKey; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.Nature; import pcgen.cdom.helper.AbilityTargetSelector; import pcgen.cdom.reference.CDOMSingleRef; import pcgen.cdom.reference.ReferenceManufacturer; import pcgen.cdom.reference.ReferenceUtilities; import pcgen.core.Ability; import pcgen.core.AbilityCategory; import pcgen.core.AbilityUtilities; import pcgen.core.Race; import pcgen.rules.context.Changes; import pcgen.rules.context.LoadContext; import pcgen.rules.persistence.TokenUtilities; import pcgen.rules.persistence.token.AbstractTokenWithSeparator; import pcgen.rules.persistence.token.CDOMPrimaryToken; import pcgen.rules.persistence.token.DeferredToken; import pcgen.rules.persistence.token.ParseResult; /** * Class deals with FEAT Token */ public class FeatToken extends AbstractTokenWithSeparator<Race> implements CDOMPrimaryToken<Race>, DeferredToken<Race> { public static final Class<Ability> ABILITY_CLASS = Ability.class; @Override public String getTokenName() { return "FEAT"; } @Override protected char separator() { return '|'; } @Override protected ParseResult parseTokenWithSeparator(LoadContext context, Race race, String value) { context.getObjectContext().removeList(race, ListKey.FEAT_TOKEN_LIST); StringTokenizer tok = new StringTokenizer(value, Constants.PIPE); boolean first = true; ReferenceManufacturer<Ability> rm = context.getReferenceContext().getManufacturer( ABILITY_CLASS, AbilityCategory.FEAT); while (tok.hasMoreTokens()) { String token = tok.nextToken(); if (Constants.LST_DOT_CLEAR.equals(token)) { if (!first) { return new ParseResult.Fail(" Non-sensical " + getTokenName() + ": .CLEAR was not the first list item: " + value, context); } } else { CDOMReference<Ability> ability = TokenUtilities.getTypeOrPrimitive(rm, token); if (ability == null) { return ParseResult.INTERNAL_ERROR; } ability.setRequiresTarget(true); context.getObjectContext().addToList(race, ListKey.FEAT_TOKEN_LIST, ability); } first = false; } return ParseResult.SUCCESS; } @Override public String[] unparse(LoadContext context, Race race) { Changes<CDOMReference<Ability>> changes = context.getObjectContext() .getListChanges(race, ListKey.FEAT_TOKEN_LIST); Collection<CDOMReference<Ability>> added = changes.getAdded(); Collection<CDOMReference<Ability>> removedItems = changes.getRemoved(); String returnVal = null; if (changes.includesGlobalClear()) { if (removedItems != null && !removedItems.isEmpty()) { context.addWriteMessage("Non-sensical relationship in " + getTokenName() + ": global .CLEAR and local .CLEAR. performed"); return null; } returnVal = Constants.LST_DOT_CLEAR; } else if (removedItems != null && !removedItems.isEmpty()) { context.addWriteMessage(getTokenName() + " does not support " + Constants.LST_DOT_CLEAR_DOT); return null; } if (added != null && !added.isEmpty()) { returnVal = ReferenceUtilities.joinLstFormat(added, Constants.PIPE); } if (returnVal == null) { return null; } return new String[] { returnVal }; } @Override public Class<Race> getTokenClass() { return Race.class; } @Override public Class<Race> getDeferredTokenClass() { return Race.class; } /* * Explanation of: Why is this a deferred token? * * The OVERWRITE behavior of this token changes behavior in such a way that * the processing below needs to be done *after* processing. It makes the * OVERWRITE and .CLEAR processing significantly easier, in that it only has * to worry about one list, and not clearing in two locations. It's also an * issue because the ListContext list used below is a shared list with other * tokens, so the overwrite needs to be isolated to this token, so it needs * to be separate. * * As a result of this, the unparse gets a bit counter-intuitive in that no * special processing on %LIST needs to be done, so this may look * inconsistent to other Ability/Feat based tokens * * -thpr Dec 15, 2012 */ @Override public boolean process(LoadContext context, Race obj) { for (CDOMReference<Ability> ability : obj .getSafeListFor(ListKey.FEAT_TOKEN_LIST)) { String token = ability.getLSTformat(false); boolean loadList = true; List<String> choices = null; if (token.indexOf('(') != -1) { choices = new ArrayList<String>(); AbilityUtilities.getUndecoratedName(token, choices); if (choices.size() == 1) { if (Constants.LST_PERCENT_LIST.equals(choices.get(0)) && (ability instanceof CDOMSingleRef)) { CDOMSingleRef<Ability> ref = (CDOMSingleRef<Ability>) ability; AbilityTargetSelector ats = new AbilityTargetSelector( getTokenName(), AbilityCategory.FEAT, ref, Nature.AUTOMATIC); context.getObjectContext().addToList(obj, ListKey.NEW_CHOOSE_ACTOR, ats); loadList = false; } } } if (loadList) { AssociatedPrereqObject assoc = context.getListContext() .addToList(getTokenName(), obj, Ability.FEATLIST, ability); assoc.setAssociation(AssociationKey.NATURE, Nature.AUTOMATIC); assoc.setAssociation(AssociationKey.CATEGORY, AbilityCategory.FEAT); if (choices != null) { assoc.setAssociation(AssociationKey.ASSOC_CHOICES, choices); } } } return true; } }