/* * Copyright 2007 (C) Thomas Parker <thpr@users.sourceforge.net> * * 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 */ package plugin.lsttokens.auto; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; import pcgen.base.util.HashMapToList; import pcgen.base.util.MapToList; import pcgen.cdom.base.AssociatedPrereqObject; import pcgen.cdom.base.CDOMObject; import pcgen.cdom.base.CDOMReference; import pcgen.cdom.base.ChooseSelectionActor; import pcgen.cdom.base.Constants; import pcgen.cdom.base.PrereqObject; import pcgen.cdom.content.AbilitySelection; import pcgen.cdom.content.ConditionalSelectionActor; import pcgen.cdom.enumeration.AssociationKey; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.Nature; import pcgen.cdom.helper.AbilitySelector; import pcgen.cdom.helper.AbilityTargetSelector; import pcgen.cdom.list.AbilityList; 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.prereq.Prerequisite; 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.CDOMSecondaryToken; import pcgen.rules.persistence.token.ParseResult; public class FeatToken extends AbstractTokenWithSeparator<CDOMObject> implements CDOMSecondaryToken<CDOMObject> { private static final Class<Ability> ABILITY_CLASS = Ability.class; /** * We use a specific source string so we can split our choice actor entries * from other FEAT tag entries. */ private static final String SOURCE = "AUTO:FEAT"; @Override public String getParentToken() { return "AUTO"; } private String getFullName() { return getParentToken() + Constants.COLON + getTokenName(); } @Override public String getTokenName() { return "FEAT"; } @Override protected char separator() { return '|'; } @Override protected ParseResult parseTokenWithSeparator(LoadContext context, CDOMObject obj, String value) { AbilityCategory category = AbilityCategory.FEAT; Nature nature = Nature.AUTOMATIC; StringTokenizer tok = new StringTokenizer(value, Constants.PIPE); String token = tok.nextToken(); if (looksLikeAPrerequisite(token)) { return new ParseResult.Fail("Cannot have only PRExxx subtoken in " + getFullName() + ": " + value, context); } ArrayList<PrereqObject> edgeList = new ArrayList<PrereqObject>(); CDOMReference<AbilityList> abilList = AbilityList .getAbilityListReference(category, nature); boolean first = true; boolean allowPre = true; ReferenceManufacturer<Ability> rm = context.getReferenceContext().getManufacturer( ABILITY_CLASS, AbilityCategory.FEAT); while (true) { if (Constants.LST_DOT_CLEAR.equals(token)) { if (!first) { return new ParseResult.Fail(" Non-sensical " + getFullName() + ": .CLEAR was not the first list item: " + value, context); } context.getListContext().removeAllFromList(getFullName(), obj, abilList); allowPre = false; } else if (token.startsWith(Constants.LST_DOT_CLEAR_DOT)) { String clearText = token.substring(7); CDOMReference<Ability> ref = TokenUtilities.getTypeOrPrimitive(rm, clearText); if (ref == null) { return ParseResult.INTERNAL_ERROR; } context.getListContext().removeFromList(getFullName(), obj, abilList, ref); allowPre = false; } else if (Constants.LST_PERCENT_LIST.equals(token)) { ConditionalSelectionActor<AbilitySelection> cca = new ConditionalSelectionActor<AbilitySelection>( new AbilitySelector(SOURCE, AbilityCategory.FEAT, Nature.AUTOMATIC)); edgeList.add(cca); context.getObjectContext().addToList(obj, ListKey.NEW_CHOOSE_ACTOR, cca); allowPre = false; } 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( SOURCE, category, ref, nature); context.getObjectContext().addToList(obj, ListKey.NEW_CHOOSE_ACTOR, ats); edgeList.add(ats); loadList = false; } } } if (loadList) { AssociatedPrereqObject assoc = context.getListContext() .addToList(getFullName(), obj, abilList, ability); assoc.setAssociation(AssociationKey.NATURE, nature); assoc.setAssociation(AssociationKey.CATEGORY, category); if (choices != null) { assoc.setAssociation(AssociationKey.ASSOC_CHOICES, choices); } edgeList.add(assoc); } } if (!tok.hasMoreTokens()) { // No prereqs, so we're done return ParseResult.SUCCESS; } first = false; token = tok.nextToken(); if (looksLikeAPrerequisite(token)) { break; } } if (!allowPre) { return new ParseResult.Fail( "Cannot use PREREQs when using .CLEAR, .CLEAR., or %LIST in " + getTokenName(), context); } while (true) { Prerequisite prereq = getPrerequisite(token); if (prereq == null) { return new ParseResult.Fail(" (Did you put feats after the " + "PRExxx tags in " + getFullName() + ":?)", context); } for (PrereqObject edge : edgeList) { edge.addPrerequisite(prereq); } if (!tok.hasMoreTokens()) { break; } token = tok.nextToken(); } return ParseResult.SUCCESS; } @Override public String[] unparse(LoadContext context, CDOMObject obj) { Set<String> returnSet = new TreeSet<String>(); List<String> returnList = new ArrayList<String>(); MapToList<List<Prerequisite>, CDOMReference<Ability>> m = new HashMapToList<List<Prerequisite>, CDOMReference<Ability>>(); AbilityCategory category = AbilityCategory.FEAT; Nature nature = Nature.AUTOMATIC; CDOMReference<AbilityList> abilList = AbilityList .getAbilityListReference(category, nature); AssociatedChanges<CDOMReference<Ability>> changes = context .getListContext() .getChangesInList(getFullName(), obj, abilList); Collection<CDOMReference<Ability>> removedItems = changes.getRemoved(); if (changes.includesGlobalClear()) { if (removedItems != null && !removedItems.isEmpty()) { context.addWriteMessage("Non-sensical relationship in " + getTokenName() + ": global .CLEAR and local .CLEAR. performed"); return null; } returnList.add(Constants.LST_DOT_CLEAR); } else if (removedItems != null && !removedItems.isEmpty()) { returnList.add(Constants.LST_DOT_CLEAR_DOT + ReferenceUtilities.joinLstFormat(removedItems, "|.CLEAR.", true)); } Changes<ChooseSelectionActor<?>> listChanges = context.getObjectContext().getListChanges(obj, ListKey.NEW_CHOOSE_ACTOR); Collection<ChooseSelectionActor<?>> listAdded = listChanges.getAdded(); if (listAdded != null && !listAdded.isEmpty()) { for (ChooseSelectionActor<?> csa : listAdded) { if (csa.getSource().equals(SOURCE)) { try { returnList.add(csa.getLstFormat()); } catch (PersistenceLayerException e) { context.addWriteMessage(getTokenName() + " encountered error: " + e.getMessage()); return null; } } } } MapToList<CDOMReference<Ability>, AssociatedPrereqObject> mtl = changes .getAddedAssociations(); if (mtl != null) { for (CDOMReference<Ability> ab : mtl.getKeySet()) { for (AssociatedPrereqObject assoc : mtl.getListFor(ab)) { m.addToListFor(assoc.getPrerequisiteList(), ab); } } } for (List<Prerequisite> prereqs : m.getKeySet()) { StringBuilder sb = new StringBuilder(); sb.append(ReferenceUtilities.joinLstFormat(m.getListFor(prereqs), Constants.PIPE)); if (prereqs != null && !prereqs.isEmpty()) { sb.append(Constants.PIPE); sb.append(getPrerequisiteString(context, prereqs)); } returnSet.add(sb.toString()); } returnList.addAll(returnSet); return returnList.toArray(new String[returnList.size()]); } @Override public Class<CDOMObject> getTokenClass() { return CDOMObject.class; } }