package com.github.czyzby.lml.parser.impl.tag.macro;
import com.github.czyzby.kiwi.util.common.Nullables;
import com.github.czyzby.kiwi.util.common.Strings;
import com.github.czyzby.kiwi.util.gdx.collection.GdxArrays;
import com.github.czyzby.kiwi.util.gdx.collection.GdxMaps;
import com.github.czyzby.lml.parser.LmlParser;
import com.github.czyzby.lml.parser.action.ActorConsumer;
import com.github.czyzby.lml.parser.impl.tag.AbstractMacroLmlTag;
import com.github.czyzby.lml.parser.tag.LmlTag;
/** Macro that allows to invoke registered LML methods during template parsing. First attribute is the method name to
* invoke (method marker optional). The second - optional - attribute is the name of LML argument that should be set as
* the result of the method. If the macro tag is a child, it will pass its parent's actor as the method argument; if the
* macro tag is a parent, it will look for method consuming a string (or no-arg) and pass its data between tags as
* method argument. For example:
*
* <blockquote>
*
* <pre>
* <label id=someLabel><:evaluate methodName/></label>
* </pre>
*
* </blockquote>This macro will look for method with "methodName" ID (either {@literal @}LmlAction-mapped, actual method
* name or a field name) and invoke it with label (id: "someLabel") as its argument - if it consumes one.
*
* <blockquote>
*
* <pre>
* <:evaluate methodId argumentName>Method argument</:evaluate>
* </pre>
*
* </blockquote>This macro will look for a method with "methodId" ID (consuming a string or no-arg) and invoke it with
* "Method argument" string argument. Its result will be assigned to "argumentName" all will be accessible like any
* other LML argument: {argumentName}. Note that tags between evaluate macro tags are NOT parsed and will be sent as
* plain text to the method; LML arguments, on the other hand, will be properly replaced.
*
* <p>
* This macro can be also used with named parameters: <blockquote>
*
* <pre>
* <:evaluate method="methodId" id="argumentName">Method argument</:evaluate>
* </pre>
*
* </blockquote>
*
* @author MJ */
public class EvaluateLmlMacroTag extends AbstractMacroLmlTag {
/** Optional name of first attribute: method name. */
public static final String METHOD_ATTRIBUTE = "method";
/** Optional name of second attribute: argument ID. */
public static final String ID_ATTRIBUTE = "id";
private String methodArgument;
public EvaluateLmlMacroTag(final LmlParser parser, final LmlTag parentTag, final StringBuilder rawTagData) {
super(parser, parentTag, rawTagData);
}
@Override
public void handleDataBetweenTags(final CharSequence rawMacroContent) {
if (Strings.isNotEmpty(rawMacroContent)) {
methodArgument = replaceArguments(rawMacroContent, getParser().getData().getArguments()).toString();
}
}
@Override
public void closeTag() {
if (GdxArrays.isEmpty(getAttributes())) {
getParser().throwErrorIfStrict("Evaluate macro needs at least one attribute: method ID.");
return;
}
final Object result;
if (methodArgument != null) {
result = executeMethod(methodArgument);
} else {
result = executeMethod(getParent() != null ? getParent().getActor() : null);
}
processMethodResult(result);
}
/** @param result was returned by the evaluated method. */
protected void processMethodResult(final Object result) {
if (hasAssignmentArgumentName()) {
getParser().getData().addArgument(getAssignmentArgumentName(), Nullables.toString(result));
}
}
/** @param value method argument. Finds a method that consumes this type of object (or no-arg).
* @return result of the invoked method.
* @param <ArgumentType> type of argument consumed by the method. */
protected <ArgumentType> Object executeMethod(final ArgumentType value) {
final ActorConsumer<?, ArgumentType> action = getParser().parseAction(getMethodId(), value);
if (action == null) {
getParser().throwErrorIfStrict("Cannot process evaluate macro. Did not found method with ID: "
+ getMethodId() + " for value: " + value);
return null;
}
return action.consume(value);
}
/** @return attribute assigned to method ID. */
protected String getMethodId() {
if (hasAttribute(METHOD_ATTRIBUTE)) {
return getAttribute(METHOD_ATTRIBUTE);
} else if (GdxMaps.isNotEmpty(getNamedAttributes())) {
getParser().throwError("Evaluate macro needs 'method' attribute. Got: " + getNamedAttributes());
}
return getAttributes().get(0);
}
/** @return true if has at least 2 attributes. */
protected boolean hasAssignmentArgumentName() {
return GdxArrays.isNotEmpty(getAttributes()) && GdxArrays.sizeOf(getAttributes()) > 1;
}
/** @return name of the argument that should be assigned to the result of the evaluated method. */
protected String getAssignmentArgumentName() {
if (hasAttribute(ID_ATTRIBUTE)) {
return getAttribute(ID_ATTRIBUTE);
}
return getAttributes().get(1);
}
@Override
public String[] getExpectedAttributes() {
return new String[] { METHOD_ATTRIBUTE, ID_ATTRIBUTE };
}
}