blob: e1936c149446cdb30143a04cba9f6ede3420397b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.olingo.server.core.debug;
import java.util.List;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceFunction;
import org.apache.olingo.server.api.uri.UriResourceIt;
import org.apache.olingo.server.api.uri.UriResourceLambdaAll;
import org.apache.olingo.server.api.uri.UriResourceLambdaAny;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
import org.apache.olingo.server.api.uri.UriResourceSingleton;
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
import org.apache.olingo.server.api.uri.queryoption.expression.Member;
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* A custom expression visitor which converts the tree into a {@link JsonNode} tree.
*/
public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
private static final String ANY_NAME = "ANY";
private static final String ALL_NAME = "ALL";
private static final String UNKNOWN_NAME = "unknown";
private static final String STRING_NAME = "String";
private static final String BOOLEAN_NAME = "Boolean";
private static final String NUMBER_NAME = "Number";
private static final String DATE_NAME = "Date";
private static final String TIME_NAME = "TimeOfDay";
private static final String DATETIMEOFFSET_NAME = "DateTimeOffset";
private static final String ENUM_NAME = "enum";
private static final String VALUES_NAME = "values";
private static final String NAME_NAME = "name";
private static final String ALIAS_NAME = "alias";
private static final String RESOURCE_SEGMENTS_NAME = "resourceSegments";
private static final String MEMBER_NAME = "member";
private static final String VALUE_NAME = "value";
private static final String LITERAL_NAME = "literal";
private static final String EXPRESSION_NAME = "expression";
private static final String LAMBDA_VARIABLE_NAME = "lambdaVariable";
private static final String LAMBDA_FUNCTION_NAME = "lambdaFunction";
private static final String LAMBDA_REFERENCE_NAME = "lambdaReference";
private static final String UNARY_NAME = "unary";
private static final String BINARY_NAME = "binary";
private static final String LEFT_NODE_NAME = "left";
private static final String RIGHT_NODE_NAME = "right";
private static final String PARAMETERS_NAME = "parameters";
private static final String METHOD_NAME = "method";
private static final String OPERAND_NAME = "operand";
private static final String TYPE_NAME = "type";
private static final String OPERATOR_NAME = "operator";
private static final String NODE_TYPE_NAME = "nodeType";
private static final String KEYS_NAME = "keys";
private static final String TYPE_FILTER_NAME = "typeFilter";
private static final String TYPE_FILTER_ON_COLLECTION_NAME = "typeFilterOnCollection";
private static final String TYPE_FILTER_ON_ENTRY_NAME = "typeFilterOnEntry";
private final JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
@Override
public JsonNode visitBinaryOperator(final BinaryOperatorKind operator, final JsonNode left, final JsonNode right)
throws ExpressionVisitException, ODataApplicationException {
ObjectNode result = nodeFactory.objectNode()
.put(NODE_TYPE_NAME, BINARY_NAME)
.put(OPERATOR_NAME, operator.toString())
.put(TYPE_NAME, getType(operator));
result.set(LEFT_NODE_NAME, left);
result.set(RIGHT_NODE_NAME, right);
return result;
}
@Override
public JsonNode visitUnaryOperator(final UnaryOperatorKind operator, final JsonNode operand)
throws ExpressionVisitException, ODataApplicationException {
return nodeFactory.objectNode()
.put(NODE_TYPE_NAME, UNARY_NAME)
.put(OPERATOR_NAME, operator.toString())
.put(TYPE_NAME, getType(operator))
.set(OPERAND_NAME, operand);
}
@Override
public JsonNode visitMethodCall(final MethodKind methodCall, final List<JsonNode> parameters)
throws ExpressionVisitException, ODataApplicationException {
ObjectNode result = nodeFactory.objectNode()
.put(NODE_TYPE_NAME, METHOD_NAME)
.put(OPERATOR_NAME, methodCall.toString())
.put(TYPE_NAME, getType(methodCall));
ArrayNode jsonParameters = result.putArray(PARAMETERS_NAME);
for (final JsonNode parameter : parameters) {
jsonParameters.add(parameter);
}
return result;
}
@Override
public JsonNode visitLambdaExpression(final String lambdaFunction, final String lambdaVariable,
final Expression expression) throws ExpressionVisitException, ODataApplicationException {
return nodeFactory.objectNode()
.put(NODE_TYPE_NAME, LAMBDA_FUNCTION_NAME)
.put(LAMBDA_VARIABLE_NAME, lambdaVariable)
.set(EXPRESSION_NAME, expression.accept(this));
}
@Override
public JsonNode visitLiteral(final Literal literal) throws ExpressionVisitException, ODataApplicationException {
return nodeFactory.objectNode()
.put(NODE_TYPE_NAME, LITERAL_NAME)
.put(TYPE_NAME, getTypeString(literal.getType()))
.put(VALUE_NAME, literal.getText());
}
@Override
public JsonNode visitMember(final Member member)
throws ExpressionVisitException, ODataApplicationException {
final List<UriResource> uriResourceParts = member.getResourcePath().getUriResourceParts();
final UriResource lastSegment = uriResourceParts.get(uriResourceParts.size() - 1);
ObjectNode result = nodeFactory.objectNode()
.put(NODE_TYPE_NAME, MEMBER_NAME)
.put(TYPE_NAME, getType(lastSegment));
putType(result, TYPE_FILTER_NAME, member.getStartTypeFilter());
ArrayNode segments = result.putArray(RESOURCE_SEGMENTS_NAME);
for (final UriResource segment : uriResourceParts) {
if (segment instanceof UriResourceLambdaAll) {
final UriResourceLambdaAll all = (UriResourceLambdaAll) segment;
segments.add(visitLambdaExpression(ALL_NAME, all.getLambdaVariable(), all.getExpression()));
} else if (segment instanceof UriResourceLambdaAny) {
final UriResourceLambdaAny any = (UriResourceLambdaAny) segment;
segments.add(visitLambdaExpression(ANY_NAME, any.getLambdaVariable(), any.getExpression()));
} else if (segment instanceof UriResourcePartTyped) {
ObjectNode node = nodeFactory.objectNode()
.put(NODE_TYPE_NAME, segment.getKind().toString())
.put(NAME_NAME, segment.toString())
.put(TYPE_NAME, getType(segment));
if (segment instanceof UriResourceEntitySet) {
putParameters(node, KEYS_NAME, ((UriResourceEntitySet) segment).getKeyPredicates());
putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceEntitySet) segment).getTypeFilterOnCollection());
putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceEntitySet) segment).getTypeFilterOnEntry());
} else if (segment instanceof UriResourceNavigation) {
putParameters(node, KEYS_NAME, ((UriResourceNavigation) segment).getKeyPredicates());
putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceNavigation) segment).getTypeFilterOnCollection());
putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceNavigation) segment).getTypeFilterOnEntry());
} else if (segment instanceof UriResourceFunction) {
putParameters(node, PARAMETERS_NAME, ((UriResourceFunction) segment).getParameters());
putParameters(node, KEYS_NAME, ((UriResourceFunction) segment).getKeyPredicates());
putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceFunction) segment).getTypeFilterOnCollection());
putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceFunction) segment).getTypeFilterOnEntry());
} else if (segment instanceof UriResourceIt) {
putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceIt) segment).getTypeFilterOnCollection());
putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceIt) segment).getTypeFilterOnEntry());
} else if (segment instanceof UriResourceSingleton) {
putType(node, TYPE_FILTER_NAME, ((UriResourceSingleton) segment).getEntityTypeFilter());
} else if (segment instanceof UriResourceComplexProperty) {
putType(node, TYPE_FILTER_NAME, ((UriResourceComplexProperty) segment).getComplexTypeFilter());
}
segments.add(node);
} else {
segments.add(nodeFactory.objectNode()
.put(NODE_TYPE_NAME, segment.getKind().toString())
.put(NAME_NAME, segment.toString())
.putNull(TYPE_NAME));
}
}
return result;
}
private void putType(ObjectNode node, final String name, final EdmType type) {
if (type != null) {
node.put(name, type.getFullQualifiedName().getFullQualifiedNameAsString());
}
}
private void putParameters(ObjectNode node, final String name, final List<UriParameter> parameters) {
if (!parameters.isEmpty()) {
ObjectNode parametersNode = node.putObject(name);
for (final UriParameter parameter : parameters) {
parametersNode.put(parameter.getName(),
parameter.getText() == null ? parameter.getAlias() : parameter.getText());
}
}
}
@Override
public JsonNode visitAlias(final String aliasName) throws ExpressionVisitException, ODataApplicationException {
return nodeFactory.objectNode()
.put(NODE_TYPE_NAME, ALIAS_NAME)
.put(ALIAS_NAME, aliasName);
}
@Override
public JsonNode visitTypeLiteral(final EdmType type) throws ExpressionVisitException, ODataApplicationException {
return nodeFactory.objectNode()
.put(NODE_TYPE_NAME, TYPE_NAME)
.put(TYPE_NAME, getTypeString(type));
}
@Override
public JsonNode visitLambdaReference(final String variableName)
throws ExpressionVisitException, ODataApplicationException {
return nodeFactory.objectNode()
.put(NODE_TYPE_NAME, LAMBDA_REFERENCE_NAME)
.put(NAME_NAME, variableName);
}
@Override
public JsonNode visitEnum(final EdmEnumType type, final List<String> enumValues)
throws ExpressionVisitException, ODataApplicationException {
ObjectNode result = nodeFactory.objectNode()
.put(NODE_TYPE_NAME, ENUM_NAME)
.put(TYPE_NAME, getTypeString(type));
ArrayNode values = result.putArray(VALUES_NAME);
if (enumValues != null) {
for (final String enumValue : enumValues) {
values.add(enumValue);
}
}
return result;
}
private String getType(final UnaryOperatorKind operator) {
switch (operator) {
case MINUS:
return NUMBER_NAME;
case NOT:
return BOOLEAN_NAME;
}
return UNKNOWN_NAME;
}
private String getType(final MethodKind methodCall) {
switch (methodCall) {
case STARTSWITH:
case CONTAINS:
case ENDSWITH:
case ISOF:
case GEOINTERSECTS:
return BOOLEAN_NAME;
case INDEXOF:
case LENGTH:
case ROUND:
case FLOOR:
case CEILING:
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
case SECOND:
case FRACTIONALSECONDS:
case TOTALOFFSETMINUTES:
case TOTALSECONDS:
case GEODISTANCE:
case GEOLENGTH:
return NUMBER_NAME;
case CONCAT:
case SUBSTRING:
case TOLOWER:
case TOUPPER:
case TRIM:
return STRING_NAME;
case DATE:
return DATE_NAME;
case TIME:
return TIME_NAME;
case MAXDATETIME:
case MINDATETIME:
case NOW:
return DATETIMEOFFSET_NAME;
case CAST:
return UNKNOWN_NAME;
}
return UNKNOWN_NAME;
}
private String getType(final BinaryOperatorKind operator) {
switch (operator) {
case MUL:
case DIV:
case MOD:
case ADD:
case SUB:
return NUMBER_NAME;
case HAS:
case GT:
case GE:
case LT:
case LE:
case EQ:
case NE:
case AND:
case OR:
case IN:
return BOOLEAN_NAME;
}
return UNKNOWN_NAME;
}
private String getTypeString(final EdmType type) {
return type == null ? null : type.getFullQualifiedName().getFullQualifiedNameAsString();
}
private String getType(final UriResource segment) {
final EdmType type = segment instanceof UriResourcePartTyped ? ((UriResourcePartTyped) segment).getType() : null;
return type == null ? UNKNOWN_NAME : type.getFullQualifiedName().getFullQualifiedNameAsString();
}
@Override
public JsonNode visitBinaryOperator(BinaryOperatorKind operator, JsonNode left, List<JsonNode> right)
throws ExpressionVisitException, ODataApplicationException {
ObjectNode result = nodeFactory.objectNode()
.put(NODE_TYPE_NAME, BINARY_NAME)
.put(OPERATOR_NAME, operator.toString())
.put(TYPE_NAME, getType(operator));
result.set(LEFT_NODE_NAME, left);
ArrayNode jsonExprs = result.putArray(RIGHT_NODE_NAME);
for (final JsonNode exp : right) {
jsonExprs.add(exp);
}
return result;
}
}