/*
* Copyright 2008 (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;
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.enumeration.AssociationKey;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.Nature;
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.CDOMPrimaryToken;
import pcgen.rules.persistence.token.DeferredToken;
import pcgen.rules.persistence.token.ParseResult;
public class VFeatLst extends AbstractTokenWithSeparator<CDOMObject> implements
CDOMPrimaryToken<CDOMObject>, DeferredToken<CDOMObject>
{
public static final Class<Ability> ABILITY_CLASS = Ability.class;
@Override
public String getTokenName()
{
return "VFEAT";
}
@Override
protected char separator()
{
return '|';
}
@Override
protected ParseResult parseTokenWithSeparator(LoadContext context,
CDOMObject obj, String value)
{
StringTokenizer tok = new StringTokenizer(value, Constants.PIPE);
String token = tok.nextToken();
if (looksLikeAPrerequisite(token))
{
return new ParseResult.Fail("Cannot have only PRExxx subtoken in "
+ getTokenName() + ": " + value, context);
}
ArrayList<PrereqObject> edgeList = new ArrayList<PrereqObject>();
boolean first = true;
boolean foundClear = false;
AbilityCategory category = AbilityCategory.FEAT;
Nature nature = Nature.VIRTUAL;
CDOMReference<AbilityList> list = Ability.FEATLIST;
ReferenceManufacturer<Ability> rm = context.getReferenceContext().getManufacturer(
ABILITY_CLASS, AbilityCategory.FEAT);
while (true)
{
if (token.equals(Constants.LST_DOT_CLEAR))
{
if (!first)
{
return new ParseResult.Fail(" Non-sensical " + getTokenName()
+ ": .CLEAR was not the first list item: " + value, context);
}
context.getListContext().removeAllFromList(getTokenName(), obj,
list);
context.getObjectContext().removeList(obj, ListKey.GVF_CHOOSE_ACTOR);
foundClear = true;
}
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(), category, ref, nature);
context.getObjectContext().addToList(obj,
ListKey.GVF_CHOOSE_ACTOR, ats);
edgeList.add(ats);
loadList = false;
}
}
}
if (loadList)
{
AssociatedPrereqObject assoc = context.getListContext()
.addToList(getTokenName(), obj, list, ability);
assoc.setAssociation(AssociationKey.NATURE, nature);
assoc.setAssociation(AssociationKey.CATEGORY, category);
if (choices != null)
{
assoc.setAssociation(AssociationKey.ASSOC_CHOICES, choices);
}
edgeList.add(assoc);
}
}
first = false;
if (!tok.hasMoreTokens())
{
// No prereqs, so we're done
return ParseResult.SUCCESS;
}
token = tok.nextToken();
if (looksLikeAPrerequisite(token))
{
break;
}
}
if (foundClear)
{
return new ParseResult.Fail(
"Cannot use PREREQs when using .CLEAR 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 " + getTokenName() + ":?)", 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)
{
AssociatedChanges<CDOMReference<Ability>> changes = context
.getListContext().getChangesInList(getTokenName(), obj,
Ability.FEATLIST);
MapToList<CDOMReference<Ability>, AssociatedPrereqObject> added = changes
.getAddedAssociations();
Collection<CDOMReference<Ability>> removedItems = changes.getRemoved();
List<String> returnList = new ArrayList<String>();
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())
{
context.addWriteMessage(getTokenName() + " does not support "
+ Constants.LST_DOT_CLEAR_DOT);
return null;
}
if (added != null && !added.isEmpty())
{
HashMapToList<List<Prerequisite>, CDOMReference<Ability>> m =
new HashMapToList<List<Prerequisite>, CDOMReference<Ability>>();
for (CDOMReference<Ability> ab : added.getKeySet())
{
for (AssociatedPrereqObject assoc : added.getListFor(ab))
{
m.addToListFor(assoc.getPrerequisiteList(), ab);
}
}
Set<String> returnSet = new TreeSet<String>();
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);
}
Changes<ChooseSelectionActor<?>> actors = context.getObjectContext()
.getListChanges(obj, ListKey.GVF_CHOOSE_ACTOR);
Collection<ChooseSelectionActor<?>> addedActors = actors.getAdded();
if (addedActors != null)
{
for (ChooseSelectionActor<?> cra : addedActors)
{
if (getTokenName().equals(cra.getSource()))
{
try
{
returnList.add(cra.getLstFormat());
}
catch (PersistenceLayerException e)
{
context.addWriteMessage(getTokenName()
+ " encountered error: " + e.getMessage());
return null;
}
}
}
}
if (returnList.isEmpty())
{
return null;
}
return returnList.toArray(new String[returnList.size()]);
}
@Override
public Class<CDOMObject> getTokenClass()
{
return CDOMObject.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, CDOMObject cdo)
{
cdo.addAllToListFor(ListKey.NEW_CHOOSE_ACTOR,
cdo.getListFor(ListKey.GVF_CHOOSE_ACTOR));
return true;
}
@Override
public Class<CDOMObject> getDeferredTokenClass()
{
return CDOMObject.class;
}
}