eu.stratosphere.sopremo.expressions.EvaluationExpression.java Source code

Java tutorial

Introduction

Here is the source code for eu.stratosphere.sopremo.expressions.EvaluationExpression.java

Source

/***********************************************************************************************************************
 *
 * Copyright (C) 2010 by the Stratosphere project (http://stratosphere.eu)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 **********************************************************************************************************************/
package eu.stratosphere.sopremo.expressions;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.esotericsoftware.kryo.DefaultSerializer;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

import eu.stratosphere.sopremo.AbstractSopremoType;
import eu.stratosphere.sopremo.ISopremoType;
import eu.stratosphere.sopremo.SingletonSerializer;
import eu.stratosphere.sopremo.expressions.tree.ChildIterator;
import eu.stratosphere.sopremo.expressions.tree.ListChildIterator;
import eu.stratosphere.sopremo.type.IJsonNode;
import eu.stratosphere.util.Immutable;

/**
 * Base class for all evaluable expressions that can form an expression tree.<br>
 * All implementing classes are not thread-safe unless otherwise noted.<br/>
 * Please note, that a variety of expressions may return the same {@link IJsonNode} for multiple invocation to avoid
 * object allocations. It is thus never save to form a DAG of expressions.
 */
public abstract class EvaluationExpression extends AbstractSopremoType
        implements ISopremoType, Iterable<EvaluationExpression> {

    /**
     * Represents an expression that returns the input node without any modifications. The constant is mostly used for
     * {@link Operator}s that do not perform any transformation to the input, such as a filter operator.
     */
    public static final PathSegmentExpression VALUE = new ValueExpression();

    @Override
    public void appendAsString(final Appendable appendable) throws IOException {
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#clone()
     */
    @Override
    public EvaluationExpression clone() {
        return (EvaluationExpression) super.clone();
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (this.getClass() != obj.getClass())
            return false;
        return true;
    }

    /**
     * Evaluates the given node in the provided context.<br>
     * The given node can either be a normal {@link JsonNode} or one of the following special nodes:
     * <ul>
     * <li>{@link IArrayNode} wrapping an array of nodes if the evaluation is performed for more than one
     * {@link JsonStream},
     * <li>{@link IStreamNode} wrapping an iterator of incoming nodes which is most likely the content of a complete
     * {@link JsonStream} that is going to be aggregated, or
     * <li>IArrayNode<IJsonNode> of JsonStream when aggregating multiple JsonStreams.
     * </ul>
     * <br>
     * Consequently, the result may also be of one of the previously mentioned types.<br/>
     * Please note, that a variety of expressions may return the same {@link IJsonNode} for multiple invocation to avoid
     * object allocations.
     * 
     * @param node
     *        the node that should be evaluated or a special node representing containing several nodes
     * @return the node resulting from the evaluation or several nodes wrapped in a special node type
     */
    public abstract IJsonNode evaluate(IJsonNode node);

    @SuppressWarnings("unchecked")
    public <T extends EvaluationExpression> List<T> findAll(final Class<T> evaluableClass) {
        return (List<T>) this.findAll(Predicates.instanceOf(evaluableClass));
    }

    public List<EvaluationExpression> findAll(final Predicate<? super EvaluationExpression> predicate) {
        final ArrayList<EvaluationExpression> expressions = new ArrayList<EvaluationExpression>();
        this.findAll(predicate, expressions);
        return expressions;
    }

    @SuppressWarnings("unchecked")
    public <T extends EvaluationExpression> T findFirst(final Class<T> evaluableClass) {
        return (T) this.findFirst(Predicates.instanceOf(evaluableClass));
    }

    public EvaluationExpression findFirst(final Predicate<? super EvaluationExpression> predicate) {
        if (predicate.apply(this))
            return this;
        for (final EvaluationExpression child : this) {
            final EvaluationExpression expr = child.findFirst(predicate);
            if (expr != null)
                return child.findFirst(predicate);
        }
        return null;
    }

    @Override
    public int hashCode() {
        return 37;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Iterable#iterator()
     */
    @Override
    public ChildIterator iterator() {
        @SuppressWarnings("unchecked")
        final List<EvaluationExpression> emptyList = Collections.EMPTY_LIST;
        return new ListChildIterator(emptyList.listIterator());
    }

    public String printAsTree() {
        final StringBuilder builder = new StringBuilder();
        try {
            this.printAsTree(builder, 0);
        } catch (final IOException e) {
        }
        return builder.toString();
    }

    /**
     * Removes all sub-trees in-place that start with the given expression type.<br>
     * If expressions cannot be completely removed, they are replaced by {@link EvaluationExpression#VALUE}.
     * 
     * @param expressionType
     *        the expression type to remove
     * @return this expression without removed sub-expressions
     */
    public EvaluationExpression remove(final Class<?> expressionType) {
        return this.remove(Predicates.instanceOf(expressionType));
    }

    /**
     * Removes all sub-trees in-place that are equal to the given expression.<br>
     * If expressions cannot be completely removed, they are replaced by {@link EvaluationExpression#VALUE}.
     * 
     * @param expressionToRemove
     *        the expression to compare to
     * @return this expression without removed sub-expressions
     */
    public EvaluationExpression remove(final EvaluationExpression expressionToRemove) {
        return this.remove(Predicates.equalTo(expressionToRemove));
    }

    /**
     * Removes all sub-trees in-place that satisfy the predicate.<br>
     * If expressions cannot be completely removed, they are replaced by {@link EvaluationExpression#VALUE}.
     * 
     * @param predicate
     *        the predicate that determines whether to remove an expression
     * @return this expression without removed sub-expressions
     */
    public EvaluationExpression remove(final Predicate<? super EvaluationExpression> predicate) {
        if (predicate.apply(this))
            return VALUE;

        this.removeRecursively(predicate);
        return this;
    }

    /**
     * Replaces all expressions that are equal to <code>toReplace</code> with the given <code>replaceFragment</code> .
     * 
     * @param toReplace
     *        the expressions that should be replaced
     * @param replaceFragment
     *        the expression which should replace another one
     * @return the expression with the replaces
     */
    public EvaluationExpression replace(final EvaluationExpression toReplace,
            final EvaluationExpression replaceFragment) {
        return this.replace(Predicates.equalTo(toReplace), replaceFragment);
    }

    /**
     * Replaces all expressions that satisfy the <code>replacePredicate</code> with the given
     * <code>replaceFragment</code> .
     * 
     * @param replacePredicate
     *        the predicate that indicates whether an expression should be replaced
     * @param replaceFragment
     *        the expression which should replace another one
     * @return the expression with the replaces
     */
    public EvaluationExpression replace(final Predicate<? super EvaluationExpression> replacePredicate,
            final EvaluationExpression replaceFragment) {
        return this.replace(replacePredicate, new TransformFunction() {
            @Override
            public EvaluationExpression apply(final EvaluationExpression argument) {
                return replaceFragment;
            }
        });
    }

    /**
     * Replaces all expressions that satisfy the <code>replacePredicate</code> with the given
     * <code>replaceFunction</code> .
     * 
     * @param replacePredicate
     *        the predicate that indicates whether an expression should be replaced
     * @param replaceFunction
     *        the function that is used to replace an expression
     * @return the expression with the replaces
     */
    public EvaluationExpression replace(final Predicate<? super EvaluationExpression> replacePredicate,
            final Function<EvaluationExpression, EvaluationExpression> replaceFunction) {
        return this.transformRecursively(new TransformFunction() {
            @Override
            public EvaluationExpression apply(final EvaluationExpression evaluationExpression) {
                return replacePredicate.apply(evaluationExpression) ? replaceFunction.apply(evaluationExpression)
                        : evaluationExpression;
            }
        });
    }

    public EvaluationExpression simplify() {
        final ChildIterator iterator = this.iterator();
        while (iterator.hasNext()) {
            final EvaluationExpression evaluationExpression = iterator.next();
            iterator.set(evaluationExpression.simplify());
        }
        return this;
    }

    /**
     * Recursively invokes the transformation function on all children and on the expression itself.<br>
     * In general, this method can modify this expression in-place.<br>
     * To retain the original expression, next to the transformed expression, use {@link #clone()}.
     * 
     * @param function
     *        the transformation function
     * @return the transformed expression
     */
    public EvaluationExpression transformRecursively(
            final Function<EvaluationExpression, EvaluationExpression> function) {
        final ChildIterator iterator = this.iterator();
        while (iterator.hasNext()) {
            final EvaluationExpression evaluationExpression = iterator.next();
            iterator.set(evaluationExpression.transformRecursively(function));
        }
        return function.apply(this);
    }

    protected void printAsTree(final Appendable appendable, final int level) throws IOException {
        for (int index = 0; index < level; index++)
            appendable.append(' ');
        appendable.append(this.getClass().getSimpleName()).append(' ');
        this.appendAsString(appendable);
        appendable.append('\n');

        for (final EvaluationExpression child : this)
            child.printAsTree(appendable, level + 1);
    }

    private void findAll(final Predicate<? super EvaluationExpression> predicate,
            final ArrayList<EvaluationExpression> expressions) {
        if (predicate.apply(this))
            expressions.add(this);

        for (final EvaluationExpression child : this)
            child.findAll(predicate, expressions);
    }

    private void removeRecursively(final Predicate<? super EvaluationExpression> predicate) {
        final ChildIterator iterator = this.iterator();
        while (iterator.hasNext()) {
            final EvaluationExpression child = iterator.next();
            if (predicate.apply(child)) {
                if (!iterator.canChildBeRemoved())
                    iterator.set(VALUE);
                else
                    iterator.remove();
            } else
                child.removeRecursively(predicate);
        }
    }

    @DefaultSerializer(ValueSerializer.class)
    @Immutable
    public static final class ValueExpression extends PathSegmentExpression {
        /**
         * Initializes ValueExpression.
         */
        public ValueExpression() {
        }

        /*
         * (non-Javadoc)
         * @see eu.stratosphere.sopremo.expressions.EvaluationExpression#appendAsString(java.lang.Appendable)
         */
        @Override
        public void appendAsString(final Appendable appendable) throws IOException {
            appendable.append('x');
        }

        /*
         * (non-Javadoc)
         * @see eu.stratosphere.sopremo.expressions.PathSegmentExpression#equals(java.lang.Object)
         */
        @Override
        public boolean equals(final Object obj) {
            return obj == this;
        }

        /*
         * (non-Javadoc)
         * @see
         * eu.stratosphere.sopremo.expressions.PathSegmentExpression#equalsSameClass(eu.stratosphere.sopremo.expressions
         * .PathSegmentExpression)
         */
        @Override
        public boolean equalsSameClass(final PathSegmentExpression other) {
            return true;
        }

        @Override
        public IJsonNode evaluate(final IJsonNode node) {
            return node;
        }

        /*
         * (non-Javadoc)
         * @see eu.stratosphere.sopremo.expressions.PathSegmentExpression#getLast()
         */
        @Override
        public PathSegmentExpression getLast() {
            return this;
        }

        /*
         * (non-Javadoc)
         * @see eu.stratosphere.sopremo.expressions.PathSegmentExpression#hashCode()
         */
        @Override
        public int hashCode() {
            return System.identityHashCode(this);
        }

        /*
         * (non-Javadoc)
         * @see eu.stratosphere.sopremo.expressions.PathSegmentExpression#iterator()
         */
        @Override
        public ChildIterator iterator() {
            final List<EvaluationExpression> emptyList = Collections.emptyList();
            return new ListChildIterator(emptyList.listIterator());
        }

        @Override
        public IJsonNode set(final IJsonNode node, final IJsonNode value) {
            return value;
        }

        /*
         * (non-Javadoc)
         * @see
         * eu.stratosphere.sopremo.expressions.PathSegmentExpression#setInputExpression(eu.stratosphere.sopremo.expressions
         * .EvaluationExpression)
         */
        @Override
        public void setInputExpression(final EvaluationExpression inputExpression) {
            throw new IllegalStateException();
        }

        /*
         * (non-Javadoc)
         * @see eu.stratosphere.sopremo.expressions.PathSegmentExpression#withTail(eu.stratosphere.sopremo.expressions.
         * EvaluationExpression)
         */
        @Override
        public PathSegmentExpression withTail(final EvaluationExpression tail) {
            if (tail instanceof PathSegmentExpression)
                return (PathSegmentExpression) tail;
            return new ChainedSegmentExpression(tail);
        }

        /*
         * (non-Javadoc)
         * @see
         * eu.stratosphere.sopremo.expressions.PathSegmentExpression#evaluateSegment(eu.stratosphere.sopremo.type.IJsonNode
         * )
         */
        @Override
        protected IJsonNode evaluateSegment(final IJsonNode node) {
            return node;
        }

        /*
         * (non-Javadoc)
         * @see eu.stratosphere.sopremo.expressions.PathSegmentExpression#segmentHashCode()
         */
        @Override
        protected int segmentHashCode() {
            return 0;
        }
    }

    public static class ValueSerializer extends SingletonSerializer {
        /**
         * Initializes EvaluationExpression.ValueSerializer.
         */
        public ValueSerializer() {
            super(VALUE);
        }
    }

}