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

javax0.jamal.tools.param.Param Maven / Gradle / Ivy

package javax0.jamal.tools.param;

import javax0.jamal.api.BadSyntax;
import javax0.jamal.api.Processor;
import javax0.jamal.tools.MacroReader;
import javax0.jamal.tools.OptionsStore;
import javax0.jamal.tools.Params;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Param implements Params.Param {
    final public String[] key;
    private String name;
    final List value = new ArrayList<>();
    private Processor processor;
    private String macroName;
    private String defaultValue = null;
    private boolean mandatory = true;

    /**
     * When calculating the final value, the actual string value from the parameter or the user defined macro is needed.
     * When we return a list or a boolean, we do not need the string. It is okay if it is there, and may be used,
     * but it is not a problem if neither the parameter nor a user defined macro defines the value.
     * 

* In that case, the value of the option or an empty list is returned. */ private boolean stringNeeded = true; @Override public void copy(Params.Param p) { Param it = (Param) p; name = it.name; value.clear(); value.addAll(it.value); defaultValue = it.defaultValue; mandatory = it.mandatory; } @Override public String[] keys() { return key; } @Override public void setName(final String name) { this.name = name; } @Override public String name() { return name; } private interface ThrowFunction { T apply(String s) throws Exception; } private ThrowFunction converter = s -> s; @Override public Param orElse(String defaultValue) { this.defaultValue = defaultValue; this.mandatory = false; return this; } @Override public Param orElseNull() { this.defaultValue = null; this.mandatory = false; return this; } @Override public Param orElseInt(int defaultValue) { this.defaultValue = "" + defaultValue; this.converter = s -> getInt(); this.mandatory = false; return (Param) this; } @Override public Param as(Function converter) { this.calculated = false; this.converter = converter::apply; return this; } public Param asPattern() { return as(Pattern.class, Pattern::compile); } @Override public Param asType(Class klass) { this.calculated = false; return (Param) this; } @Override public Param as(Class klass, Function converter) { this.calculated = false; this.converter = converter::apply; return (Param) this; } @Override public Param asInt() { this.calculated = false; this.converter = s -> getInt(); return (Param) this; } @Override public Param asBoolean() { this.calculated = false; stringNeeded = false; this.converter = s -> getBoolean(); return (Param) this; } @Override public Param asString() { this.calculated = false; this.converter = s -> s; return (Param) this; } @Override public Param> asList() { this.calculated = false; stringNeeded = false; this.converter = s -> getList(); return (Param>) this; } @Override public Param> asList(Class k) { this.calculated = false; stringNeeded = false; if (k == Integer.class) { this.converter = s -> getIntList(); } else { this.converter = s -> getList(); } return (Param>) this; } @Override public void inject(Processor processor, String macroName) { this.processor = processor; this.macroName = macroName; } public Param(String... key) { this.key = key; } @Override public void set(String value) { this.value.add(value); } /** * Get the value of a parameter as a string. * * @return the single value in an optional, or empty optional if the key was not present on the input and was not * defined in any user defined macro * @throws BadSyntax if there are multiple values for this key or if the key is not allowed for this macro */ private Optional _get() throws BadSyntax { if (!value.isEmpty()) { BadSyntax.when(value.size() > 1 && stringNeeded, "The key '%s' must not be multi valued in the macro '%s'", reportingName(key), macroName); return Optional.ofNullable(value.get(0)); } final var reader = MacroReader.macro(processor); return reader.readValue(key[0]); } private String reportingName(String[] key) { return key[0] == null ? key[1] : key[0]; } private String getRaw() throws BadSyntax { final var opt = _get(); BadSyntax.when(opt.isEmpty() && mandatory && stringNeeded, "The key '%s' for the macro '%s' is mandatory", reportingName(key), macroName); return opt.orElse(defaultValue); } private boolean calculated = false; private K cachedValue = null; /** * Get the value of the parameter. * * @return the value of the parameter * @throws BadSyntax if the parameter evaluation is faulty */ public K get() throws BadSyntax { if (processor == null) { // this cannot happen with the new scanner build classes throw new IllegalArgumentException("The parameter variable '" + reportingName(key) + "' was not processed during parsing."); } try { if (!calculated) { cachedValue = (K) converter.apply(getRaw()); calculated = true; } return cachedValue; } catch (BadSyntax bs) { throw bs; } catch (Exception e) { throw new BadSyntax("There was an exception converting the parameter '" + reportingName(key) + "' for the macro '" + macroName + "'", e); } } /** * Get the value of a boolean parameter */ public boolean is() throws BadSyntax { K result = get(); if (!(result instanceof Boolean)) { throw new IllegalArgumentException("Param.is() can only be used for boolean parameters"); } return (boolean) result; } @Override public boolean isOptional() { return !mandatory; } @Override public boolean isPresent() { return !value.isEmpty() || (key != null && key.length >0 && key[0] != null && processor.getRegister().getUserDefined(key[0]).isPresent()); } /** * @return {@code true} if the key was defined on the input or as user defined macro and the value is not "0", * "false" or "no", OR if the key was defined as an option, then the value of the option * @throws BadSyntax when the underlying call to {@link #_get()} throws */ private boolean getBoolean() throws BadSyntax { BadSyntax.when(value.size() > 1, "The key '%s' must not be multi valued in the macro '%s'", reportingName(key), macroName); if (!value.isEmpty()) { return !value.get(0).equals("false") && !value.get(0).equals("no") && !value.get(0).equals("0"); } else { return key[0] != null && OptionsStore.getInstance(processor).is(key[0]); } } /** * @return the possibly empty list of the values * @throws BadSyntax if there was no parameter defined with the name {@code key} and evaluating the user defined * macro of the same name throws up. */ private List getList() throws BadSyntax { if (!value.isEmpty()) { return value; } else { return MacroReader.macro(processor).readValue(key[0]).map(List::of).orElse(List.of()); } } /** * @return the possibly empty list of the values converted to integer values * @throws BadSyntax if there was no parameter defined with the name {@code key} and evaluating the user defined * macro of the same name throws up. */ private List getIntList() throws BadSyntax { if (!value.isEmpty()) { return value.stream().map(Integer::parseInt).collect(Collectors.toList()); } else { return MacroReader.macro(processor).readValue(key[0]).map(Integer::parseInt).map(List::of).orElse(List.of()); } } /** * Gets the value assigned to the {@code key} calling {@link #_get()} and converts it to an optional int. * * @return optional int value of the parameter * @throws BadSyntax if the used }{@link #_get()} throws up, or if the value cannot be converted to int */ private int getInt() throws BadSyntax { final var string = getRaw(); try { return Integer.parseInt(string); } catch (NumberFormatException nfe) { throw new BadSyntax(key[0] + " is not a number using the macro '" + macroName + "'."); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy