/*
* Copyright (c) 2007 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 pcgen.cdom.base;
import pcgen.base.formula.Formula;
import pcgen.base.formula.base.DependencyManager;
import pcgen.base.formula.base.EvaluationManager;
import pcgen.base.formula.base.FormulaManager;
import pcgen.base.formula.base.FormulaSemantics;
import pcgen.base.formula.base.LegalScope;
import pcgen.base.formula.base.ManagerFactory;
import pcgen.base.formula.inst.ComplexNEPFormula;
import pcgen.base.formula.inst.NEPFormula;
import pcgen.base.util.FormatManager;
import pcgen.core.Equipment;
import pcgen.core.PlayerCharacter;
/**
* FormulaFactory is a utility class which creates Formula objects based on the
* input provided
*/
public final class FormulaFactory
{
/**
* A Formula for the integer constant ZERO. This is done in order to
* minimize memory usage in the many cases where a default Formula of ZERO
* is required.
*/
public static final Formula ZERO = new NumberFormula(0);
/**
* A Formula for the integer constant ONE. This is done in order to minimize
* memory usage in the many cases where a default Formula of ONE is
* required.
*/
public static final Formula ONE = new NumberFormula(1);
private FormulaFactory()
{
// Can't instantiate Utility Class
}
/**
* Returns a Formula for the given String.
*
* @param formulaString
* The String to be converted to a Formula
* @return A Formula for the given String.
* @throws IllegalArgumentException
* if the given String is null or empty
*/
public static Formula getFormulaFor(String formulaString)
{
if (formulaString == null || formulaString.isEmpty())
{
throw new IllegalArgumentException("Formula cannot be empty");
}
try
{
return getFormulaFor(Integer.valueOf(formulaString));
}
catch (NumberFormatException e)
{
// Okay, just not an integer
try
{
return getFormulaFor(Double.valueOf(formulaString));
}
catch (NumberFormatException e2)
{
// Okay, just not a double
return new JEPFormula(formulaString);
}
}
}
/**
* Returns a Formula for the given String, using "old" formula system
*
* @param formulaString
* The String to be converted to a Formula
* @return A Formula for the given String.
* @throws IllegalArgumentException
* if the given String is null or empty
*/
public static Formula getJEPFormulaFor(String formulaString)
{
if (formulaString == null || formulaString.isEmpty())
{
throw new IllegalArgumentException("Formula cannot be empty");
}
try
{
return getFormulaFor(Integer.valueOf(formulaString));
}
catch (NumberFormatException e)
{
// Okay, just not an integer
try
{
return getFormulaFor(Double.valueOf(formulaString));
}
catch (NumberFormatException e2)
{
// Okay, just not a double
return new JEPFormula(formulaString);
}
}
}
/**
* Returns a Formula for the given Number.
*
* @param number
* The int to be converted to a Formula
* @return A Formula for the given Number.
* @throws IllegalArgumentException
* if the given Number is null
*/
public static Formula getFormulaFor(Number number)
{
return new NumberFormula(number);
}
/**
* NumberFormula is a fixed-value formula for a specific Integer.
*/
private static class NumberFormula implements Formula
{
/**
* The Number value of this NumberFormula
*/
private final Number number;
/**
* Creates a new NumberFormula from the given Number.
*
* @param intValue
* The Number value of this NumberFormula.
* @throws IllegalArgumentException
* if the given Number is null
*/
public NumberFormula(Number intValue)
{
if (intValue == null)
{
throw new IllegalArgumentException(
"Cannot create an NumberFormula with a null Number");
}
number = intValue;
}
/**
* Resolves this NumberFormula, returning the Number in this
* NumberFormula.
*
* @return the Number in this NumberFormula.
*/
@Override
public Number resolve(PlayerCharacter pc, String source)
{
return number;
}
/**
* Resolves this NumberFormula, returning the Number in this
* NumberFormula.
*
* @return the Number in this NumberFormula.
*/
@Override
public Number resolve(Equipment equipment, boolean primary,
PlayerCharacter pc, String source)
{
return number;
}
/**
* Returns a String representation of this NumberFormula.
*/
@Override
public String toString()
{
return number.toString();
}
/**
* Returns the consistent-with-equals hashCode for this NumberFormula
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return number.intValue();
}
/**
* Returns true if this NumberFormula is equal to the given Object.
* Equality is defined as being another NumberFormula object with equal
* value.
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
return (obj instanceof NumberFormula)
&& ((NumberFormula) obj).number.equals(number);
}
/**
* Returns true as an NumberFormula has an underlying Number (static)
* value
*/
@Override
public boolean isStatic()
{
return true;
}
/**
* Returns true as an NumberFormula is a valid Formula.
*/
@Override
public boolean isValid()
{
return true;
}
/**
* Resolves this NumberFormula, returning the Number in this
* NumberFormula.
*
* @return the Integer in this NumberFormula.
*/
@Override
public Number resolveStatic()
{
return number;
}
}
/**
* SimpleFormula is a fixed-value formula for a specific value.
*/
private static class SimpleFormula<T> implements NEPFormula<T>
{
/**
* The value of this SimpleFormula
*/
private final T value;
/**
* Creates a new SimpleFormula from the given value.
*
* @param val
* The value of this SimpleFormula.
* @throws IllegalArgumentException
* if the given value is null
*/
public SimpleFormula(T val)
{
if (val == null)
{
throw new IllegalArgumentException(
"Cannot create an SimpleFormula with a null value");
}
value = val;
}
/**
* Returns a String representation of this SimpleFormula.
*/
@Override
public String toString()
{
return value.toString();
}
/**
* Returns the consistent-with-equals hashCode for this SimpleFormula
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return value.hashCode();
}
/**
* Returns true if this SimpleFormula is equal to the given Object.
* Equality is defined as being another SimpleFormula object with equal
* value.
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
return (obj instanceof SimpleFormula)
&& ((SimpleFormula<?>) obj).value.equals(value);
}
@Override
public void getDependencies(DependencyManager fdm)
{
//None
}
@Override
public T resolve(EvaluationManager evalManager)
{
return value;
}
@Override
public void isValid(FormatManager<T> formatManager,
FormulaSemantics semantics)
{
Class<?> expectedFormat = formatManager.getManagedClass();
if (!expectedFormat.isAssignableFrom(value.getClass()))
{
semantics.setInvalid("Parse Error: Invalid Value Format: "
+ value.getClass() + " found in location requiring a "
+ expectedFormat + " (class cannot be evaluated)");
}
}
}
/**
* Returns a "New Equation Parser" formula for the given String when
* interpreted by the given FormatManager.
*
* Due to the type implied by the construction of ComplexNEPFormula, this
* should remain private and external users should be encouraged to use
* getValidFormula (or create a new NEPFormula themselves and check it for
* validity).
*
* @param fmtManager
* The FormulaManager to be used to interpret a "simple" formula
* @param expression
* The expression to be interpreted by the formula parser
* @return The NEPFormula representing the given expression
*/
private static <T> NEPFormula<T> getNEPFormulaFor(
FormatManager<T> fmtManager, String expression)
{
if (expression == null || expression.isEmpty())
{
throw new IllegalArgumentException("Formula cannot be empty");
}
try
{
return new SimpleFormula<>(fmtManager.convert(expression));
}
catch (IllegalArgumentException e)
{
// Okay, not simple :P
return new ComplexNEPFormula<>(expression);
}
}
/**
* Returns a "valid" NEPFormula for the given expression.
*
* If the given expression does not represent a valid formula, then this
* will throw an IllegalArgumentException.
*
* If the given expression does not return an object of the type in the
* given FormatManager, then this will throw an IllegalArgumentException.
*
* @param expression
* The String representation of the formula to be converted to a
* NEPFormula
* @param managerFactory
* The ManagerFactory to be used for building the FormulaSemantics
* @param formulaManager
* The FormulaManager to be used for validating the NEPExpression
* @param varScope
* The LegalScope in which the NEPFormula is established and
* checked
* @param formatManager
* The FormatManager in which the NEPFormula is established and
* checked
* @return a "valid" NEPFormula for the given expression
*/
public static <T> NEPFormula<T> getValidFormula(String expression,
ManagerFactory managerFactory, FormulaManager formulaManager, LegalScope varScope,
FormatManager<T> formatManager)
{
NEPFormula<T> formula = getNEPFormulaFor(formatManager, expression);
FormulaSemantics semantics = managerFactory.generateFormulaSemantics(
formulaManager, varScope, formatManager.getManagedClass());
formula.isValid(formatManager, semantics);
if (!semantics.isValid())
{
throw new IllegalArgumentException("Cannot create a Formula from: "
+ expression + ", due to: " + semantics.getReport()
+ " with format " + formatManager.getIdentifierType());
}
return formula;
}
}