fr.gael.dhus.olingo.v1.FunctionalVisitor.java Source code

Java tutorial

Introduction

Here is the source code for fr.gael.dhus.olingo.v1.FunctionalVisitor.java

Source

/*
 * Data Hub Service (DHuS) - For Space data distribution.
 * Copyright (C) 2016 GAEL Systems
 *
 * This file is part of DHuS software sources.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package fr.gael.dhus.olingo.v1;

import fr.gael.dhus.olingo.v1.visitor.ExecutableExpressionTree;
import fr.gael.dhus.olingo.v1.visitor.functors.Transformers;
import fr.gael.dhus.util.functional.ComparatorTransformer;
import fr.gael.dhus.util.functional.tuple.Duo;

import java.util.Comparator;
import java.util.List;

import org.apache.commons.collections4.Factory;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantFactory;

import org.apache.olingo.odata2.api.edm.EdmException;
import org.apache.olingo.odata2.api.edm.EdmLiteral;
import org.apache.olingo.odata2.api.edm.EdmLiteralKind;
import org.apache.olingo.odata2.api.edm.EdmTyped;
import org.apache.olingo.odata2.api.uri.expression.BinaryExpression;
import org.apache.olingo.odata2.api.uri.expression.BinaryOperator;
import org.apache.olingo.odata2.api.uri.expression.ExpressionVisitor;
import org.apache.olingo.odata2.api.uri.expression.FilterExpression;
import org.apache.olingo.odata2.api.uri.expression.LiteralExpression;
import org.apache.olingo.odata2.api.uri.expression.MemberExpression;
import org.apache.olingo.odata2.api.uri.expression.MethodExpression;
import org.apache.olingo.odata2.api.uri.expression.MethodOperator;
import org.apache.olingo.odata2.api.uri.expression.OrderByExpression;
import org.apache.olingo.odata2.api.uri.expression.OrderExpression;
import org.apache.olingo.odata2.api.uri.expression.PropertyExpression;
import org.apache.olingo.odata2.api.uri.expression.SortOrder;
import org.apache.olingo.odata2.api.uri.expression.UnaryExpression;
import org.apache.olingo.odata2.api.uri.expression.UnaryOperator;

/**
 * An Expression visitor that produces a functional expression tree able to validate entries
 * from any kind of collection.
 * <p>Subclasses must implement
 * {@link #visitProperty(PropertyExpression, String, EdmTyped)} and
 * {@link #visitOrder(OrderExpression, Object, SortOrder).
 * <p><strong>THIS CLASS IS NOT SUITABLE FOR FILTERING OF DATABASE DATA!</strong>, please use the
 * {@link OlingoSQLVisitor} instead.
 */
public abstract class FunctionalVisitor implements ExpressionVisitor {

    // $filter

    @Override
    public Object visitFilterExpression(FilterExpression fe, String filter, Object exp) {
        // Exp is a Node<?, Boolean>, returns an ExecutableExpressionTree.
        ExecutableExpressionTree.Node node = ExecutableExpressionTree.Node.class.cast(exp);
        return new ExecutableExpressionTree(node);
    }

    @Override
    public Object visitBinary(BinaryExpression be, BinaryOperator op, Object left, Object right) {
        // `left` and `right` are instances of Node<?> (Expression Tree)
        // Returns a BiTransformer (functional Java) as a Node<?> (Expression Tree)
        Transformer res;
        switch (op) {
        case EQ:
            res = Transformers.<Object>eq();
            break;
        case NE:
            res = Transformers.<Object>ne();
            break;

        case AND:
            res = Transformers.and();
            break;
        case OR:
            res = Transformers.or();
            break;

        case GE:
            res = Transformers.ge();
            break;
        case GT:
            res = Transformers.gt();
            break;
        case LE:
            res = Transformers.le();
            break;
        case LT:
            res = Transformers.lt();
            break;

        case ADD:
            res = Transformers.add();
            break;
        case SUB:
            res = Transformers.sub();
            break;
        case MUL:
            res = Transformers.mul();
            break;
        case DIV:
            res = Transformers.div();
            break;
        case MODULO:
            res = Transformers.mod();
            break;

        default:
            throw new UnsupportedOperationException("Unsupported operator: " + op.toUriLiteral());
        }

        ExecutableExpressionTree.Node nleft = ExecutableExpressionTree.Node.class.cast(left);
        ExecutableExpressionTree.Node nright = ExecutableExpressionTree.Node.class.cast(right);
        return ExecutableExpressionTree.Node.createNode(res, nleft, nright);
    }

