/* * 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.domain; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.StringTokenizer; import pcgen.base.util.MapToList; import pcgen.cdom.base.AssociatedPrereqObject; import pcgen.cdom.base.CDOMReference; import pcgen.cdom.base.ChooseSelectionActor; 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.core.Ability; import pcgen.core.AbilityCategory; import pcgen.core.AbilityUtilities; import pcgen.core.Domain; import pcgen.persistence.PersistenceLayerException; import pcgen.rules.context.AssociatedChanges; 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; /** * Deal with FEAT token */ public class FeatToken extends AbstractTokenWithSeparator<Domain> implements CDOMPrimaryToken<Domain>, DeferredToken<Domain> { 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, Domain obj, String value) { 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); } context.getListContext().removeAllFromList(getTokenName(), obj, Ability.FEATLIST); context.getObjectContext().removeList(obj, ListKey.DF_CHOOSE_ACTOR); } else { CDOMReference<Ability> ability = TokenUtilities.getTypeOrPrimitive(rm, token); if (ability == null) { return ParseResult.INTERNAL_ERROR; } ability.setRequiresTarget(true); 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.DF_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); } } } first = false; } return ParseResult.SUCCESS; } @Override public String[] unparse(LoadContext context, Domain domain) { AssociatedChanges<CDOMReference<Ability>> changes = context .getListContext().getChangesInList(getTokenName(), domain, Ability.FEATLIST); Changes<ChooseSelectionActor<?>> actors = context.getObjectContext() .getListChanges(domain, ListKey.DF_CHOOSE_ACTOR); MapToList<CDOMReference<Ability>, AssociatedPrereqObject> added = changes .getAddedAssociations(); Collection<CDOMReference<Ability>> removedItems = changes.getRemoved(); StringBuilder sb = new StringBuilder(); if (changes.includesGlobalClear()) { if (removedItems != null && !removedItems.isEmpty()) { context.addWriteMessage("Non-sensical relationship in " + getTokenName() + ": global .CLEAR and local .CLEAR. performed"); return null; } sb.append(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()) { boolean needsPipe = sb.length() != 0; for (CDOMReference<Ability> ref : added.getKeySet()) { String lstFormat = ref.getLSTformat(false); for (int i = 0; i < added.sizeOfListFor(ref); i++) { if (needsPipe) { sb.append(Constants.PIPE); } needsPipe = true; sb.append(lstFormat); } } } Collection<ChooseSelectionActor<?>> addedActors = actors.getAdded(); if (addedActors != null) { for (ChooseSelectionActor<?> cra : addedActors) { if (getTokenName().equals(cra.getSource())) { try { sb.append(cra.getLstFormat()); } catch (PersistenceLayerException e) { context.addWriteMessage(getTokenName() + " encountered error: " + e.getMessage()); return null; } } } } if (sb.length() == 0) { return null; } return new String[] { sb.toString() }; } @Override public Class<Domain> getTokenClass() { return Domain.class; } /* * This is a DeferredToken because attempting to extract "self" out of the * "generic" (widely shared) CHOOSE_ACTOR list is extremely difficult since * the item added is not this token but a derivative object whose reference * is not saved by this token. Therefore a unique list is used to store the * CHOOSE_ACTORs generated by this token and they are added into the * "global" list when load is complete - thpr Dec 15, 2012 */ @Override public boolean process(LoadContext context, Domain domain) { domain.addAllToListFor(ListKey.NEW_CHOOSE_ACTOR, domain.getListFor(ListKey.DF_CHOOSE_ACTOR)); return true; } @Override public Class<Domain> getDeferredTokenClass() { return Domain.class; } }