org.openiot.ui.request.definition.web.sparql.SparqlGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.openiot.ui.request.definition.web.sparql.SparqlGenerator.java

Source

/**
 *    Copyright (c) 2011-2014, OpenIoT
 *   
 *    This file is part of OpenIoT.
 *
 *    OpenIoT 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, version 3 of the License.
 *
 *    OpenIoT 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 OpenIoT.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     Contact: OpenIoT mailto: info@openiot.eu
 */

package org.openiot.ui.request.definition.web.sparql;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Level;

import org.openiot.ui.request.commons.interfaces.GraphModel;
import org.openiot.ui.request.commons.logging.LoggerService;
import org.openiot.ui.request.commons.nodes.base.AbstractGraphNodeVisitor;
import org.openiot.ui.request.commons.nodes.enums.EndpointType;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNode;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNodeConnection;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNodeEndpoint;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNodeProperty;
import org.openiot.ui.request.definition.web.model.nodes.impl.sinks.LineChart;
import org.openiot.ui.request.definition.web.model.nodes.impl.sinks.Passthrough;
import org.openiot.ui.request.definition.web.model.nodes.impl.sinks.Pie;
import org.openiot.ui.request.definition.web.model.nodes.impl.sources.GenericSource;
import org.openiot.ui.request.definition.web.sparql.nodes.base.AbstractSparqlNode;
import org.openiot.ui.request.definition.web.sparql.nodes.base.AggregateExpression;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Comment;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Expression;
import org.openiot.ui.request.definition.web.sparql.nodes.base.From;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Group;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Order;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Root;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Scope;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Select;
import org.openiot.ui.request.definition.web.sparql.nodes.base.SensorSelectExpression;
import org.openiot.ui.request.definition.web.sparql.nodes.base.Where;

/**
 * 
 * @author Achilleas Anagnostopoulos (aanag) email: aanag@sensap.eu
 */
public class SparqlGenerator extends AbstractGraphNodeVisitor {

    private GraphModel model;
    // Nodes
    private Root primaryRootNode;
    private Comment primaryCommentNode;
    private Select primarySelectNode;
    private From primaryFromNode;
    private Where primaryWhereNode;
    private Group primaryGroupNode;
    //
    private AbstractSparqlNode subSelectOriginalNode;
    private AbstractSparqlNode subSelectNode;
    private Where subWhereNode;
    private Group subGroupNode;
    private Order subOrderNode;
    private boolean sinkNodeNeedsGeoCoords;
    //
    private GenericSource targetDataSource;
    private GraphNodeEndpoint targetAttribute;
    private Set<String> visitedSensorTypes;
    private Map<GraphNodeProperty, Object> variableMap;
    private Stack<GraphNodeConnection> visitedConnectionGraphStack;
    // Generated code blocks
    private List<String> queryBlocks;

    // -------------------------------------------------------------------------
    // Public API
    // -------------------------------------------------------------------------

    public SparqlGenerator() {
        this.visitedConnectionGraphStack = new Stack<GraphNodeConnection>();
        this.visitedSensorTypes = new LinkedHashSet<String>();
        this.variableMap = new LinkedHashMap<GraphNodeProperty, Object>();
        this.queryBlocks = new ArrayList<String>();
    }

    public List<String> generateQueriesForNodeEndpoints(GraphModel model, GraphNode visualizerNode) {
        this.model = model;
        this.queryBlocks.clear();
        reset();

        // Generate code for passed node
        visitSink(visualizerNode);

        // Return output
        return this.queryBlocks;
    }

    public Map<GraphNodeProperty, Object> getVariableMap() {
        return variableMap;
    }

    // -------------------------------------------------------------------------
    // Query blocks
    // -------------------------------------------------------------------------

    private void reset() {
        this.visitedConnectionGraphStack.clear();
        this.visitedSensorTypes.clear();
        this.variableMap.clear();

        // Allocate initial nodes
        primaryCommentNode = new Comment();
        primarySelectNode = new Select();
        primaryFromNode = new From(AbstractSparqlNode.GRAPH_DATA_URI);
        primaryWhereNode = new Where();
        primaryGroupNode = new Group();

        primaryRootNode = new Root();
        primaryRootNode.appendToScope(primaryCommentNode);
        primaryRootNode.appendToScope(primarySelectNode);
        primaryRootNode.appendToScope(primaryFromNode);
        primaryRootNode.appendToScope(primaryWhereNode);
        primaryRootNode.appendToScope(primaryGroupNode);
    }

