/*
* Copyright 2008 (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.rules.persistence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import pcgen.base.util.CaseInsensitiveMap;
import pcgen.base.util.DoubleKeyMapToList;
import pcgen.base.util.TripleKeyMapToList;
import pcgen.base.util.WeightedCollection;
import pcgen.cdom.base.GroupDefinition;
import pcgen.cdom.base.Loadable;
import pcgen.persistence.PersistenceLayerException;
import pcgen.rules.context.LoadContext;
import pcgen.rules.persistence.TokenLibrary.SubTokenIterator;
import pcgen.rules.persistence.TokenLibrary.TokenIterator;
import pcgen.rules.persistence.token.CDOMPrimaryToken;
import pcgen.rules.persistence.token.CDOMSecondaryToken;
import pcgen.rules.persistence.token.CDOMSubToken;
import pcgen.rules.persistence.token.CDOMToken;
import pcgen.rules.persistence.token.ComplexParseResult;
import pcgen.rules.persistence.token.DeferredToken;
import pcgen.rules.persistence.token.ParseResult;
import pcgen.rules.persistence.util.Revision;
import pcgen.rules.persistence.util.TokenFamily;
import pcgen.rules.persistence.util.TokenFamilyIterator;
import pcgen.rules.persistence.util.TokenFamilySubIterator;
import pcgen.util.Logging;
public class TokenSupport
{
private TokenFamily localTokens = new TokenFamily(new Revision(0, 0, 0));
private DoubleKeyMapToList<Class<?>, String, CDOMToken<?>> tokenCache =
new DoubleKeyMapToList<>();
private TripleKeyMapToList<Class<?>, String, String, CDOMToken<?>> subTokenCache =
new TripleKeyMapToList<>(HashMap.class, CaseInsensitiveMap.class, CaseInsensitiveMap.class);
public <T extends Loadable> boolean processToken(LoadContext context,
T derivative, String typeStr, String argument)
throws PersistenceLayerException
{
Class<T> cl = (Class<T>) derivative.getClass();
List<? extends CDOMToken<T>> tokenList = getTokens(cl, typeStr);
if (tokenList != null)
{
for (CDOMToken<T> token : tokenList)
{
ParseResult parse;
try
{
parse = token.parseToken(context, derivative, argument);
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
Logging.addParseMessage(
Logging.LST_ERROR,
"Token generated an IllegalArgumentException: "
+ e.getLocalizedMessage());
parse = new ParseResult.Fail("Token processing failed");
}
// Need to add messages as there may be warnings.
parse.addMessagesToLog();
if (parse.passed())
{
return true;
}
if (Logging.isLoggable(Logging.LST_INFO))
{
Logging.addParseMessage(Logging.LST_INFO,
"Failed in parsing typeStr: " + typeStr + " " + argument);
}
}
}
if (typeStr.startsWith(" "))
{
Logging.addParseMessage(Logging.LST_ERROR, "Illegal whitespace at start of token '" + typeStr
+ "' '" + argument + "' for " + cl.getName() + " "
+ derivative.getDisplayName() + " in " + context.getSourceURI());
}
else
{
Logging.addParseMessage(Logging.LST_ERROR, "Illegal Token '" + typeStr
+ "' '" + argument + "' for " + cl.getName() + " "
+ derivative.getDisplayName() + " in " + context.getSourceURI());
}
return false;
}
private <T extends Loadable> List<? extends CDOMToken<T>> getTokens(Class<T> cl,
String name)
{
List list = tokenCache.getListFor(cl, name);
if (list == null)
{
CDOMToken<?> local = localTokens.getToken(cl, name);
if (local != null)
{
tokenCache.addToListFor(cl, name, local);
}
for (Iterator<? extends CDOMToken<T>> it =
new TokenIterator<>(cl, name); it.hasNext();)
{
CDOMToken<T> token = it.next();
tokenCache.addToListFor(cl, name, token);
}
list = tokenCache.getListFor(cl, name);
}
return list;
}
private <T> List<? extends CDOMToken<T>> getTokens(
Class<T> cl, String name, String subtoken)
{
List list = subTokenCache.getListFor(cl, name, subtoken);
if (list == null)
{
CDOMToken<?> local = localTokens.getSubToken(cl, name, subtoken);
if (local != null)
{
subTokenCache.addToListFor(cl, name, subtoken, local);
}
for (Iterator<CDOMSubToken<T>> it =
new SubTokenIterator<>(cl, name, subtoken); it
.hasNext();)
{
CDOMToken<T> token = it.next();
subTokenCache.addToListFor(cl, name, subtoken, token);
}
list = subTokenCache.getListFor(cl, name, subtoken);
}
return list;
}
public <T> ParseResult processSubToken(LoadContext context, T cdo,
String tokenName, String key, String value)
{
ComplexParseResult cpr = new ComplexParseResult();
List<? extends CDOMToken<T>> tokenList = getTokens((Class<T>) cdo.getClass(), tokenName, key);
if (tokenList != null)
{
for (CDOMToken<T> token : tokenList)
{
ParseResult pr = token.parseToken(context, cdo, value);
if (pr.passed())
{
return pr;
}
cpr.copyMessages(pr);
cpr.addErrorMessage("Failed in parsing subtoken: " + key + " of " + value);
}
}
/*
* CONSIDER Better option than toString, given that T != CDOMObject
*/
cpr.addErrorMessage("Illegal " + tokenName
+ " subtoken '" + key + "' '" + value + "' for " + cdo.toString());
return cpr;
}
public <T> String[] unparseSubtoken(LoadContext context, T cdo, String tokenName)
{
char separator = tokenName.charAt(0) == '*' ? ':' : '|';
Collection<String> set = new WeightedCollection<>(
String.CASE_INSENSITIVE_ORDER);
Class<T> cl = (Class<T>) cdo.getClass();
TokenFamilySubIterator<T> it =
new TokenFamilySubIterator<>(cl, tokenName);
while (it.hasNext())
{
CDOMSecondaryToken<? super T> token = it.next();
String[] s = token.unparse(context, cdo);
if (s != null)
{
for (String aString : s)
{
set.add(token.getTokenName() + separator + aString);
}
}
}
Set<CDOMSecondaryToken<? super T>> local = localTokens.getSubTokens(cl, tokenName);
for (CDOMSecondaryToken<? super T> token : local)
{
String[] s = token.unparse(context, cdo);
if (s != null)
{
for (String aString : s)
{
set.add(token.getTokenName() + separator + aString);
}
}
}
if (set.isEmpty())
{
return null;
}
return set.toArray(new String[set.size()]);
}
public <T> Collection<String> unparse(LoadContext context, T cdo)
{
Collection<String> set = new WeightedCollection<>(
String.CASE_INSENSITIVE_ORDER);
Class<T> cl = (Class<T>) cdo.getClass();
TokenFamilyIterator<T> it = new TokenFamilyIterator<>(cl);
while (it.hasNext())
{
CDOMPrimaryToken<? super T> token = it.next();
String[] s = token.unparse(context, cdo);
if (s != null)
{
for (String aString : s)
{
set.add(token.getTokenName() + ':' + aString);
}
}
}
if (set.isEmpty())
{
return null;
}
return set;
}
/**
* Produce the LST code for any occurrences of the token. An attempt to
* unparse an invalid or non-existent token will result in an
* IllegalArgumentError.
*
* @param loadContext The load context to be used
* @param cdo The object to be partially unparsed
* @param tokenName The name of the token to be extracted, must be a primary token.
* @param <T> The type of object to be processed, generally a CDOMObject.
* @return An array of LST code 'fields' being each occurrence of the token for the target object.
*/
public <T> String[] unparseToken(LoadContext loadContext, T cdo, String tokenName)
{
Class<? super T> cl = (Class<T>) cdo.getClass();
CDOMToken<?> token = null;
while (token == null)
{
token = TokenFamily.CURRENT.getToken(cl, tokenName);
if (token == null)
{
if (Object.class.equals(cl))
{
return null;
}
cl = cl.getSuperclass();
}
}
List<String> result = new ArrayList<>();
if (CDOMPrimaryToken.class.isAssignableFrom(token.getClass()))
{
@SuppressWarnings("unchecked")
CDOMPrimaryToken<? super T> primaryToken =
(CDOMPrimaryToken<? super T>) token;
String[] s = primaryToken.unparse(loadContext, cdo);
if (s != null)
{
for (String aString : s)
{
result.add(token.getTokenName() + ':' + aString);
}
}
}
else
{
/*
* This is catching any compatibility tokens that were (incorrectly)
* placed into CURRENT
*/
throw new IllegalArgumentException(
"Expected a primary token in unparseToken, but " + tokenName
+ " - " + token.getClass().getName() + " is not a CDOMPrimaryToken.");
}
if (result.isEmpty())
{
return null;
}
return result.toArray(new String[]{});
}
public Collection<DeferredToken<? extends Loadable>> getDeferredTokens()
{
List<DeferredToken<? extends Loadable>> c =
new ArrayList<>();
c.addAll(localTokens.getDeferredTokens());
c.addAll(TokenFamily.CURRENT.getDeferredTokens());
return c;
}
public void loadLocalToken(Object token)
{
TokenLibrary.loadFamily(localTokens, token);
}
public <T> GroupDefinition<T> getGroup(Class<T> cl, String s)
{
return localTokens.getGroup(cl, s);
}
}