/*
* 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.Iterator;
import java.util.List;
import pcgen.base.text.ParsingSeparator;
import pcgen.base.util.ObjectContainer;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.base.Constants;
import pcgen.cdom.base.GroupDefinition;
import pcgen.cdom.base.Loadable;
import pcgen.cdom.base.PrimitiveCollection;
import pcgen.cdom.primitive.CompoundAndPrimitive;
import pcgen.cdom.primitive.CompoundOrPrimitive;
import pcgen.cdom.primitive.NegatingPrimitive;
import pcgen.cdom.primitive.ObjectContainerPrimitive;
import pcgen.cdom.reference.CDOMGroupRef;
import pcgen.cdom.reference.PatternMatchingReference;
import pcgen.cdom.reference.SelectionCreator;
import pcgen.rules.context.LoadContext;
import pcgen.rules.persistence.TokenLibrary.QualifierTokenIterator;
import pcgen.rules.persistence.token.PrimitiveToken;
import pcgen.rules.persistence.token.QualifierToken;
import pcgen.util.Logging;
public final class ChoiceSetLoadUtilities
{
private static final String FOUND_ERR_IN_QUAL_CHOICE = "Found error in Qualifier Choice: ";
private static final String FOUND_ERR_IN_PRIM_CHOICE = "Found error in Primitive Choice: ";
private ChoiceSetLoadUtilities()
{
//Don't instantiate utility class
}
public static <T extends CDOMObject> PrimitiveCollection<T> getChoiceSet(
LoadContext context, SelectionCreator<T> sc, String joinedOr)
{
List<PrimitiveCollection<T>> orList = new ArrayList<>();
ParsingSeparator pipe = new ParsingSeparator(joinedOr, '|');
pipe.addGroupingPair('[', ']');
pipe.addGroupingPair('(', ')');
for (; pipe.hasNext();)
{
String joinedAnd = pipe.next();
if (hasIllegalSeparator(',', joinedAnd))
{
return null;
}
List<PrimitiveCollection<T>> andList = new ArrayList<>();
ParsingSeparator comma = new ParsingSeparator(joinedAnd, ',');
comma.addGroupingPair('[', ']');
comma.addGroupingPair('(', ')');
for (; comma.hasNext();)
{
String primitive = comma.next();
if (primitive == null || primitive.isEmpty())
{
Logging.addParseMessage(Logging.LST_ERROR,
"Choice argument was null or empty: " + primitive);
return null;
}
QualifierToken<T> qual = getQualifier(context, sc,
primitive);
if (qual == null)
{
PrimitiveCollection<T> pcf = getSimplePrimitive(context,
sc, primitive);
if (pcf == null)
{
Logging.addParseMessage(Logging.LST_ERROR,
"Choice argument was not valid: " + primitive);
return null;
}
else
{
andList.add(pcf);
}
}
else
{
andList.add(qual);
}
}
if (!andList.isEmpty())
{
if (andList.size() == 1)
{
orList.add(andList.get(0));
}
else
{
orList.add(new CompoundAndPrimitive<>(andList));
}
}
}
if (orList.isEmpty())
{
return null;
}
else if (orList.size() == 1)
{
return orList.get(0);
}
else
{
return new CompoundOrPrimitive<>(orList);
}
}
private static boolean hasIllegalSeparator(char separator, String value)
{
if (value.charAt(0) == separator)
{
Logging.addParseMessage(Logging.LST_ERROR,
"Choice arguments may not start with " + separator + " : "
+ value);
return true;
}
if (value.charAt(value.length() - 1) == separator)
{
Logging.addParseMessage(Logging.LST_ERROR,
"Choice arguments may not end with " + separator + " : "
+ value);
return true;
}
if (value.indexOf(String.valueOf(new char[] { separator, separator })) != -1)
{
Logging.addParseMessage(Logging.LST_ERROR,
"Choice arguments uses double separator " + separator
+ separator + " : " + value);
return true;
}
return false;
}
public static <T extends CDOMObject> PrimitiveCollection<T> getPrimitive(
LoadContext context, SelectionCreator<T> sc, String joinedOr)
{
if (joinedOr.isEmpty() || hasIllegalSeparator('|', joinedOr))
{
return null;
}
List<PrimitiveCollection<T>> pcfOrList = new ArrayList<>();
ParsingSeparator pipe = new ParsingSeparator(joinedOr, '|');
pipe.addGroupingPair('[', ']');
pipe.addGroupingPair('(', ')');
for (; pipe.hasNext();)
{
String joinedAnd = pipe.next();
if (joinedAnd.isEmpty() || hasIllegalSeparator(',', joinedAnd))
{
return null;
}
List<PrimitiveCollection<T>> pcfAndList = new ArrayList<>();
ParsingSeparator comma = new ParsingSeparator(joinedAnd, ',');
comma.addGroupingPair('[', ']');
comma.addGroupingPair('(', ')');
for (; comma.hasNext();)
{
String primitive = comma.next();
if (primitive == null || primitive.isEmpty())
{
Logging.addParseMessage(Logging.LST_ERROR,
"Choice argument was null or empty: " + primitive);
return null;
}
PrimitiveCollection<T> pcf = getSimplePrimitive(context,
sc, primitive);
if (pcf == null)
{
Logging.addParseMessage(Logging.LST_ERROR,
"Choice argument was not valid: " + primitive);
return null;
}
else
{
pcfAndList.add(pcf);
}
}
if (pcfAndList.size() == 1)
{
pcfOrList.add(pcfAndList.get(0));
}
else
{
pcfOrList.add(new CompoundAndPrimitive<>(pcfAndList));
}
}
if (pcfOrList.size() == 1)
{
return pcfOrList.get(0);
}
else
{
return new CompoundOrPrimitive<>(pcfOrList);
}
}
public static PrimitiveInfo getPrimitiveInfo(String key)
{
int openBracketLoc = key.indexOf('[');
int closeBracketLoc = key.indexOf(']');
int equalLoc = key.indexOf('=');
PrimitiveInfo pi = new PrimitiveInfo();
pi.key = key;
if (openBracketLoc == -1)
{
if (closeBracketLoc != -1)
{
Logging.errorPrint(FOUND_ERR_IN_PRIM_CHOICE + key
+ " has a close bracket but no open bracket");
return null;
}
if (equalLoc == -1)
{
pi.tokKey = key;
pi.tokValue = null;
}
else
{
pi.tokKey = key.substring(0, equalLoc);
pi.tokValue = key.substring(equalLoc + 1);
if (pi.tokValue.isEmpty())
{
Logging.errorPrint(FOUND_ERR_IN_PRIM_CHOICE + key
+ " has equals but no target value");
return null;
}
}
pi.tokRestriction = null;
}
else
{
if (closeBracketLoc == -1)
{
Logging.errorPrint(FOUND_ERR_IN_PRIM_CHOICE + key
+ " has an open bracket but no close bracket");
return null;
}
if (closeBracketLoc != key.length() - 1)
{
Logging.errorPrint(FOUND_ERR_IN_PRIM_CHOICE + key
+ " had close bracket, but had characters "
+ "following the close bracket");
return null;
}
if (equalLoc == -1 || equalLoc > openBracketLoc)
{
pi.tokKey = key.substring(0, openBracketLoc);
pi.tokValue = null;
pi.tokRestriction = key.substring(openBracketLoc + 1,
closeBracketLoc);
}
else
{
pi.tokKey = key.substring(0, equalLoc);
pi.tokValue = key.substring(equalLoc + 1, openBracketLoc);
pi.tokRestriction = key.substring(openBracketLoc + 1,
closeBracketLoc);
}
}
return pi;
}
public static <T extends Loadable> PrimitiveCollection<T> getSimplePrimitive(
LoadContext context, SelectionCreator<T> sc, String key)
{
PrimitiveInfo pi = getPrimitiveInfo(key);
if (pi == null)
{
return null;
}
Class<T> refClass = sc.getReferenceClass(); //check against reference class of GroupDefinition
PrimitiveCollection<T> prim = getTokenPrimitive(context, refClass, pi);
if (prim == null)
{
prim = getDynamicGroup(context, pi, refClass);
}
if (prim == null)
{
return getTraditionalPrimitive(sc, pi);
}
return prim;
}
private static <T extends Loadable> PrimitiveCollection<T> getDynamicGroup(
LoadContext context, PrimitiveInfo pi, Class<T> refClass)
{
GroupDefinition<T> fgd = context.getGroup(refClass, pi.tokKey);
if (fgd == null)
{
return null;
}
ObjectContainer<T> p = fgd.getPrimitive(context, pi.tokValue);
return new ObjectContainerPrimitive<>(p);
}
public static <T> PrimitiveCollection<T> getTokenPrimitive(
LoadContext context, Class<T> cl, PrimitiveInfo pi)
{
PrimitiveToken<T> prim = TokenLibrary.getPrimitive(cl, pi.tokKey);
if ((prim != null)
&& !prim.initialize(context, cl, pi.tokValue, pi.tokRestriction))
{
return null;
}
return prim;
}
public static <T extends Loadable> PrimitiveCollection<T> getTraditionalPrimitive(
SelectionCreator<T> sc, PrimitiveInfo pi)
{
String tokKey = pi.tokKey;
if (pi.tokRestriction != null)
{
Logging.errorPrint("Didn't expect tokRestriction on " + tokKey
+ " here: " + pi.tokRestriction);
return null;
}
String tokValue = pi.tokValue;
if ("TYPE".equals(tokKey))
{
return TokenUtilities.getTypeReference(sc, tokValue);
}
if ("!TYPE".equals(tokKey))
{
CDOMGroupRef<T> typeReference = TokenUtilities
.getTypeReference(sc, tokValue);
if (typeReference == null)
{
return null;
}
return new NegatingPrimitive<>(typeReference, sc.getAllReference());
}
if (tokValue != null)
{
Logging.errorPrint("Didn't expect Arguments here: " + tokValue
+ " was found in " + pi.key);
}
if ("ALL".equals(tokKey))
{
return sc.getAllReference();
}
String key = pi.key;
if (key.startsWith(Constants.LST_TYPE_DOT))
{
return TokenUtilities.getTypeReference(sc, key
.substring(5));
}
if (key.startsWith(Constants.LST_NOT_TYPE_DOT))
{
return new NegatingPrimitive<>(TokenUtilities.getTypeReference(sc,
key.substring(6)), sc.getAllReference());
}
if (key.indexOf('%') == -1)
{
return sc.getReference(key);
}
else
{
return new PatternMatchingReference<>(sc.getReferenceClass(),
sc.getAllReference(), key);
}
}
public static class PrimitiveInfo
{
public String key;
public String tokKey;
public String tokValue;
public String tokRestriction;
}
public static <T extends CDOMObject> QualifierToken<T> getQualifier(
LoadContext loadContext, SelectionCreator<T> sc, String key)
{
if (key == null || key.isEmpty())
{
Logging.errorPrint(FOUND_ERR_IN_PRIM_CHOICE
+ "item was null or empty");
return null;
}
int openBracketLoc = key.indexOf('[');
int closeBracketLoc = key.indexOf(']');
int equalLoc = key.indexOf('=');
boolean startsNot = key.charAt(0) == '!';
String tokKey;
String tokValue;
String tokRestriction;
if (openBracketLoc == -1)
{
if (closeBracketLoc != -1)
{
Logging.errorPrint(FOUND_ERR_IN_QUAL_CHOICE + key
+ " has a close bracket but no open bracket");
return null;
}
if (equalLoc == -1)
{
tokKey = key;
tokValue = null;
}
else
{
tokKey = key.substring(0, equalLoc);
tokValue = key.substring(equalLoc + 1);
}
tokRestriction = null;
}
else
{
if (closeBracketLoc == -1)
{
Logging.errorPrint(FOUND_ERR_IN_QUAL_CHOICE + key
+ " has an open bracket but no close bracket");
return null;
}
if (closeBracketLoc != key.length() - 1)
{
Logging.errorPrint(FOUND_ERR_IN_QUAL_CHOICE + key
+ " had close bracket, but had characters "
+ "following the close bracket");
return null;
}
if (closeBracketLoc - openBracketLoc == 1)
{
Logging.errorPrint(FOUND_ERR_IN_QUAL_CHOICE + key
+ " has an open bracket "
+ "immediately followed by close bracket");
return null;
}
if (equalLoc == -1 || equalLoc > openBracketLoc)
{
tokKey = key.substring(0, openBracketLoc);
tokValue = null;
}
else
{
tokKey = key.substring(0, equalLoc);
tokValue = key.substring(equalLoc + 1, openBracketLoc);
}
tokRestriction = key.substring(openBracketLoc + 1, closeBracketLoc);
}
if (startsNot)
{
tokKey = tokKey.substring(1);
}
for (Iterator<QualifierToken<T>> it = new QualifierTokenIterator<>(
sc.getReferenceClass(), tokKey); it.hasNext();)
{
QualifierToken<T> token = it.next();
if (token.initialize(loadContext, sc, tokValue, tokRestriction,
startsNot))
{
return token;
}
Logging.addParseMessage(Logging.LST_ERROR,
"Failed in parsing typeStr: " + key);
}
return null;
}
}