/*
* Copyright 2013-2017 the original author or authors.
*
* 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 org.springframework.data.mongodb.core.spel;
import java.util.Collections;
import java.util.Iterator;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.ast.Literal;
import org.springframework.expression.spel.ast.MethodReference;
import org.springframework.expression.spel.ast.Operator;
import org.springframework.expression.spel.ast.OperatorNot;
import org.springframework.util.Assert;
/**
* A value object for nodes in an expression. Allows iterating ove potentially available child {@link ExpressionNode}s.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public class ExpressionNode implements Iterable<ExpressionNode> {
private static final Iterator<ExpressionNode> EMPTY_ITERATOR = Collections.<ExpressionNode> emptySet().iterator();
private final SpelNode node;
private final ExpressionState state;
/**
* Creates a new {@link ExpressionNode} from the given {@link SpelNode} and {@link ExpressionState}.
*
* @param node must not be {@literal null}.
* @param state must not be {@literal null}.
*/
protected ExpressionNode(SpelNode node, ExpressionState state) {
Assert.notNull(node, "SpelNode must not be null!");
Assert.notNull(state, "ExpressionState must not be null!");
this.node = node;
this.state = state;
}
/**
* Factory method to create {@link ExpressionNode}'s according to the given {@link SpelNode} and
* {@link ExpressionState}.
*
* @param node
* @param state must not be {@literal null}.
* @return an {@link ExpressionNode} for the given {@link SpelNode} or {@literal null} if {@literal null} was given
* for the {@link SpelNode}.
*/
public static ExpressionNode from(SpelNode node, ExpressionState state) {
if (node == null) {
return null;
}
if (node instanceof Operator) {
return new OperatorNode((Operator) node, state);
}
if (node instanceof MethodReference) {
return new MethodReferenceNode((MethodReference) node, state);
}
if (node instanceof Literal) {
return new LiteralNode((Literal) node, state);
}
if (node instanceof OperatorNot) {
return new NotOperatorNode((OperatorNot) node, state);
}
return new ExpressionNode(node, state);
}
/**
* Returns the name of the {@link ExpressionNode}.
*
* @return
*/
public String getName() {
return node.toStringAST();
}
/**
* Returns whether the current {@link ExpressionNode} is backed by the given type.
*
* @param type must not be {@literal null}.
* @return
*/
public boolean isOfType(Class<?> type) {
Assert.notNull(type, "Type must not be empty!");
return type.isAssignableFrom(node.getClass());
}
/**
* Returns whether the given {@link ExpressionNode} is representing the same backing node type as the current one.
*
* @param node
* @return
*/
boolean isOfSameTypeAs(ExpressionNode node) {
return node == null ? false : this.node.getClass().equals(node.node.getClass());
}
/**
* Returns whether the {@link ExpressionNode} is a mathematical operation.
*
* @return
*/
public boolean isMathematicalOperation() {
return false;
}
/**
* Returns whether the {@link ExpressionNode} is a logical conjunction operation like {@code &&, ||}.
*
* @return
* @since 1.10
*/
public boolean isLogicalOperator() {
return false;
}
/**
* Returns whether the {@link ExpressionNode} is a literal.
*
* @return
*/
public boolean isLiteral() {
return false;
}
/**
* Returns the value of the current node.
*
* @return
*/
public Object getValue() {
return node.getValue(state);
}
/**
* Returns whether the current node has child nodes.
*
* @return
*/
public boolean hasChildren() {
return node.getChildCount() != 0;
}
/**
* Returns the child {@link ExpressionNode} with the given index.
*
* @param index must not be negative.
* @return
*/
public ExpressionNode getChild(int index) {
Assert.isTrue(index >= 0, "Index must be greater or equal to zero!");
return from(node.getChild(index), state);
}
/**
* Returns whether the {@link ExpressionNode} has a first child node that is not of the given type.
*
* @param type must not be {@literal null}.
* @return
*/
public boolean hasfirstChildNotOfType(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
return hasChildren() && !node.getChild(0).getClass().equals(type);
}
/**
* Creates a new {@link ExpressionNode} from the given {@link SpelNode}.
*
* @param node
* @return
*/
protected ExpressionNode from(SpelNode node) {
return from(node, state);
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<ExpressionNode> iterator() {
if (!hasChildren()) {
return EMPTY_ITERATOR;
}
return new Iterator<ExpressionNode>() {
int index = 0;
@Override
public boolean hasNext() {
return index < node.getChildCount();
}
@Override
public ExpressionNode next() {
return from(node.getChild(index++));
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}