    private void beginQueryBlock(GraphNode visNode, int tupleIndex, int totalTuples) {
        reset();
        this.visitedConnectionGraphStack.clear();

        primaryCommentNode.appendComment("[" + tupleIndex + " / " + totalTuples + "] visualization type: '"
                + visNode.getLabel() + "' and sensors of type:");
        this.visitedSensorTypes.clear();
    }

    private void endQueryBlock() {
        // Setup comments
        primaryCommentNode
                .appendComment("\t - " + org.apache.commons.lang3.StringUtils.join(visitedSensorTypes, "\n#\t - "));
        primaryCommentNode.appendComment("Generated: " + (new Date()));
        if (this.variableMap.size() > 0) {
            primaryCommentNode.appendComment("Using " + this.variableMap.size() + " variable"
                    + (this.variableMap.size() > 1 ? "s" : "") + ":");
            for (Map.Entry<GraphNodeProperty, Object> entry : variableMap.entrySet()) {
                primaryCommentNode.appendComment(
                        "- " + entry.getKey().getVariableName() + " (default value: " + entry.getValue() + ")");
            }
        }

        queryBlocks.add(primaryRootNode.generate());
    }

    private void generateAttributeSubQueryWhereCode(GenericSource sensorNode, GraphNodeEndpoint attributeEndpoint) {

        // Encode attribute selection
        if (attributeEndpoint != null) {
            subWhereNode.appendToScope(new Expression("?" + sensorNode.getUID()
                    + "_record <http://openiot.eu/ontology/ns/value> ?" + attributeEndpoint.getUID() + " ."));
            subWhereNode.appendToScope(new Expression(
                    "?" + sensorNode.getUID() + "_record <http://www.w3.org/2000/01/rdf-schema#label> '"
                            + attributeEndpoint.getUserData() + "' ."));
        }
        subWhereNode.appendToScope(new Expression(
                "?" + sensorNode.getUID() + "_record <http://purl.oclc.org/NET/ssnx/ssn#observationResultTime> ?"
                        + sensorNode.getUID() + "_recordTime ."));

        // Encode sensor selection expression
        GraphNodeProperty prop = sensorNode.getPropertyByName("LAT");
        Object latQuery = sensorNode.getPropertyValueMap().get("LAT");
        if (prop.isVariable()) {
            defineVariable(prop, latQuery);
            latQuery = "#" + prop.getVariableName() + "#";
        }
        prop = sensorNode.getPropertyByName("LON");
        Object lonQuery = sensorNode.getPropertyValueMap().get("LON");
        if (prop.isVariable()) {
            defineVariable(prop, lonQuery);
            lonQuery = "#" + prop.getVariableName() + "#";
        }
        prop = sensorNode.getPropertyByName("RADIUS");
        Object radiusQuery = sensorNode.getPropertyValueMap().get("RADIUS");
        if (prop.isVariable()) {
            defineVariable(prop, radiusQuery);
            radiusQuery = "#" + prop.getVariableName() + "#";
        }

        subWhereNode.appendToScope(new SensorSelectExpression(sensorNode.getUID(), sensorNode.getLabel(), latQuery,
                lonQuery, radiusQuery, this.sinkNodeNeedsGeoCoords));
    }

    private void defineVariable(GraphNodeProperty property, Object defaultValue) {
        this.variableMap.put(property, defaultValue);
    }

    // -------------------------------------------------------------------------
    // Generic non-sink node visitors
    // -------------------------------------------------------------------------
    @Override
    public void defaultVisit(GraphNode node) {
        if (node instanceof GenericSource) {
            visit((GenericSource) node);
            return;
        }

        LoggerService.log(Level.SEVERE,
                "[SparqlGenerator] Default visitor called for node of class: " + node.getClass().getSimpleName());
    }

