/*
* PJEP.java
* Copyright 2003 (C) Greg Bingleman <byngl@hotmail.com>
*
* 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
*
* Created on January 28, 2003, 11:18 PM
*
* @(#) $Id$
*/
package pcgen.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.nfunk.jep.ASTFunNode;
import org.nfunk.jep.JEP;
import org.nfunk.jep.Node;
import org.nfunk.jep.ParseException;
import org.nfunk.jep.function.PostfixMathCommand;
import pcgen.core.PlayerCharacter;
import pcgen.core.VariableProcessor;
import pcgen.persistence.lst.LstUtils;
import pcgen.system.PluginLoader;
/**
* {@code PJEP}
*
* @author Greg Bingleman <byngl@hotmail.com>
*
* Provides a common interface setup for Singular Systems' Java Mathematical Expression Parser.
*
* Provides the following functions:
* ceil, floor, getvar, var, max, min, if, roll, cl, charbonusto
*
* Provides the following variables:
* FALSE, TRUE
*
*/
public final class PJEP extends JEP
{
private Object parent;
private String variableSource;
private static List<Class<PCGenCommand>> commandList =
new ArrayList<>();
private List<PCGenCommand> localCommandList = new ArrayList<>();
public static void addCommand(Class<PCGenCommand> clazz)
{
commandList.add(clazz);
}
public static PluginLoader getJepPluginLoader()
{
return new PluginLoader()
{
@Override
public void loadPlugin(Class clazz) throws Exception
{
addCommand(clazz);
}
@Override
public Class[] getPluginClasses()
{
return new Class[]
{
PCGenCommand.class
};
}
};
}
public PJEP()
{
setAllowUndeclared(true);
addStandardFunctions();
for (Class<PCGenCommand> aClass : commandList)
{
try
{
PCGenCommand com = aClass.newInstance();
localCommandList.add(com);
addFunction(com.getFunctionName().toLowerCase(), com);
addFunction(com.getFunctionName().toUpperCase(), com);
}
catch (InstantiationException | IllegalAccessException e)
{
e.printStackTrace();
}
}
addFunction("cl", new ClassLevel());
addVariable("TRUE", 1);
addVariable("FALSE", 0);
}
@Override
public Node parseExpression(String expression_in)
{
if (updateVariables())
{
initSymTab();
}
return super.parseExpression(expression_in);
}
/**
* Identify if the results of the calculation will be cachable.
*
* @return True if the result would be cachable, false otherwise.
*/
public boolean isResultCachable()
{
return isResultCachable(getTopNode());
}
/**
* Identify if results from this node (and its children) are all cachable.
*
* @param node The node to be checked.
* @return True if the result would be cachable, false otherwise.
*/
public boolean isResultCachable(Node node)
{
if (node instanceof ASTFunNode)
{
ASTFunNode funcNode = (ASTFunNode) node;
if (funcNode.getPFMC() instanceof PCGenCommand)
{
PCGenCommand cmd = (PCGenCommand) funcNode.getPFMC();
if (!cmd.getCachable())
{
return false;
}
}
}
for (int i = 0; i < node.jjtGetNumChildren(); i++)
{
if (!isResultCachable(node.jjtGetChild(i)))
{
return false;
}
}
return true;
}
private boolean updateVariables()
{
boolean updated = true;
if (localCommandList != null)
{
for (PCGenCommand com : localCommandList)
{
updated = updated && !com.updateVariables(this);
}
}
return updated;
}
/**
* @deprecated
* eg. cl("Fighter")
* eg. cl("Fighter", 21)
* eg. cl()
*/
@Deprecated
private final class ClassLevel extends PostfixMathCommand
{
private ClassLevel()
{
numberOfParameters = -1;
}
/**
* Runs classlevel on the inStack. The parameter is popped
* off the {@code inStack}, and the variable's value is
* pushed back to the top of {@code inStack}.
*
* @param inStack The stack to process
*
* @throws ParseException
*/
// @SuppressWarnings("unchecked") //Uses JEP, which doesn't use generics
@Override
public void run(Stack inStack) throws ParseException
{
LstUtils.deprecationWarning("Jep function cl deprecated, use classlevel instead");
// check the stack
checkStack(inStack);
// get the parameter from the stack
int paramCount = curNumberOfParameters;
// If there are no parameters and this is used in a CLASS file,
// then use the class name
if (paramCount == 0)
{
String src = getVariableSource();
if (src.startsWith("CLASS:"))
{
src = src.substring(6);
inStack.push(src);
++paramCount;
}
}
//
// have to do this in reverse order...this is a stack afterall
//
Object param1;
Object param2 = null;
if (paramCount == 1)
{
param1 = inStack.pop();
}
else if (paramCount == 2)
{
param2 = inStack.pop();
param1 = inStack.pop();
if (param2 instanceof Integer)
{
// Nothing to do, it's already an Integer
}
else if (param2 instanceof Double)
{
param2 = ((Double) param2).intValue();
}
else
{
throw new ParseException("Invalid parameter type");
}
}
else
{
throw new ParseException("Invalid parameter count");
}
if (param1 instanceof String)
{
PlayerCharacter aPC = null;
if (parent instanceof VariableProcessor)
{
aPC = ((VariableProcessor) parent).getPc();
}
else if (parent instanceof PlayerCharacter)
{
aPC = (PlayerCharacter) parent;
}
if (aPC == null)
{
throw new ParseException("Invalid parent (no PC): "
+ parent.getClass().getName());
}
// ";BEFORELEVEL="
String cl = (String) param1;
if (param2 != null)
{
cl += ";BEFORELEVEL=" + param2.toString();
}
inStack.push(new Double(aPC.getClassLevelString(cl, false)));
}
else
{
throw new ParseException("Invalid parameter type");
}
}
}
/**
* @deprecated
* @return Returns the variableSource.
*/
@Deprecated
protected String getVariableSource()
{
return variableSource;
}
/**
* @param variableSource The variableSource to set.
*/
protected void setVariableSource(String variableSource)
{
this.variableSource = variableSource;
for (PCGenCommand com : localCommandList)
{
com.setVariableSource(variableSource);
}
}
/**
* @param parent The parent to set.
*/
public void setParent(Object parent)
{
this.parent = parent;
for (PCGenCommand com : localCommandList)
{
com.setParent(parent);
}
}
}