/*
* 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.pcclass;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import pcgen.base.lang.StringUtil;
import pcgen.base.util.TreeMapToList;
import pcgen.cdom.base.CDOMReference;
import pcgen.cdom.base.Constants;
import pcgen.cdom.content.KnownSpellIdentifier;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.reference.ReferenceUtilities;
import pcgen.core.PCClass;
import pcgen.core.spell.Spell;
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.ComplexParseResult;
import pcgen.rules.persistence.token.ParseResult;
/**
* Class deals with KNOWNSPELLS Token
*/
public class KnownspellsToken extends AbstractTokenWithSeparator<PCClass>
implements CDOMPrimaryToken<PCClass>
{
private static final Class<Spell> SPELL_CLASS = Spell.class;
@Override
public String getTokenName()
{
return "KNOWNSPELLS";
}
@Override
protected char separator()
{
return '|';
}
@Override
protected ParseResult parseTokenWithSeparator(LoadContext context,
PCClass pcc, String value)
{
StringTokenizer pipeTok = new StringTokenizer(value, Constants.PIPE);
boolean firstToken = true;
while (pipeTok.hasMoreTokens())
{
String totalFilter = pipeTok.nextToken();
if (Constants.LST_DOT_CLEAR_ALL.equals(totalFilter))
{
if (!firstToken)
{
return new ParseResult.Fail("Non-sensical situation was "
+ "encountered while parsing " + getTokenName()
+ ": When used, .CLEARALL must be the first argument", context);
}
context.getObjectContext()
.removeList(pcc, ListKey.KNOWN_SPELLS);
continue;
}
ParseResult pr = checkForIllegalSeparator(',', totalFilter);
if (!pr.passed())
{
return pr;
}
StringTokenizer commaTok = new StringTokenizer(totalFilter,
Constants.COMMA);
/*
* This is a rather interesting situation - this takes items that
* are ALLOWED and converts them to GRANTS. Therefore, this must be
* done as a post-manufacturing run on the Graph.
*
* As there is no guarantee when the factory is added that the list
* is complete, this resolution of known MUST be performed as a
* query against the PC, not stored in the graph as Grants edges.
*/
// must satisfy all elements in a comma delimited list
Integer levelLim = null;
CDOMReference<Spell> sp = null;
while (commaTok.hasMoreTokens())
{
String filterString = commaTok.nextToken();
if (filterString.startsWith("LEVEL="))
{
if (levelLim != null)
{
return new ParseResult.Fail(
"Cannot have more than one Level limit in " + getTokenName()
+ ": " + value, context);
}
// if the argument starts with LEVEL=, compare the level to
// the desired spellLevel
try
{
levelLim = Integer.valueOf(filterString.substring(6));
if (levelLim < 0)
{
ComplexParseResult cpr = new ComplexParseResult();
cpr.addErrorMessage("Invalid Number in "
+ getTokenName() + ": " + value);
cpr.addErrorMessage(" Level must be >= 0");
return cpr;
}
}
catch (NumberFormatException e)
{
ComplexParseResult cpr = new ComplexParseResult();
cpr.addErrorMessage("Invalid Number in "
+ getTokenName() + ": " + value);
cpr.addErrorMessage(" Level must be "
+ "a non-negative integer");
return cpr;
}
}
else
{
if (sp != null)
{
return new ParseResult.Fail(
"Cannot have more than one Type/Spell limit in "
+ getTokenName() + ": " + value, context);
}
sp = TokenUtilities.getTypeOrPrimitive(context,
SPELL_CLASS, filterString);
if (sp == null)
{
return new ParseResult.Fail(" encountered Invalid limit in "
+ getTokenName() + ": " + value, context);
}
}
firstToken = false;
}
if (sp == null)
{
/*
* There is no need to check for an invalid construction here
* (meaning levelLim is null as well) as that was implicitly
* checked by ensuring || did not occur.
*/
sp = context.getReferenceContext().getCDOMAllReference(SPELL_CLASS);
}
KnownSpellIdentifier ksi = new KnownSpellIdentifier(sp, levelLim);
context.getObjectContext()
.addToList(pcc, ListKey.KNOWN_SPELLS, ksi);
}
return ParseResult.SUCCESS;
}
@Override
public String[] unparse(LoadContext context, PCClass pcc)
{
Changes<KnownSpellIdentifier> changes = context.getObjectContext()
.getListChanges(pcc, ListKey.KNOWN_SPELLS);
List<String> list = new ArrayList<>();
if (changes.includesGlobalClear())
{
list.add(Constants.LST_DOT_CLEAR_ALL);
}
Collection<KnownSpellIdentifier> removedItems = changes.getRemoved();
if (removedItems != null && !removedItems.isEmpty())
{
context.addWriteMessage(getTokenName()
+ " does not support .CLEAR.");
return null;
}
Collection<KnownSpellIdentifier> added = changes.getAdded();
if (added != null && !added.isEmpty())
{
TreeMapToList<CDOMReference<?>, Integer> map = new TreeMapToList<>(
ReferenceUtilities.REFERENCE_SORTER);
for (KnownSpellIdentifier ksi : added)
{
CDOMReference<Spell> ref = ksi.getSpellReference();
Integer i = ksi.getSpellLevel();
map.addToListFor(ref, i);
}
for (CDOMReference<?> ref : map.getKeySet())
{
for (Integer lvl : map.getListFor(ref))
{
StringBuilder sb = new StringBuilder();
boolean needComma = false;
String refString = ref.getLSTformat(false);
if (!Constants.LST_ALL.equals(refString))
{
sb.append(refString);
needComma = true;
}
if (lvl != null)
{
if (needComma)
{
sb.append(',');
}
sb.append("LEVEL=").append(lvl);
}
list.add(sb.toString());
}
}
}
if (list.isEmpty())
{
return null;
}
return new String[] { StringUtil.join(list, Constants.PIPE) };
}
@Override
public Class<PCClass> getTokenClass()
{
return PCClass.class;
}
}