net.sf.jasperreports.engine.json.expression.member.evaluation.ObjectKeyExpressionEvaluator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jasperreports Show documentation
Show all versions of jasperreports Show documentation
Free Java Reporting Library
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see .
*/
package net.sf.jasperreports.engine.json.expression.member.evaluation;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.MissingNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import net.sf.jasperreports.engine.json.JRJsonNode;
import net.sf.jasperreports.engine.json.JsonNodeContainer;
import net.sf.jasperreports.engine.json.expression.EvaluationContext;
import net.sf.jasperreports.engine.json.expression.member.MemberExpression;
import net.sf.jasperreports.engine.json.expression.member.ObjectKeyExpression;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Narcis Marcu ([email protected])
*/
public class ObjectKeyExpressionEvaluator extends AbstractMemberExpressionEvaluator {
private static final Log log = LogFactory.getLog(ObjectKeyExpressionEvaluator.class);
private ObjectKeyExpression expression;
private boolean isCalledFromFilter;
private Pattern fieldNamePattern;
public ObjectKeyExpressionEvaluator(EvaluationContext evaluationContext, ObjectKeyExpression expression) {
this(evaluationContext, expression, false);
}
public ObjectKeyExpressionEvaluator(EvaluationContext evaluationContext,
ObjectKeyExpression expression, boolean isCalledFromFilter) {
super(evaluationContext);
this.expression = expression;
this.isCalledFromFilter = isCalledFromFilter;
if (!expression.isWildcard() && expression.isComplex()) {
this.fieldNamePattern = Pattern.compile(expression.getObjectKey());
}
}
@Override
public JsonNodeContainer evaluate(JsonNodeContainer contextNode) {
List nodes = contextNode.getNodes();
if (log.isDebugEnabled()) {
log.debug("---> evaluating expression [" + expression +
"] on a node with (size: " + contextNode.getSize() +
", cSize: " + contextNode.getContainerSize() + ")");
}
JsonNodeContainer result = new JsonNodeContainer();
for (JRJsonNode node: nodes) {
List evaluatedNodes = singleEval(node);
if (evaluatedNodes.size() > 0) {
result.addNodes(evaluatedNodes);
}
}
if (result.getSize() > 0) {
return result;
}
return null;
}
@Override
public MemberExpression getMemberExpression() {
return expression;
}
private List singleEval(JRJsonNode jrJsonNode) {
switch (expression.getDirection()) {
case DOWN:
return goDown(jrJsonNode);
case ANYWHERE_DOWN:
return goAnywhereDown(jrJsonNode);
}
return null;
}
private List goDown(JRJsonNode jrJsonNode) {
if (log.isDebugEnabled()) {
log.debug("going " + MemberExpression.DIRECTION.DOWN + " by " +
(expression.isWildcard() ?
"wildcard" :
"key: [" + expression.getObjectKey() + "]") + " on " + jrJsonNode.getDataNode());
}
List result = new ArrayList<>();
JsonNode dataNode = jrJsonNode.getDataNode();
// advance into object
if (dataNode.isObject()) {
// if wildcard => filter and add all its children(the values for each key) to an arrayNode
if (expression.isWildcard()) {
ArrayNode container = getEvaluationContext().getObjectMapper().createArrayNode();
Iterator> it = dataNode.fields();
while (it.hasNext()) {
JsonNode current = it.next().getValue();
if (applyFilter(jrJsonNode.createChild(current))) {
container.add(current);
}
}
if (container.size() > 0) {
result.add(jrJsonNode.createChild(container));
}
}
// else go down and filter
else {
JRJsonNode deeperNode = goDeeperIntoObjectNode(jrJsonNode, isCalledFromFilter);
if (deeperNode != null) {
result.add(deeperNode);
}
}
}
// advance into array
// when called from filter => keep the array containment
else if (dataNode.isArray()) {
if (expression.isWildcard()) {
result = filterArrayNode(jrJsonNode, (ArrayNode) dataNode, null, isCalledFromFilter);
} else {
result = filterArrayNode(jrJsonNode, (ArrayNode) dataNode, expression.getObjectKey(), isCalledFromFilter);
}
}
return result;
}
private List goAnywhereDown(JRJsonNode jrJsonNode) {
if (log.isDebugEnabled()) {
log.debug("going " + MemberExpression.DIRECTION.ANYWHERE_DOWN + " by " +
(expression.isWildcard() ?
"wildcard" :
"key: [" + expression.getObjectKey() + "]") + " on " + jrJsonNode.getDataNode());
}
List result = new ArrayList<>();
Deque stack = new ArrayDeque<>();
JsonNode initialDataNode = jrJsonNode.getDataNode();
if (log.isDebugEnabled()) {
log.debug("initial stack population with: " + initialDataNode);
}
// populate the stack initially
if (initialDataNode.isArray()) {
for (JsonNode deeper: initialDataNode) {
stack.addLast(jrJsonNode.createChild(deeper));
}
} else {
stack.push(jrJsonNode);
}
while (!stack.isEmpty()) {
JRJsonNode stackNode = stack.pop();
JsonNode stackDataNode = stackNode.getDataNode();
addChildrenToStack(stackNode, stack);
if (log.isDebugEnabled()) {
log.debug("processing stack element: " + stackDataNode);
}
// process the current stack item
if (stackDataNode.isObject()) {
if (log.isDebugEnabled()) {
log.debug("stack element is object; wildcard: " + expression.isWildcard());
}
// if wildcard => only filter the parent; we already added the object keys to the stack
if (expression.isWildcard()) {
if (applyFilter(stackNode)) {
result.add(stackNode);
}
}
// else go down and filter
else {
JRJsonNode deeperNode = goDeeperIntoObjectNode(stackNode, false);
if (deeperNode != null) {
result.add(deeperNode);
}
}
}
else if (stackDataNode.isValueNode() || stackDataNode.isArray()) {
if (log.isDebugEnabled()) {
log.debug("stack element is " + (stackDataNode.isValueNode() ? "value node" : "array") + "; wildcard: " + expression.isWildcard());
}
if (expression.isWildcard()) {
if (applyFilter(stackNode)) {
result.add(stackNode);
}
}
}
}
return result;
}
private JRJsonNode goDeeperIntoObjectNode(JRJsonNode jrJsonNode, boolean keepMissingNode) {
ObjectNode dataNode = (ObjectNode) jrJsonNode.getDataNode();
ArrayNode container = getEvaluationContext().getObjectMapper().createArrayNode();
// A complex expression allows an object key to treated as a REGEX
if (expression.isComplex()) {
Iterator fieldNamesIterator = dataNode.fieldNames();
while (fieldNamesIterator.hasNext()) {
String fieldName = fieldNamesIterator.next();
Matcher fieldNameMatcher = fieldNamePattern.matcher(fieldName);
if (fieldNameMatcher.matches()) {
JsonNode deeperNode = dataNode.path(fieldName);
// if the deeper node is object/value => filter and add it
if (deeperNode.isObject() || deeperNode.isValueNode() || deeperNode.isArray()) {
JRJsonNode child = jrJsonNode.createChild(deeperNode);
if (applyFilter(child)) {
container.add(deeperNode);
}
}
}
}
} else {
JsonNode deeperNode = dataNode.path(expression.getObjectKey());
// if the deeper node is object/value => filter and add it
if (deeperNode.isObject() || deeperNode.isValueNode() || deeperNode.isArray()) {
JRJsonNode child = jrJsonNode.createChild(deeperNode);
if (applyFilter(child)) {
container.add(deeperNode);
}
}
}
if (container.size() > 1) {
return jrJsonNode.createChild(container);
} else if (container.size() == 1) {
return jrJsonNode.createChild(container.get(0));
}
// Filtering expressions need the missing node to check for null
else if (keepMissingNode) {
return jrJsonNode.createChild(MissingNode.getInstance());
}
return null;
}
}