All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax0.jamal.snippet.CounterHierarchical Maven / Gradle / Ivy

The newest version!
package javax0.jamal.snippet;

import javax0.jamal.api.BadSyntax;
import javax0.jamal.api.Evaluable;
import javax0.jamal.api.Identified;
import javax0.jamal.api.Processor;
import javax0.jamal.tools.Input;
import javax0.jamal.tools.InputHandler;
import javax0.jamal.tools.Scanner;
import javax0.jamal.tools.param.StringParameter;

import java.util.ArrayList;
import java.util.List;

public class CounterHierarchical implements Identified, Evaluable, Scanner.WholeInput {
    final String id;

    @Override
    public String getId() {
        return id;
    }

    final String format;
    final boolean iiii;
    final List counters = new ArrayList<>();
    final List used = new ArrayList<>();
    final int[] starts;
    final int[] steps;
    final Processor processor;
    boolean frozen;
    String title = "";

    public CounterHierarchical(final Processor processor, final String id, final String format, final boolean iiii, final int[] starts, final int[] steps, List counters, List used, String title) {
        this.processor = processor;
        this.id = id;
        this.format = format;
        this.iiii = iiii;
        this.starts = starts;
        this.steps = steps;
        this.counters.addAll(counters);
        this.used.addAll(used);
        this.title = title;
    }

    public CounterHierarchical(final Processor processor, final String id, final String format, final boolean iiii, final int[] starts, final int[] steps) {
        this(processor, id, format, iiii, starts, steps, List.of(0 == starts.length ? 1 : starts[0]), List.of(false), "");
    }


    private enum Command {
        display, open, close, reset, last
    }

    @Override
    public String evaluate(String... parameters) throws BadSyntax {
        final var scanner = newScanner(Input.makeInput(parameters.length > 0 ? parameters[0] : ""), processor);
        final var command = scanner.enumeration(Command.class).optional();
        final var format = scanner.str(getId() + "$format", "format").defaultValue(this.format);
        final var saveAs = scanner.str(null, "save", "saveAs").optional();
        final var title = scanner.str(null, "title").defaultValue("");
        scanner.done();

        Command cmd = command.get(Command.class);
        switch (cmd) {
            case open:
                assertNotFrozen(cmd);
                assertNoTitle(title, cmd);
                final int index = counters.size();
                used.add(false);
                counters.add(getStart(index));
                break;
            case close:
                assertNotFrozen(cmd);
                assertNoTitle(title, cmd);
                counters.remove(counters.size() - 1);
                used.remove(used.size() - 1);
                break;
            case reset:
                assertNotFrozen(cmd);
                assertNoTitle(title, cmd);
                counters.clear();
                used.clear();
                counters.add(0, getStart(0));
                used.add(0, false);
                break;
            case display:
                if (used.get(used.size() - 1) && !frozen) {
                    stepLastLevel();
                }
                if (title.isPresent()) {
                    this.title = title.get();
                }
                used.set(used.size() - 1, true);
                if (saveAs.isPresent()) {
                    saveCounter(saveAs.get());
                }
                return formatCounter(format.get(), this.title);
            case last:
                assertNotFrozen(cmd);
                assertNoTitle(title, cmd);
                if (saveAs.isPresent()) {
                    saveCounter(saveAs.get());
                }
                return formatCounter(format.get(), this.title);
        }
        return "";
    }

    private void assertNoTitle(StringParameter title, Command cmd) throws BadSyntax {
        BadSyntax.when(title.isPresent(), "The command %s does not accept title.", cmd.name());
    }

    private void assertNotFrozen(Command cmd) throws BadSyntax {
        BadSyntax.when(frozen, "The counter '%s' is frozen and cannot be modified using %s.", id, cmd.name());
    }

    private void saveCounter(String id) throws BadSyntax {
        final var macro = new CounterHierarchical(processor, id, format, iiii, starts, steps, counters, used, title);
        macro.frozen = true;
        if (InputHandler.isGlobalMacro(id)) {
            processor.defineGlobal(macro);
        } else {
            processor.define(macro);
        }
        processor.getRegister().export(id);
    }

    private String formatCounter(String format, String title) throws BadSyntax {
        final var fmt = new StringBuilder(format);
        int start;
        while ((start = fmt.indexOf("{")) > -1) {
            final int end = fmt.indexOf("}", start);
            BadSyntax.when(end == -1, "Format string '%s' is not valid.", format);
            final var fmt0 = fmt.substring(start + 1, end);
            final var parts = fmt0.split(":", 2);
            BadSyntax.when(parts.length < 2, "Format string '%s' is not valid, it has to be 'N:format'.", format);
            final int level;
            try {
                level = Integer.parseInt(parts[0]);
            } catch (NumberFormatException nfe) {
                throw new BadSyntax(String.format("Format string '%s' is not valid, it has to be 'N:format' where N is an integer.", format));
            }
            if (level < 1) {
                throw new BadSyntax(String.format("Format string '%s' is not valid, it has to be 'N:format' where N is a positive.", format));
            }
            final var fmt1 = parts[1];
            if (level > counters.size()) {
                fmt.replace(start, end + 1, "");
            } else {
                final var counter = counters.get(level - 1);
                final var fmt2 = new CounterFormatter(fmt1, iiii, id).formatValue(counter, title);
                fmt.replace(start, end + 1, fmt2);
            }
        }
        return new CounterFormatter(fmt.toString(), iiii, id).formatValue(counters.get(0), title);
    }

    private void stepLastLevel() {
        counters.set(counters.size() - 1, counters.get(counters.size() - 1) + getStep(counters.size() - 1));
    }

    private int getStart(int level) {
        return level >= starts.length ? 1 : starts[level];
    }

    private int getStep(int level) {
        return level >= steps.length ? 1 : steps[level];
    }

    @Override
    public int expectedNumberOfArguments() {
        return 1;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy