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

org.epics.pvmanager.ExpressionLanguage Maven / Gradle / Ivy

/**
 * Copyright (C) 2010-14 pvmanager developers. See COPYRIGHT.TXT
 * All rights reserved. Use is subject to license terms. See LICENSE.TXT
 */
package org.epics.pvmanager;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.epics.pvmanager.expression.*;

/**
 * Operators to constructs expression of PVs that the {@link PVManager} will
 * be able to monitor.
 *
 * @author carcassi
 */
public class ExpressionLanguage {

    static {
        // Install support for basic java types
        BasicTypeSupport.install();
    }
    
    private ExpressionLanguage() {}
    
    /**
     * Creates a constant expression that always return that object.
     * This is useful to test expressions or to introduce data that is available
     * at connection time at that will not change.
     * 
     * @param  type of the value
     * @param value the actual value
     * @return an expression that is always going to return the given value
     */
    public static  DesiredRateExpression constant(T value) {
        return constant(value, value.toString());
    }
    
    /**
     * Creates a constant expression that always return that object, with the
     * given name for the expression.
     * This is useful to test expressions or to introduce data that is available
     * at connection time at that will not change.
     * 
     * @param  type of the value
     * @param value the actual value
     * @param name the name of the expression
     * @return an expression that is always going to return the given value
     */
    public static  DesiredRateExpression constant(T value, String name) {
        Class clazz = Object.class;
        if (value != null)
            clazz = value.getClass();
        @SuppressWarnings("unchecked")
        ValueCache cache = (ValueCache) new ValueCacheImpl(clazz);
        if (value != null)
            cache.writeValue(value);
        return new DesiredRateExpressionImpl(new DesiredRateExpressionListImpl(), cache, name);
    }

    /**
     * A channel with the given name of any type. This expression can be
     * used both in a read and a write expression.
     *
     * @param name the channel name
     * @return an expression representing the channel
     */
    public static ChannelExpression channel(String name) {
        return channel(name, Object.class, Object.class);
    }

    /**
     * A channel with the given name and type. This expression can be
     * used both in a read and a write expression.
     *
     * @param  read payload
     * @param  write payload
     * @param name the channel name
     * @param readType type being read
     * @param writeType type being written
     * @return an expression representing the channel
     */
    public static  ChannelExpression channel(String name, Class readType, Class writeType) {
        if (name == null) {
            return new ChannelExpression(readType, writeType);
        } else {
            return new ChannelExpression(name, readType, writeType);
        }
    }

    /**
     * A list of channels with the given names of any type. This expression can be
     * used both in a read and a write expression.
     *
     * @param names the channel names; can't be null
     * @return an list of expressions representing the channels
     */
    public static ChannelExpressionList channels(String... names) {
        return channels(Arrays.asList(names), Object.class, Object.class);
    }

    /**
     * A list of channels with the given names and type. This expression can be
     * used both in a read and a write expression.
     *
     * @param  read payload
     * @param  write payload
     * @param readType type being read
     * @param writeType type being written
     * @param names the channel names; can't be null
     * @return an list of expressions representing the channels
     */
    public static  ChannelExpressionList channels(Collection names, Class readType, Class writeType) {
        return new ChannelExpressionList(names, readType, writeType);
    }

    /**
     * A list of channels with the given names of any type. This expression can be
     * used both in a read and a write expression.
     *
     * @param names the channel names; can't be null
     * @return an list of expressions representing the channels
     */
    public static ChannelExpressionList channels(Collection names) {
        return channels(names, Object.class, Object.class);
    }

    /**
     * Returns all the new values generated by the expression source rate.
     *
     * @param  type being read
     * @param expressions source rate expressions
     * @return a new expression
     */
    public static  DesiredRateExpressionList>
            newValuesOf(SourceRateExpressionList expressions) {
        DesiredRateExpressionList> list = new DesiredRateExpressionListImpl>();
        for (SourceRateExpression expression : expressions.getSourceRateExpressions()) {
            list.and(newValuesOf(expression));
        }
        return list;
    }

    /**
     * Returns up to 1,000 new values generated by the expression source rate.
     * 

* You are strongly encouraged to use {@link #newValuesOf(org.epics.pvmanager.expression.SourceRateExpression, int) } * to set a limit that is appropriate for your application. * * @param type being read * @param expression source rate expression * @return a new expression */ public static DesiredRateExpression> newValuesOf(SourceRateExpression expression) { return newValuesOf(expression, 1000); } /** * Returns up to maxValues new values generated by the expression source rate. * * @param type being read * @param expression source rate expression * @param maxValues maximum number of values to send with each notification * @return a new expression */ public static DesiredRateExpression> newValuesOf(SourceRateExpression expression, int maxValues) { return new DesiredRateExpressionImpl>(expression, new QueueCollector(maxValues), expression.getName()); } /** * Expression that returns (only) the latest value computed * from a {@code SourceRateExpression}. * * @param type being read * @param expression expression read at the source rate * @return a new expression */ public static DesiredRateExpression latestValueOf(SourceRateExpression expression) { return new DesiredRateExpressionImpl(expression, new LatestValueCollector(), expression.getName()); } /** * Expression that returns (only) the latest value computed * from a {@code SourceRateExpression}. * * @param type being read * @param expressions expressions read at the source rate * @return an expression list */ public static DesiredRateExpressionList latestValueOf(SourceRateExpressionList expressions) { DesiredRateExpressionList list = new DesiredRateExpressionListImpl(); for (SourceRateExpression expression : expressions.getSourceRateExpressions()) { list.and(latestValueOf(expression)); } return list; } /** * For reads, returns (only) the latest value computed * from a {@code SourceRateReadWriteExpression}; for writes, same * as the given expression. * * @param read payload * @param write payload * @param expression expression read at the source rate * @return a new expression */ public static DesiredRateReadWriteExpression latestValueOf(SourceRateReadWriteExpression expression) { return new DesiredRateReadWriteExpressionImpl(latestValueOf((SourceRateExpression) expression), expression); } /** * For reads, returns (only) the latest value computed * from a {@code SourceRateReadWriteExpression}; for writes, same * as the given expression. * * @param read payload * @param write payload * @param expressions expressions read at the source rate * @return a new expression */ public static DesiredRateReadWriteExpressionList latestValueOf(SourceRateReadWriteExpressionList expressions) { DesiredRateReadWriteExpressionListImpl list = new DesiredRateReadWriteExpressionListImpl(); for (SourceRateReadWriteExpression expression : expressions.getSourceRateReadWriteExpressions()) { list.and(latestValueOf(expression)); } return list; } /** * A user provided single argument function. * * @param result type * @param argument type */ public static interface OneArgFunction { /** * Calculates the new value. * * @param arg argument * @return result */ R calculate(A arg); } /** * A user provided double argument function. * * @param result type * @param first argument type * @param second argument type */ public static interface TwoArgFunction { /** * Calculates the new value. * * @param arg1 first argument * @param arg2 second argument * @return result */ R calculate(A1 arg1, A2 arg2); } /** * An expression that represents the result of a user provided function. * * @param result type * @param argument type * @param function the user provided function * @param argExpression expression for the function argument * @return a new expression */ public static DesiredRateExpression resultOf(final OneArgFunction function, DesiredRateExpression argExpression) { return resultOf(function, argExpression, function.getClass().getSimpleName() + "(" + argExpression.getName() + ")"); } /** * An expression that represents the result of a user provided function. * * @param result type * @param argument type * @param function the user provided function * @param argExpression expression for the function argument * @param name expression name * @return a new expression */ public static DesiredRateExpression resultOf(final OneArgFunction function, DesiredRateExpression argExpression, String name) { final ReadFunction arg = argExpression.getFunction(); return new DesiredRateExpressionImpl(argExpression, new ReadFunction() { @Override public R readValue() { return function.calculate(arg.readValue()); } }, name); } /** * An expression that represents the result of a user provided function. * * @param result type * @param first argument type * @param second argument type * @param function the user provided function * @param arg1Expression expression for the first argument * @param arg2Expression expression for the second argument * @return a new expression */ public static DesiredRateExpression resultOf(final TwoArgFunction function, DesiredRateExpression arg1Expression, DesiredRateExpression arg2Expression) { return resultOf(function, arg1Expression, arg2Expression, function.getClass().getSimpleName() + "(" + arg1Expression.getName() + ", " + arg2Expression.getName() + ")"); } /** * An expression that represents the result of a user provided function. * * @param result type * @param first argument type * @param second argument type * @param function the user provided function * @param arg1Expression expression for the first argument * @param arg2Expression expression for the second argument * @param name expression name * @return a new expression */ public static DesiredRateExpression resultOf(final TwoArgFunction function, DesiredRateExpression arg1Expression, DesiredRateExpression arg2Expression, String name) { final ReadFunction arg1 = arg1Expression.getFunction(); final ReadFunction arg2 = arg2Expression.getFunction(); @SuppressWarnings("unchecked") DesiredRateExpressionList argExpressions = new DesiredRateExpressionListImpl().and(arg1Expression).and(arg2Expression); return new DesiredRateExpressionImpl(argExpressions, new ReadFunction() { @Override public R readValue() { return function.calculate(arg1.readValue(), arg2.readValue()); } }, name); } /** * Filters a data stream, removing updates that match the given function. * Looks for objects of a specific type, * and filters based on previous and current value. * * @param the type to cast to before the filtering */ public static abstract class Filter { private final Class clazz; private final boolean filterUnmatched; Filter() { clazz = null; filterUnmatched = false; } /** * Creates a filter which looks for and cases data objects of the * given class. * * @param clazz the argument type of the filter */ public Filter(Class clazz) { this(clazz, false); } /** * Creates a filter which looks for and cases data objects of the * given class. If objects do not match, returns filterUnmatched. * * @param clazz the argument type of the filter * @param filterUnmatched whether objects that don't match the class * should be filtered or not */ public Filter(Class clazz, boolean filterUnmatched) { this.clazz = clazz; this.filterUnmatched = filterUnmatched; } // This is what the framework should actually call: it does the // type checking and casting boolean innerFilter(Object previousValue, Object currentValue) { if ((previousValue == null || clazz.isInstance(previousValue)) && (currentValue == null || clazz.isInstance(currentValue))) { return filter(clazz.cast(previousValue), clazz.cast(currentValue)); } return filterUnmatched; } /** * Determines whether the new value should be filtered or not. The * filtering is done based on the previousValue, which is always a * value that passed the filtering. The first value ever to be * passed to the filter will have null for previousValue. * * @param previousValue the previous data update * @param currentValue the current data update * @return true if the current data update should be dropped */ public abstract boolean filter(T previousValue, T currentValue); /** * Returns a new filter that is the logical AND of this and the given * one. * * @param filter another filter * @return a new filter that is the AND of the two */ public Filter and(final Filter filter) { return new Filter() { @Override public boolean innerFilter(Object previousValue, Object currentValue) { return super.innerFilter(previousValue, currentValue) && filter.innerFilter(previousValue, currentValue); } @Override public boolean filter(Object previousValue, Object currentValue) { throw new UnsupportedOperationException("Not used."); } }; } /** * Returns a new filter that is the logical OR of this and the given * one. * * @param filter another filter * @return a new filter that is the OR of the two */ public Filter or(final Filter filter) { return new Filter() { @Override public boolean innerFilter(Object previousValue, Object currentValue) { return super.innerFilter(previousValue, currentValue) || filter.innerFilter(previousValue, currentValue); } @Override public boolean filter(Object previousValue, Object currentValue) { throw new UnsupportedOperationException("Not used."); } }; } } /** * Filters a stream of updates with the given filter. * * @param the type of data streaming in and out * @param filter the filtering function * @param expression the argument expression * @return a new expression for the filtering result */ public static DesiredRateExpression> filterBy(final Filter filter, DesiredRateExpression> expression) { String name = expression.getName(); final ReadFunction> arg = expression.getFunction(); return new DesiredRateExpressionImpl>(expression, new ReadFunction>() { private T previousValue; @Override public List readValue() { List list = arg.readValue(); List newList = new ArrayList(); for (T element : list) { if (!filter.innerFilter(previousValue, element)) { newList.add(element); previousValue = element; } } return newList; } }, name); } // Static collections /** * Converts a list of expressions to an expression that returns the list of results. * * @param type being read * @param expressions a list of expressions * @return an expression representing the list of results */ public static DesiredRateExpression> listOf(DesiredRateExpressionList expressions) { // Calculate all the needed functions to combine List functions = new ArrayList(); for (DesiredRateExpression expression : expressions.getDesiredRateExpressions()) { functions.add(expression.getFunction()); } @SuppressWarnings("unchecked") DesiredRateExpression> expression = new DesiredRateExpressionImpl>(expressions, (ReadFunction>) (ReadFunction) new ListOfFunction(functions), null); return expression; } // Dynamic collections (change after expression creation) /** * An empty map that can manage expressions of the given type. *

* The returned expression is dynamic, which means child expressions * can be added or removed from the map. * * @param the type of the values * @param clazz the type of the values * @return an expression representing a map from name to results */ public static ReadMap readMapOf(Class clazz){ return new ReadMap<>(); } /** * An empty map that can write expressions of the given type. *

* The returned expression is dynamic, which means child expressions * can be added or removed from the map. * * @param the type of the values * @param clazz the type of the values * @return an expression representing a map from name to results */ public static WriteMap writeMapOf(Class clazz){ return new WriteMap<>(); } /** * An empty map that can read/write expressions of the given type. *

* The returned expression is dynamic, which means child expressions * can be added or removed from the map. * * @param the type of the values to read * @param the type of the values to write * @param readClass the type of the values to read * @param writeClass the type of the values to write * @return an expression representing a map from name to results */ public static ReadWriteMap mapOf(Class readClass, Class writeClass){ return new ReadWriteMap<>(); } /** * An expression that returns a key/value map where the key is the * expression name and the value is the expression value. *

* The returned expression is dynamic, which means child expressions * can be added or removed from the map. * * @param the type of the values * @param expressions a list of expressions * @return an expression representing a map from name to results */ public static ReadMap mapOf(DesiredRateExpressionList expressions){ return new ReadMap().add(expressions); } /** * An expression that expects a key/value map where the key is the * expression name and the value is the expression value. *

* The returned expression is dynamic, which means child expressions * can be added or removed from the map. * * @param the type of the values * @param expressions a list of expressions * @return an expression representing a map from name to results */ public static WriteMap mapOf(WriteExpressionList expressions){ return new WriteMap().add(expressions); } /** * An expression that works on a key/value map where the key is the * expression name and the value is the expression value. *

* The returned expression is dynamic, which means child expressions * can be added or removed from the map. * * @param the type for the read values * @param the type for the write values * @param expressions a list of expressions * @return an expression representing a map from name to results */ public static ReadWriteMap mapOf(DesiredRateReadWriteExpressionList expressions){ return new ReadWriteMap().add(expressions); } // Collectors for external sources /** * A queue of objects of the given class. By default, it holds at maximum * 10 elements. *

* This can be used to create expressions where the source of the data is * not just pvmanager data sources. One can add new values to a queue * from any thread, and in response to any event, such as user input, * updates from time consuming tasks or responses from services. * * @param the type to be kept in the queue * @param clazz the type for the values to be kept in the queue * @return a new queue */ public static Queue queueOf(Class clazz) { return new Queue<>(10); } /** * A cache of objects of the given class. By default, it holds at maximum * 10 elements. *

* This can be used to create expressions where the source of the data is * not just pvmanager data sources. One can add new values to the cache * from any thread, and in response to any event, such as user input, * updates from time consuming tasks or responses from services. * * @param the type to be kept in the queue * @param clazz the type for the values to be kept in the queue * @return a new queue */ public static Cache cacheOf(Class clazz) { return new Cache<>(10); } }