/*
* Copyright 2014 (C) Tom 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 pcgen.cdom.content.factset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import pcgen.base.util.FormatManager;
import pcgen.base.util.Indirect;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.base.Constants;
import pcgen.cdom.enumeration.FactSetKey;
import pcgen.rules.context.AbstractObjectContext;
import pcgen.rules.context.Changes;
import pcgen.rules.context.LoadContext;
import pcgen.rules.persistence.token.AbstractTokenWithSeparator;
import pcgen.rules.persistence.token.CDOMSecondaryToken;
import pcgen.rules.persistence.token.ParseResult;
/**
* A FactSetParser is a dynamically built subtoken created when a FACTSET: is
* defined
*
* @param <T>
* The type of of object upon which the FactSetParser can be used
* @param <F>
* The format of the data stored in the FactSet
*/
public class FactSetParser<T extends CDOMObject, F> extends
AbstractTokenWithSeparator<T> implements CDOMSecondaryToken<T>
{
/**
* The underlying FactSetInfo indicating static information about the Fact
* for which this FactSetParser can parse the LST information
*/
private final FactSetInfo<T, F> def;
/**
* Constructs a new FactSetParser with the given FactSetInfo.
*
* @param fsi
* The FactSetInfo underlying this FactSetParser
* @throws IllegalArgumentException
* if the given FactSetInfo is null
*/
public FactSetParser(FactSetInfo<T, F> fsi)
{
if (fsi == null)
{
throw new IllegalArgumentException("FactSet Info cannot be null");
}
def = fsi;
}
@Override
protected char separator()
{
return '|';
}
@Override
protected ParseResult parseTokenWithSeparator(LoadContext context, T obj,
String value)
{
FormatManager<F> fmtManager = def.getFormatManager();
FactSetKey<F> fsk = def.getFactSetKey();
StringTokenizer st = new StringTokenizer(value, Constants.PIPE);
boolean firstToken = true;
AbstractObjectContext objContext = context.getObjectContext();
while (st.hasMoreTokens())
{
String token = st.nextToken();
if (Constants.LST_DOT_CLEAR_ALL.equals(token))
{
if (!firstToken)
{
return new ParseResult.Fail("Non-sensical situation was "
+ "encountered while parsing " + getParentToken()
+ Constants.PIPE + getTokenName()
+ ": When used, .CLEARALL must be the first argument",
context);
}
objContext.removeSet(obj, fsk);
}
Indirect<F> indirect = fmtManager.convertIndirect(token);
objContext.addToSet(obj, fsk, indirect);
}
return ParseResult.SUCCESS;
}
@Override
public Class<T> getTokenClass()
{
return def.getUsableLocation();
}
@Override
public String getTokenName()
{
return def.getFactSetName();
}
@Override
public String getParentToken()
{
return "FACTSET";
}
@Override
public String[] unparse(LoadContext context, T obj)
{
FactSetKey<F> fk = def.getFactSetKey();
Changes<Indirect<F>> changes =
context.getObjectContext().getSetChanges(obj, fk);
Collection<Indirect<F>> removedItems = changes.getRemoved();
List<String> results = new ArrayList<>(2);
if (changes.includesGlobalClear())
{
results.add(Constants.LST_DOT_CLEAR_ALL);
}
if (removedItems != null && !removedItems.isEmpty())
{
context.addWriteMessage(getTokenName() + " does not support "
+ Constants.LST_DOT_CLEAR_DOT);
return null;
}
Collection<Indirect<F>> added = changes.getAdded();
if (added != null && !added.isEmpty())
{
StringBuilder sb = new StringBuilder();
boolean needsPipe = false;
for (Indirect<F> indirect : added)
{
if (needsPipe)
{
sb.append(Constants.PIPE);
}
sb.append(indirect.getUnconverted());
needsPipe = true;
}
results.add(sb.toString());
}
return results.toArray(new String[results.size()]);
}
}