package com.github.czyzby.lml.parser.impl.tag;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.utils.ObjectMap;
import com.github.czyzby.kiwi.util.common.Strings;
import com.github.czyzby.kiwi.util.tuple.immutable.Pair;
import com.github.czyzby.lml.parser.LmlParser;
import com.github.czyzby.lml.parser.LmlSyntax;
import com.github.czyzby.lml.parser.tag.LmlTag;
/** Common base for macro tags.
*
* @author MJ */
public abstract class AbstractMacroLmlTag extends AbstractLmlTag {
public AbstractMacroLmlTag(final LmlParser parser, final LmlTag parentTag, final StringBuilder rawTagData) {
super(parser, parentTag, rawTagData);
}
@Override
public Actor getActor() {
return getParent() == null ? null : getParent().getActor();
}
@Override
public Object getManagedObject() {
return null;
}
@Override
protected boolean supportsNamedAttributes() {
return false;
}
@Override
protected boolean supportsOptionalNamedAttributes() {
return true;
}
/** @return array of default attributes that the macro is prepared to handle. For debugging purposes. */
public String[] getExpectedAttributes() {
return Strings.EMPTY_ARRAY;
}
/** @param content will have the arguments replaced.
* @param macroArguments map of private macro arguments. This should be a separate map than these managed by LML
* data container, as regular LML arguments should not be parsed directly by the macro - marco replaces
* only its own arguments.
* @return content with replaced arguments. */
protected CharSequence replaceArguments(final CharSequence content,
final ObjectMap<String, ? extends CharSequence> macroArguments) {
if (Strings.isEmpty(content)) {
return Strings.EMPTY_STRING;
}
final StringBuilder contentBuilder = new StringBuilder(content.length());
final StringBuilder argumentNameBuilder = new StringBuilder();
final LmlSyntax syntax = getParser().getSyntax();
MAIN_LOOP:
for (int index = 0, length = content.length(); index < length; index++) {
final char character = content.charAt(index);
if (character == syntax.getArgumentOpening()) {
Strings.clearBuilder(argumentNameBuilder);
// Character is an argument opening. It might be a regular LML parser argument - which is not ours to
// replace - but it might also be a macro argument, so we need to investigate.
for (int argumentIndex = index + 1; argumentIndex < length; argumentIndex++) {
final char argumentCharacter = content.charAt(argumentIndex);
if (argumentCharacter == syntax.getArgumentClosing()) {
final String argumentName = argumentNameBuilder.toString();
if (macroArguments.containsKey(argumentName)) {
// Argument name found and present in macro argument. Appending argument value:
contentBuilder.append(macroArguments.get(argumentName));
// Skipping argument declaration, jumping to index after argument closing marker:
index = argumentIndex;
continue MAIN_LOOP;
}
}
argumentNameBuilder.append(argumentCharacter);
}
}
contentBuilder.append(character);
}
return contentBuilder;
}
/** @param content will be split into two non-null strings according to the passed separator. If the separator does
* not occur in the content, first returned string will be the whole content and second will be an empty
* string. Cannot be null.
* @param separator first occurrence of this separator will be stripped and will separate the content into 2 parts.
* Cannot be null.
* @return content separated into 2 parts. */
protected Pair<CharSequence, CharSequence> splitInTwo(final CharSequence content, final String separator) {
int correctIndexes = 0;
for (int index = 0, length = content.length(); index < length; index++) {
if (Strings.compareIgnoreCase(content.charAt(index), separator.charAt(correctIndexes))) {
if (++correctIndexes == separator.length()) {
return Pair.of(content.subSequence(0, index + 1 - separator.length()),
content.subSequence(index + 1, length));
}
} else {
correctIndexes = 0;
}
}
return new Pair<CharSequence, CharSequence>(content.toString(), Strings.EMPTY_CHAR_SEQUENCE);
}
/** @param macroResult will be appended to the template reader. */
protected void appendTextToParse(final CharSequence macroResult) {
getParser().getTemplateReader().append(macroResult, "'" + getTagName() + "' macro result");
}
@Override
public void handleChild(final LmlTag childTag) {
// Should never happen.
if (getParent() != null) {
getParent().handleChild(childTag);
}
}
@Override
public void closeTag() {
// Most macros will be done either in the constructor or plain text parsing method. Since macros usually do not
// have to be finalized, this operation is considered optional, at best.
}
/** @return macro tag attributes converted to a single equation, with escaped characters properly converted. */
protected String convertAttributesToEquation() {
return Strings.merge((Object[]) getAttributes().toArray());
}
}