@Override public Slot<ColumnView> visitSymbol(SymbolNode symbolNode) { // Check for recursion: are we in the process of evaluating // this symbol? Trying to do so again will lead to an infinite // loop and a StackOverflowException. if(evaluationStack.contains(symbolNode)) { return batch.addEmptyColumn(filterLevel, rootFormClass); } evaluationStack.push(symbolNode); try { Collection<NodeMatch> nodes = resolver.resolveSymbol(symbolNode); LOGGER.finer(symbolNode + " matched to " + nodes); return addColumn(nodes); } catch (AmbiguousSymbolException | SymbolNotFoundException e) { // Ambiguous or unknown symbols should result in an empty column, not full failure LOGGER.log(Level.WARNING, e.getMessage(), e); return batch.addEmptyColumn(filterLevel, rootFormClass); } finally { evaluationStack.pop(); } }
private Slot<ColumnView> addColumn(Collection<NodeMatch> nodes) { // Recursively expand any calculated fields List<Slot<ColumnView>> expandedNodes = Lists.newArrayList(); for (NodeMatch node : nodes) { switch (node.getType()) { case FORM_NAME: case RECORD_ID: case FORM_ID: expandedNodes.add(batch.addColumn(filterLevel, node)); break; case FIELD: if (node.isCalculated()) { expandedNodes.add(expandCalculatedField(node)); } else { expandedNodes.add(batch.addColumn(filterLevel, node)); } break; } } if(expandedNodes.isEmpty()) { return batch.addEmptyColumn(filterLevel, rootFormClass); } else if(expandedNodes.size() == 1) { return expandedNodes.get(0); } else { return new FunctionCallSlot(CoalesceFunction.INSTANCE, expandedNodes); } }
@Override public Slot<ColumnView> visitFunctionCall(final FunctionCallNode call) { if(call.getFunction() instanceof ColumnFunction) { if(call.getArguments().isEmpty()) { return createNullaryFunctionCall(call); } else { return createFunctionCall(call); } } else if(call.getFunction() instanceof BoundingBoxFunction) { FormulaNode geometry = call.getArgument(0); Collection<NodeMatch> nodes; if(geometry instanceof SymbolNode) { nodes = resolver.resolveSymbol(((SymbolNode) geometry)); } else if(geometry instanceof CompoundExpr) { nodes = resolver.resolveCompoundExpr((CompoundExpr) geometry); } else { throw new QuerySyntaxException("Function " + call.getFunction().getId() + " can only be applied" + " to an argument of type GeoArea."); } return addColumn(nodes.stream().map(n -> n.withComponent(call.getFunction().getId())).collect(Collectors.toList())); } else { throw new UnsupportedOperationException("TODO: " + call.getFunction().getId()); } }
private Slot<ColumnView> expandCalculatedField(NodeMatch node) { try { Slot<ColumnView> calculation = evaluateExpression(node.getCalculation()); FormulaNode relevanceFormula = tryParseRelevance(node); if(relevanceFormula == null) { return calculation; } else { return new MemoizedSlot2<>(calculation, evaluateExpression(relevanceFormula), new BiFunction<ColumnView, ColumnView, ColumnView>() { @Override public ColumnView apply(ColumnView calculation, ColumnView relevance) { return new RelevanceViewMask(calculation, relevance); } }); } } catch (FormulaException e) { LOGGER.log(Level.WARNING, "Exception in calculated field " + node.getFormClass().getId() + "." + node.getFieldComponent() + " = " + node.getCalculation() + ": " + e.getMessage(), e); return batch.addEmptyColumn(filterLevel, node.getFormClass()); } }
@Override public Slot<ColumnView> visitCompoundExpr(CompoundExpr compoundExpr) { return addColumn(resolver.resolveCompoundExpr(compoundExpr)); }