package com.github.czyzby.lml.parser.impl.tag.macro; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.utils.Array; import com.github.czyzby.kiwi.util.common.Nullables; import com.github.czyzby.kiwi.util.common.Strings; import com.github.czyzby.lml.parser.LmlParser; import com.github.czyzby.lml.parser.LmlSyntax; import com.github.czyzby.lml.parser.action.ActorConsumer; import com.github.czyzby.lml.parser.tag.LmlTag; /** Allows to assign LML parser arguments from within LML templates. Arguments will be available after macro evaluation * with the given name, by default: {accessedLikeThis}. First macro attribute is the name of the argument to assign. * Other arguments will be joined to an array and a random value will be extracted. Alternatively, argument value can be * the data between macro tags. For example: <blockquote> * * <pre> * <:random key Random;Value/> * <:random key Random Value./> * <:random key>Random;Value</:assign> * </pre> * * </blockquote>All of these macro invocations would use assign either "Random" or "Value" to "key" argument. * <p> * Assignment macro supports optional named attributes:<blockquote> * * <pre> * <:assign key="name" value="Random;Value"/> * <:assign key="name">Random;Value</:assign> * </pre> * * </blockquote> * * @author MJ */ public class RandomLmlMacroTag extends AssignLmlMacroTag { public RandomLmlMacroTag(final LmlParser parser, final LmlTag parentTag, final StringBuilder rawTagData) { super(parser, parentTag, rawTagData); } @Override protected String processArgumentValue(final CharSequence argumentValue) { final String[] arguments = Strings.split(argumentValue, getParser().getSyntax().getArrayElementSeparator()); if (arguments.length == 0) { return Strings.EMPTY_STRING; } String argument; if (arguments.length == 1) { argument = arguments[0]; } else { argument = arguments[MathUtils.random(0, arguments.length - 1)]; } return extractRandomValue(argument); } /** @param argument random array element. * @return random range value (if range) or random result value (if method) or the passed element (if * unparseable). */ protected String extractRandomValue(final String argument) { final LmlSyntax syntax = getParser().getSyntax(); if (Strings.startsWith(argument, syntax.getMethodInvocationMarker())) { // Action: final ActorConsumer<?, Actor> action = getParser().parseAction(argument, getActor()); if (action == null) { getParser().throwError("Unable to find action for ID: " + argument); } return extractRandomObject(action.consume(getActor())); } // Range or plain: final int openingIndex = argument.indexOf(syntax.getRangeArrayOpening()); if (Strings.isCharacterAbsent(openingIndex)) { return argument; // Plain. } final int separatorIndex = argument.indexOf(syntax.getRangeArraySeparator()); if (Strings.isCharacterAbsent(separatorIndex) || separatorIndex < openingIndex) { return argument; // Plain. } final int closingIndex = argument.indexOf(syntax.getRangeArrayClosing()); if (Strings.isCharacterAbsent(closingIndex) || closingIndex < separatorIndex || closingIndex < openingIndex) { return argument; // Plain. } // Range: final String rangeBase = argument.substring(0, openingIndex); int rangeStart = getRangeValue(argument.substring(openingIndex + 1, separatorIndex)); int rangeEnd = getRangeValue(argument.substring(separatorIndex + 1, closingIndex)); if (rangeStart > rangeEnd) { final int temp = rangeStart; rangeStart = rangeEnd; rangeEnd = temp; } return rangeBase + MathUtils.random(rangeStart, rangeEnd); } /** @param rawData raw string data of range start or end. Expected to be a number (or a value that can be parsed * into an int). * @return parsed value of the range. */ protected int getRangeValue(final String rawData) { if (Strings.isInt(rawData)) { return Integer.parseInt(rawData); } return getParser().parseInt(rawData, getActor()); } /** @param result method result. * @return random element (if a collection) or result to string. */ protected String extractRandomObject(final Object result) { if (result instanceof Object[]) { final Object[] array = (Object[]) result; return Nullables.toString(array[MathUtils.random(0, array.length - 1)]); } else if (result instanceof Array<?>) { return Nullables.toString(((Array<?>) result).random()); } return Nullables.toString(result); } @Override protected char getAttributeSeparator() { return getParser().getSyntax().getArrayElementSeparator(); } }