    public void visit(GenericSource node) {
        visitedSensorTypes.add(node.getLabel());

        // Examine the connection endpoint that lead us to the sensor
        GraphNodeConnection outgoingConnection = visitedConnectionGraphStack.peek();
        GraphNodeEndpoint sourceEndpoint = outgoingConnection.getSourceEndpoint();

        // Remember the sensor we landed to as well as the target attribute
        // endpoint
        targetDataSource = node;
        targetAttribute = sourceEndpoint;

        // Encode selection query
        subSelectNode.appendToScope(new Expression("?" + sourceEndpoint.getUID()));

        // Generate nested where clause for selected attribute
        this.generateAttributeSubQueryWhereCode(targetDataSource, targetAttribute);

        // If sensor node has an incoming filter node connection
        // visit it and append any additional filters
        for (GraphNodeEndpoint endpoint : node.getEndpointDefinitions()) {
            if (endpoint.getType().equals(EndpointType.Output)) {
                continue;
            }

            List<GraphNodeConnection> incomingConnections = model.findGraphEndpointConnections(endpoint);
            for (GraphNodeConnection connection : incomingConnections) {
                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getSourceNode());
                this.visitedConnectionGraphStack.pop();
            }
        }
    }

    // -------------------------------------------------------------------------
    // Sink node Visitors
    // -------------------------------------------------------------------------

    public void visitSink(GraphNode node) {
        // Only process the viz node that was passed to the
        // 'generateQueriesForNodeEndpoints' method
        if (!node.getType().equals("SINK")) {
            return;
        }

        this.sinkNodeNeedsGeoCoords = false;

        if (node instanceof LineChart) {
            visitLineChartSink((LineChart) node);
        } else if (node instanceof Passthrough) {
            visitPassthroughSink((Passthrough) node);
        } else if (node instanceof org.openiot.ui.request.definition.web.model.nodes.impl.sinks.Map) {
            visitMapSink((org.openiot.ui.request.definition.web.model.nodes.impl.sinks.Map) node);
        } else if (node instanceof Pie) {
            visitPieSink((Pie) node);
        } else {
            visitGenericSink(node);
        }
    }

    public void visitGenericSink(GraphNode node) {

        beginQueryBlock(node, 1, 1);

        // Visit incoming neighbors
        for (GraphNodeEndpoint endpoint : node.getEndpointDefinitions()) {
            if (endpoint.getType().equals(EndpointType.Output)) {
                continue;
            }

            List<GraphNodeConnection> incomingConnections = model.findGraphEndpointConnections(endpoint);
            for (GraphNodeConnection connection : incomingConnections) {

                // Generate primary select
                primarySelectNode.appendToScope(new Expression("?" + endpoint.getLabel()));

                // Generate new scope for assembling the selection queries
                // *unless* this is a grp_Date scope where
                // we re use the current
                Scope subScope = new Scope();
                primaryWhereNode.appendToScope(subScope);

                // Generate subquery helpers
                subSelectNode = subSelectOriginalNode = new Select();
                subWhereNode = new Where();
                subGroupNode = new Group();
                subScope.appendToScope(subSelectNode);
                subScope.appendToScope(subWhereNode);
                subScope.appendToScope(subGroupNode);

                // Explore graph till we reach a sensor node.
                targetDataSource = null;
                targetAttribute = null;
                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getSourceNode());

                // Append the endpoint label to the end of the generated select
                // statement
                subSelectNode.appendToScope(new Expression("AS ?" + endpoint.getLabel()));

                //
                this.visitedConnectionGraphStack.pop();
            }
        }

        endQueryBlock();
    }

    public void visitMapSink(org.openiot.ui.request.definition.web.model.nodes.impl.sinks.Map node) {
        this.sinkNodeNeedsGeoCoords = true;
        beginQueryBlock(node, 1, 1);

        // Visit incoming neighbors
        for (GraphNodeEndpoint endpoint : node.getEndpointDefinitions()) {
            if (endpoint.getType().equals(EndpointType.Output)) {
                continue;
            }

            // If this is a LAT or LON connection, skip it because we will
            // include it
            // together with the VALUE endpoint
            if (endpoint.getScope().contains("geo_lat") || endpoint.getScope().contains("geo_lon")) {
                continue;
            }

            LoggerService.log(Level.INFO, "Visit endpoint with scope: " + endpoint.getScope());

            List<GraphNodeConnection> incomingConnections = model.findGraphEndpointConnections(endpoint);
            for (GraphNodeConnection connection : incomingConnections) {

                // Generate primary select (also include LAT/LON fields)
                primarySelectNode.appendToScope(new Expression("?" + endpoint.getLabel()));
                primarySelectNode.appendToScope(new Expression("?LAT"));
                primarySelectNode.appendToScope(new Expression("?LON"));

                // Generate new scope for assembling the selection queries
                // *unless* this is a grp_Date scope where
                // we re use the current
                Scope subScope = new Scope();
                primaryWhereNode.appendToScope(subScope);

                // Generate subquery helpers
                subSelectNode = subSelectOriginalNode = new Select();
                subWhereNode = new Where();
                subGroupNode = new Group();
                subScope.appendToScope(subSelectNode);
                subScope.appendToScope(subWhereNode);
                subScope.appendToScope(subGroupNode);

                // Explore graph till we reach a sensor node.
                targetDataSource = null;
                targetAttribute = null;
                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getSourceNode());

                // Append the endpoint label to the end of the generated select
                // statement
                subSelectNode.appendToScope(new Expression("AS ?" + endpoint.getLabel()));
                subSelectNode.appendToScope(new Expression("?" + targetDataSource.getUID() + "_lat AS ?LAT"));
                subSelectNode.appendToScope(new Expression("?" + targetDataSource.getUID() + "_lon AS ?LON"));

                //
                this.visitedConnectionGraphStack.pop();
            }
        }

        endQueryBlock();
    }

    public void visitLineChartSink(LineChart node) {
        String xAxisType = (String) node.getPropertyValueMap().get("X_AXIS_TYPE");

        // Group queries for xy tuples
        int seriesCount = Integer.valueOf((String) node.getPropertyValueMap().get("SERIES"));
        for (int i = 0; i < seriesCount; i++) {
            // Start a new code block for each series
            beginQueryBlock(node, i + 1, seriesCount);

            GraphNodeEndpoint xEndpoint = node.getEndpointByLabel("x" + (i + 1));
            GraphNodeEndpoint yEndpoint = node.getEndpointByLabel("y" + (i + 1));

            // Follow Y axis value
            for (GraphNodeConnection connection : model.findGraphEndpointConnections(yEndpoint)) {

                Scope subScope = new Scope();
                primaryWhereNode.appendToScope(subScope);

                // Generate subquery helpers
                subSelectNode = subSelectOriginalNode = new Select();
                subWhereNode = new Where();
                subGroupNode = new Group();
                subOrderNode = new Order();
                subScope.appendToScope(subSelectNode);
                subScope.appendToScope(subWhereNode);
                subScope.appendToScope(subGroupNode);
                subScope.appendToScope(subOrderNode);

                // Explore graph till we reach a sensor node.
                targetDataSource = null;
                targetAttribute = null;
                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getSourceNode());
                this.visitedConnectionGraphStack.pop();

                subSelectNode.appendToScope(new Expression("AS ?" + yEndpoint.getLabel()));
                primarySelectNode.appendToScope(new Expression("?" + yEndpoint.getLabel()));
            }

            // Process x axis endpoint
            if (xEndpoint != null) {
                for (GraphNodeConnection connection : model.findGraphEndpointConnections(xEndpoint)) {
                    if (xAxisType.equals("Date (observation)")) {
                        String timeComponent = connection.getSourceEndpoint().getLabel().replace("grp_recordTime_",
                                "");
                        // Note: We need to apply an aggregation function to
                        // timestamp components
                        // for the value grouping to work
                        subSelectNode.appendToScope(new Expression(
                                "AVG( fn:" + timeComponent + "-from-dateTime(?" + targetDataSource.getUID()
                                        + "_recordTime) ) AS ?" + xEndpoint.getLabel() + "_" + timeComponent));
                        primarySelectNode
                                .appendToScope(new Expression("?" + xEndpoint.getLabel() + "_" + timeComponent));
                    } else {
                        Scope subScope = new Scope();
                        primaryWhereNode.appendToScope(subScope);

                        // Generate subquery helpers
                        subSelectNode = subSelectOriginalNode = new Select();
                        subWhereNode = new Where();
                        subGroupNode = new Group();
                        subOrderNode = new Order();
                        subScope.appendToScope(subSelectNode);
                        subScope.appendToScope(subWhereNode);
                        subScope.appendToScope(subGroupNode);
                        subScope.appendToScope(subOrderNode);

                        // Explore graph till we reach a sensor node.
                        targetDataSource = null;
                        targetAttribute = null;
                        this.visitedConnectionGraphStack.push(connection);
                        this.visitViaReflection(connection.getSourceNode());
                        this.visitedConnectionGraphStack.pop();

                        subSelectNode.appendToScope(new Expression("AS ?" + xEndpoint.getLabel()));
                        primarySelectNode.appendToScope(new Expression("?" + xEndpoint.getLabel()));
                    }
                }
            }

            endQueryBlock();
        }
    }

    public void visitPassthroughSink(Passthrough node) {

        // Generate one query per attribute
        int attrCount = Integer.valueOf(node.getPropertyValueMap().get("ATTRIBUTES").toString());
        for (int i = 0; i < attrCount; i++) {
            // Start a new code block for each attribute
            beginQueryBlock(node, i + 1, attrCount);

            GraphNodeEndpoint attrEndpoint = node.getEndpointByLabel("attr" + (i + 1));

            // Follow attr value
            for (GraphNodeConnection connection : model.findGraphEndpointConnections(attrEndpoint)) {

                Scope subScope = new Scope();
                primaryWhereNode.appendToScope(subScope);

                // Generate subquery helpers
                subSelectNode = subSelectOriginalNode = new Select();
                subWhereNode = new Where();
                subGroupNode = new Group();
                subOrderNode = new Order();
                subScope.appendToScope(subSelectNode);
                subScope.appendToScope(subWhereNode);
                subScope.appendToScope(subGroupNode);
                subScope.appendToScope(subOrderNode);

                // Explore graph till we reach a sensor node.
                targetDataSource = null;
                targetAttribute = null;
                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getSourceNode());
                this.visitedConnectionGraphStack.pop();

                subSelectNode.appendToScope(new Expression("AS ?" + attrEndpoint.getLabel()));
                primarySelectNode.appendToScope(new Expression("?" + attrEndpoint.getLabel()));
            }

            endQueryBlock();
        }
    }

    public void visitPieSink(Pie node) {

        // Generate one query per attribute
        int seriesCount = Integer.valueOf((String) node.getPropertyValueMap().get("SERIES"));
        for (int i = 0; i < seriesCount; i++) {
            // Start a new code block for each series
            beginQueryBlock(node, i + 1, seriesCount);

            GraphNodeEndpoint attrEndpoint = node.getEndpointByLabel("y" + (i + 1));

            // Follow attr value
            for (GraphNodeConnection connection : model.findGraphEndpointConnections(attrEndpoint)) {

                Scope subScope = new Scope();
                primaryWhereNode.appendToScope(subScope);

                // Generate subquery helpers
                subSelectNode = subSelectOriginalNode = new Select();
                subWhereNode = new Where();
                subGroupNode = new Group();
                subOrderNode = new Order();
                subScope.appendToScope(subSelectNode);
                subScope.appendToScope(subWhereNode);
                subScope.appendToScope(subGroupNode);
                subScope.appendToScope(subOrderNode);

                // Explore graph till we reach a sensor node.
                targetDataSource = null;
                targetAttribute = null;
                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getSourceNode());
                this.visitedConnectionGraphStack.pop();

                subSelectNode.appendToScope(new Expression("AS ?" + attrEndpoint.getLabel()));
                primarySelectNode.appendToScope(new Expression("?" + attrEndpoint.getLabel()));
            }

            endQueryBlock();
        }
    }

    private void visitIncomingConnections(GraphNode destinationNode) {
        for (GraphNodeEndpoint endpoint : destinationNode.getEndpointDefinitions()) {
            if (endpoint.getType().equals(EndpointType.Output)) {
                continue;
            }

            List<GraphNodeConnection> incomingConnections = model.findGraphEndpointConnections(endpoint);
            for (GraphNodeConnection connection : incomingConnections) {
                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getSourceNode());
                this.visitedConnectionGraphStack.pop();
            }
        }
    }

    // -------------------------------------------------------------------------
    // Filter node visitors
    // -------------------------------------------------------------------------
    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.filters.SelectionFilter node) {

        // Visit all outgoing connections except the one connecting to the
        // sensor
        for (GraphNodeEndpoint endpoint : node.getEndpointDefinitions()) {
            if (endpoint.getType().equals(EndpointType.Input)) {
                continue;
            }

            List<GraphNodeConnection> incomingConnections = model.findGraphEndpointConnections(endpoint);
            for (GraphNodeConnection connection : incomingConnections) {
                if (connection.getDestinationNode() instanceof GenericSource) {
                    continue;
                }

                this.visitedConnectionGraphStack.push(connection);
                this.visitViaReflection(connection.getDestinationNode());
                this.visitedConnectionGraphStack.pop();
            }
        }
    }

    protected void generateTimeGroups(List<String> groupList) {
    }

    @SuppressWarnings("unchecked")
    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.filters.Group node) {

        // Unwind the stack till we find the sink
        GraphNodeEndpoint ourEndpoint = null;
        ListIterator<GraphNodeConnection> connectionIt = visitedConnectionGraphStack
                .listIterator(visitedConnectionGraphStack.size());
        ourEndpoint = connectionIt.previous().getSourceEndpoint();

        // Get attributes endpoint
        GraphNodeEndpoint attributesEndpoint = node.getEndpointByLabel("ATTRIBUTES");
        this.targetDataSource = (GenericSource) model.findGraphEndpointConnections(attributesEndpoint).get(0)
                .getSourceNode();

        // Generate groups
        List<String> groupList = (List<String>) node.getPropertyValueMap().get("GROUPS");
        for (String group : groupList) {
            String timeComponent = group.replace("recordTime_", "");
            subGroupNode.appendToScope(new Expression(
                    "( fn:" + timeComponent + "-from-dateTime(?" + targetDataSource.getUID() + "_recordTime) )"));
            subOrderNode.appendToScope(new Expression(
                    "( fn:" + timeComponent + "-from-dateTime(?" + targetDataSource.getUID() + "_recordTime) )"));
        }

        // Follow the connection that matches our endpoint label (ie the
        // currently grouped property)
        List<GraphNodeConnection> incomingConnections = model.findGraphEndpointConnections(attributesEndpoint);
        String attrName = ourEndpoint.getLabel().replace("grp_", "");
        for (GraphNodeConnection connection : incomingConnections) {
            if (!attrName.equals(connection.getSourceEndpoint().getLabel())) {
                continue;
            }

            this.visitedConnectionGraphStack.push(connection);
            this.visitViaReflection(connection.getSourceNode());
            this.visitedConnectionGraphStack.pop();
        }
    }

    // -------------------------------------------------------------------------
    // Comparator node visitors
    // -------------------------------------------------------------------------

    public void visit(
            org.openiot.ui.request.definition.web.model.nodes.impl.comparators.CompareAbsoluteDateTime node) {
        // Generate date string in appropriate format.
        // xsd:datetime type formats dates a bit differently than the pattern
        // used by simple date
        // format (separates hours and mins of the timezone with a colon) so we
        // need to patch it here

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        Date cmpValue = (Date) node.getPropertyValueMap().get("CMP_VALUE");
        String formattedDate = sdf.format(cmpValue);
        formattedDate = formattedDate.substring(0, formattedDate.length() - 2) + ":"
                + formattedDate.substring(formattedDate.length() - 2);

        GraphNodeProperty prop = node.getPropertyByName("CMP_VALUE");
        if (prop.isVariable()) {
            defineVariable(prop, formattedDate);
            formattedDate = "#" + prop.getVariableName() + "#";
        }

        subWhereNode.appendToScope(new Expression("FILTER (?" + targetDataSource.getUID() + "_recordTime "
                + node.getPropertyValueMap().get("OPERATOR") + " \"" + formattedDate + "\"^^xsd:date )."));
    }

    public void visit(
            org.openiot.ui.request.definition.web.model.nodes.impl.comparators.CompareRelativeDateTime node) {

        long scaler = 1L;
        String unit = (String) node.getPropertyValueMap().get("CMP_VALUE_UNIT");
        if ("SECOND(S)".equals(unit)) {
            scaler = 1L;
        } else if ("MINUTE(S)".equals(unit)) {
            scaler = 60L;
        } else if ("HOUR(S)".equals(unit)) {
            scaler = 60 * 60L;
        } else if ("DAY(S)".equals(unit)) {
            scaler = 24 * 60 * 60L;
        } else if ("MONTH(S)".equals(unit)) {
            scaler = 30 * 24 * 60 * 60L;
        } else if ("YEAR(S)".equals(unit)) {
            scaler = 365 * 24 * 60 * 60L;
        }
        String cmpValue = "" + Long.valueOf(node.getPropertyValueMap().get("CMP_VALUE").toString()) * scaler;

        GraphNodeProperty prop = node.getPropertyByName("CMP_VALUE");
        if (prop.isVariable()) {
            defineVariable(prop, cmpValue);
            cmpValue = "#" + prop.getVariableName() + "#";
        }

        subWhereNode.appendToScope(new Expression("FILTER( bif:datediff('second', xsd:dateTime(str(?"
                + targetDataSource.getUID() + "_recordTime)), bif:curdatetime()) "
                + node.getPropertyValueMap().get("OPERATOR") + " " + cmpValue + ")."));
    }

    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.comparators.BetweenDateTime node) {
        // Generate date string in appropriate format.
        // xsd:datetime type formats dates a bit differently than the pattern
        // used by simple date
        // format (separates hours and mins of the timezone with a colon) so we
        // need to patch it here
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        Date cmpFromValue = (Date) node.getPropertyValueMap().get("CMP_VALUE1");
        String formattedFromDate = sdf.format(cmpFromValue);
        formattedFromDate = formattedFromDate.substring(0, formattedFromDate.length() - 2) + ":"
                + formattedFromDate.substring(formattedFromDate.length() - 2);
        Date cmpToValue = (Date) node.getPropertyValueMap().get("CMP_VALUE2");
        String formattedToDate = sdf.format(cmpToValue);
        formattedToDate = formattedToDate.substring(0, formattedToDate.length() - 2) + ":"
                + formattedToDate.substring(formattedToDate.length() - 2);

        GraphNodeProperty prop = node.getPropertyByName("CMP_VALUE1");
        if (prop.isVariable()) {
            defineVariable(prop, formattedFromDate);
            formattedFromDate = "#" + prop.getVariableName() + "#";
        }

        prop = node.getPropertyByName("CMP_VALUE2");
        if (prop.isVariable()) {
            defineVariable(prop, formattedToDate);
            formattedToDate = "#" + prop.getVariableName() + "#";
        }

        subWhereNode.appendToScope(new Expression("FILTER (?" + targetDataSource.getUID() + "_recordTime >= \""
                + formattedFromDate + "\"^^xsd:date AND ?" + targetDataSource.getUID() + "_recordTime <= \""
                + formattedToDate + "\"^^xsd:date )."));
    }

    // -------------------------------------------------------------------------
    // Aggregator node visitors
    // -------------------------------------------------------------------------
    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.aggegators.Min node) {
        AbstractSparqlNode current = subSelectNode;
        subSelectNode = subSelectNode.appendToScope(new AggregateExpression("MIN"));
        visitIncomingConnections(node);
        subSelectNode = current;
    }

    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.aggegators.Max node) {
        AbstractSparqlNode current = subSelectNode;
        subSelectNode = subSelectNode.appendToScope(new AggregateExpression("MAX"));
        visitIncomingConnections(node);
        subSelectNode = current;
    }

    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.aggegators.Count node) {
        AbstractSparqlNode current = subSelectNode;
        subSelectNode = subSelectNode.appendToScope(new AggregateExpression("COUNT"));
        visitIncomingConnections(node);
        subSelectNode = current;
    }

    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.aggegators.Sum node) {
        AbstractSparqlNode current = subSelectNode;
        subSelectNode = subSelectNode.appendToScope(new AggregateExpression("SUM"));
        visitIncomingConnections(node);
        subSelectNode = current;
    }

    public void visit(org.openiot.ui.request.definition.web.model.nodes.impl.aggegators.Average node) {
        AbstractSparqlNode current = subSelectNode;
        subSelectNode = subSelectNode.appendToScope(new AggregateExpression("AVG"));
        visitIncomingConnections(node);
        subSelectNode = current;
    }
}