    @Override
    public Object visitLiteral(LiteralExpression literal, EdmLiteral edm_literal) {
        try {
            // A literal is a Provider<?> (Functional Java)
            // Returns a Node<?> (Expression Tree)
            Object o = edm_literal.getType().valueOfString(edm_literal.getLiteral(), EdmLiteralKind.DEFAULT, null,
                    edm_literal.getType().getDefaultType());
            return ExecutableExpressionTree.Node.createLeave(ConstantFactory.constantFactory(o));
        } catch (EdmException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public Object visitMethod(MethodExpression me, MethodOperator method, List<Object> params) {
        if (params.size() == 1) {
            Transformer trans = null;

            switch (method) {
            case TOLOWER:
                trans = Transformers.tolower();
                break;
            case TOUPPER:
                trans = Transformers.toupper();
                break;
            case TRIM:
                trans = Transformers.trim();
                break;
            case LENGTH:
                trans = Transformers.length();
                break;

            case YEAR:
                trans = Transformers.year();
                break;
            case MONTH:
                trans = Transformers.month();
                break;
            case DAY:
                trans = Transformers.day();
                break;
            case HOUR:
                trans = Transformers.hour();
                break;
            case MINUTE:
                trans = Transformers.minute();
                break;
            case SECOND:
                trans = Transformers.second();
                break;

            case ROUND:
                trans = Transformers.round();
                break;
            case FLOOR:
                trans = Transformers.floor();
                break;
            case CEILING:
                trans = Transformers.ceiling();
                break;
            default:
                throw new UnsupportedOperationException("Unsupported method: " + method.toUriLiteral());
            }

            ExecutableExpressionTree.Node param = ExecutableExpressionTree.Node.class.cast(params.get(0));

            return ExecutableExpressionTree.Node.createNode(trans, param);
        } else if (params.size() == 2) {
            Transformer bi_trans = null;
            switch (method) {
            case ENDSWITH:
                bi_trans = Transformers.endswith();
                break;
            case INDEXOF:
                bi_trans = Transformers.indexof();
                break;
            case STARTSWITH:
                bi_trans = Transformers.startswith();
                break;
            case SUBSTRING:
                bi_trans = Transformers.substring();
                break;
            case SUBSTRINGOF:
                bi_trans = Transformers.substringof();
                break;
            case CONCAT:
                bi_trans = Transformers.concat();
                break;
            default:
                throw new UnsupportedOperationException("Unsupported method: " + method.toUriLiteral());
            }

            ExecutableExpressionTree.Node param1 = ExecutableExpressionTree.Node.class.cast(params.get(0));
            ExecutableExpressionTree.Node param2 = ExecutableExpressionTree.Node.class.cast(params.get(1));

            return ExecutableExpressionTree.Node.createNode(bi_trans, param1, param2);
        } else if (params.size() == 3 && method == MethodOperator.SUBSTRING) {
            Transformer bi_trans = Transformers.substring2();
            ExecutableExpressionTree.Node str = ExecutableExpressionTree.Node.class.cast(params.get(0));
            final ExecutableExpressionTree.Node intsupp1 = ExecutableExpressionTree.Node.class.cast(params.get(1));
            final ExecutableExpressionTree.Node intsupp2 = ExecutableExpressionTree.Node.class.cast(params.get(2));

            // Merges 2 nodes in One, return an array instead.
            ExecutableExpressionTree.Node n = new ExecutableExpressionTree.Node() {
                @Override
                public Factory exec(Object element) {
                    Integer[] res = new Integer[2];
                    res[0] = (Integer) intsupp1.exec(element);
                    res[1] = (Integer) intsupp2.exec(element);
                    return ConstantFactory.constantFactory(res);
                }
            };

            return ExecutableExpressionTree.Node.createNode(bi_trans, str, n);
        }
        return null;
    }

    @Override
    public Object visitMember(MemberExpression me, Object path, Object property) {
        // Not used
        return property;
    }

    @Override
    public Object visitUnary(UnaryExpression ue, UnaryOperator operator, Object operand) {
        // operand is a Node<?> (Expression Tree)
        // Returns an instance of Transformer<?> (functional java) as a Node<?> (Expression Tree)
        Transformer trans;
        switch (operator) {
        case NOT:
            trans = Transformers.not();
            break;
        case MINUS:
            trans = Transformers.minus();
            break;

        default:
            throw new UnsupportedOperationException("Unsupported operator: " + operator.toUriLiteral());
        }
        ExecutableExpressionTree.Node param = ExecutableExpressionTree.Node.class.cast(operand);

        return ExecutableExpressionTree.Node.createNode(trans, param);
    }

    /**
     * Called for each property in the $filter option.
     * An implementation MUST return an {@link ExecutableExpressionTree.Node}.
     * @param pe PropertyExpression.
     * @param uri_literal name of the property.
     * @param prop Property.
     * @return an {@link ExecutableExpressionTree.Node}.
     */
    @Override
    public abstract Object visitProperty(PropertyExpression pe, String uri_literal, EdmTyped prop);

    // $orderby

    @Override
    public Object visitOrderByExpression(OrderByExpression orderby, String exp, List<Object> orders) {
        final ExecutableExpressionTree.Node node = (ExecutableExpressionTree.Node) (orders.get(0));

        return new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return (Integer) node.exec(new Duo<>(o1, o2));
            }
        };
    }

    /**
     * Called for each fields in the $orderby option.
     * returns a {@linkplain ExecutableExpressionTree.Node#createDuoNode(Transformer,
     * ExecutableExpressionTree.Node, ExecutableExpressionTree.Node) DuoNode}
     * {@link ExecutableExpressionTree.Node}
     * @param oe OrderExpression
     * @param filter an {@link ExecutableExpressionTree.Node}.
     * @param sort_order `asc` or `desc`.
     * @return an {@link ExecutableExpressionTree.Node}
     */
    @Override
    public Object visitOrder(OrderExpression oe, Object filter, SortOrder sort_order) {
        ExecutableExpressionTree.Node param = ExecutableExpressionTree.Node.class.cast(filter);
        Transformer cmp = new ComparatorTransformer(sort_order == SortOrder.desc);

        return ExecutableExpressionTree.Node.createDuoNode(cmp, param, param);
    }
}