001package jmri.jmrit.logixng.util.parser.functions;
002
003import java.util.ArrayList;
004import java.util.HashSet;
005import java.util.List;
006import java.util.Set;
007import java.util.regex.Matcher;
008import java.util.regex.Pattern;
009
010import jmri.JmriException;
011import jmri.jmrit.logixng.SymbolTable;
012import jmri.jmrit.logixng.util.parser.*;
013import jmri.util.TypeConversionUtil;
014
015import org.openide.util.lookup.ServiceProvider;
016
017/**
018 * Implementation of string functions.
019 *
020 * @author Daniel Bergqvist 2020
021 */
022@ServiceProvider(service = FunctionFactory.class)
023public class StringFunctions implements FunctionFactory {
024
025    @Override
026    public String getModule() {
027        return "String";
028    }
029
030    @Override
031    public Set<Function> getFunctions() {
032        Set<Function> functionClasses = new HashSet<>();
033
034        addFormatFunction(functionClasses);
035        addRegExFunction(functionClasses);
036        addStrlenFunction(functionClasses);
037
038        return functionClasses;
039    }
040
041    @Override
042    public Set<Constant> getConstants() {
043        return new HashSet<>();
044    }
045
046    @Override
047    public String getConstantDescription() {
048        // This module doesn't define any constants
049        return null;
050    }
051
052    private void addFormatFunction(Set<Function> functionClasses) {
053        functionClasses.add(new AbstractFunction(this, "format", Bundle.getMessage("String.format_Descr")) {
054            @Override
055            public Object calculate(SymbolTable symbolTable, List<ExpressionNode> parameterList)
056                    throws CalculateException, JmriException {
057                if (parameterList.isEmpty()) {
058                    throw new WrongNumberOfParametersException(Bundle.getMessage("WrongNumberOfParameters1", getName(), 1));
059                }
060
061                String formatStr = TypeConversionUtil.convertToString(
062                        parameterList.get(0).calculate(symbolTable), false);
063
064                List<Object> list = new ArrayList<>();
065                for (int i=1; i < parameterList.size(); i++) {
066                    list.add(parameterList.get(i).calculate(symbolTable));
067                }
068
069                return String.format(formatStr, list.toArray());
070            }
071        });
072    }
073
074    private void addRegExFunction(Set<Function> functionClasses) {
075        functionClasses.add(new AbstractFunction(this, "regex", Bundle.getMessage("String.regex_Descr")) {
076            @Override
077            public Object calculate(SymbolTable symbolTable, List<ExpressionNode> parameterList)
078                    throws CalculateException, JmriException {
079                if (parameterList.size() != 2) {
080                    throw new WrongNumberOfParametersException(Bundle.getMessage("WrongNumberOfParameters1", getName(), 1));
081                }
082
083                Object regex = parameterList.get(0).calculate(symbolTable);
084                Object value = parameterList.get(1).calculate(symbolTable);
085
086                if (regex == null) {
087                    throw new NullPointerException("Regular expression is null");
088                } else if (value == null) {
089                    throw new NullPointerException("Value is null");
090                } else if (! (regex instanceof String)) {
091                    throw new IllegalArgumentException("Parameter is not a String: "+regex.getClass().getName());
092                } else if (! (value instanceof String)) {
093                    throw new IllegalArgumentException("Value is not a String: "+value.getClass().getName());
094                }
095
096                Pattern p = Pattern.compile((String) regex);
097                Matcher m = p.matcher((String) value);
098
099                if (!m.matches()) {
100                    return null;
101                }
102
103                List<String> list = new ArrayList<>();
104                for (int i=1; i <= m.groupCount(); i++) {
105                    list.add(m.group(i));
106                }
107                return list;
108            }
109        });
110    }
111
112    private void addStrlenFunction(Set<Function> functionClasses) {
113        functionClasses.add(new AbstractFunction(this, "strlen", Bundle.getMessage("String.strlen_Descr")) {
114            @Override
115            public Object calculate(SymbolTable symbolTable, List<ExpressionNode> parameterList)
116                    throws CalculateException, JmriException {
117                if (parameterList.size() != 1) {
118                    throw new WrongNumberOfParametersException(Bundle.getMessage("WrongNumberOfParameters1", getName(), 1));
119                }
120
121                Object parameter = parameterList.get(0).calculate(symbolTable);
122
123                if (parameter == null) {
124                    throw new NullPointerException("Parameter is null");
125                } else if (parameter instanceof String) {
126                    return ((String)parameter).length();
127                }
128
129                throw new IllegalArgumentException("Parameter is not a String: "+parameter.getClass().getName());
130            }
131        });
132    }
133
134}