org.dllearner.utilities.QueryUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.dllearner.utilities.QueryUtils.java

Source

/**
 * Copyright (C) 2007 - 2016, Jens Lehmann
 *
 * This file is part of DL-Learner.
 *
 * DL-Learner is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * DL-Learner 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.dllearner.utilities;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.util.mxCellRenderer;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.png.mxPngEncodeParam;
import com.mxgraph.util.png.mxPngImageEncoder;
import com.mxgraph.view.mxStylesheet;
import org.aksw.jena_sparql_api.core.QueryExecutionFactory;
import org.apache.commons.collections15.ListUtils;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.*;
import org.apache.jena.sparql.core.TriplePath;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.syntax.*;
import org.apache.jena.sparql.util.VarUtils;
import org.apache.jena.vocabulary.RDF;
import org.dllearner.kb.sparql.CBDStructureTree;
import org.jgrapht.DirectedGraph;
import org.jgrapht.ext.JGraphXAdapter;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Utilities for SPARQL queries.
 */
public class QueryUtils extends ElementVisitorBase {

    private static final Logger logger = LoggerFactory.getLogger(QueryUtils.class);

    private static final ParameterizedSparqlString superClassesQueryTemplate = new ParameterizedSparqlString(
            "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> " + "SELECT ?sup WHERE {"
                    + "?sub ((rdfs:subClassOf|owl:equivalentClass)|^owl:equivalentClass)+ ?sup .}");

    private Set<Triple> triplePattern;
    private Set<Triple> optionalTriplePattern;

    private boolean inOptionalClause = false;

    private int unionCount = 0;
    private int optionalCount = 0;
    private int filterCount = 0;

    private Map<Triple, ElementGroup> triple2Parent = new HashMap<>();

    Stack<ElementGroup> parents = new Stack<>();

    public static String addPrefix(String queryString, Map<String, String> prefix2Namespace) {
        Query query = QueryFactory.create(queryString);
        for (Entry<String, String> entry : prefix2Namespace.entrySet()) {
            String prefix = entry.getKey();
            String namespace = entry.getValue();
            query.setPrefix(prefix, namespace);
        }
        return query.toString();
    }

    public static String addPrefixes(String queryString, String prefix, String namespace) {
        Query query = QueryFactory.create(queryString);
        query.setPrefix(prefix, namespace);
        return query.toString();
    }

    /**
     * Returns all variables that occur in a triple pattern of the SPARQL query.
     * @param query the query
     * @return
     */
    public Set<Var> getVariables(Query query) {
        Set<Var> vars = new HashSet<>();

        Set<Triple> triplePatterns = extractTriplePattern(query, false);

        for (Triple tp : triplePatterns) {
            if (tp.getSubject().isVariable()) {
                vars.add(Var.alloc(tp.getSubject()));
            } else if (tp.getObject().isVariable()) {
                vars.add(Var.alloc(tp.getObject()));
            } else if (tp.getPredicate().isVariable()) {
                vars.add(Var.alloc(tp.getPredicate()));
            }
        }

        return vars;
    }

    /**
     * Returns all variables that occur as subject in a triple pattern of the SPARQL query.
     * @param query the query
     * @return
     */
    public Set<Var> getSubjectVariables(Query query) {

        Set<Triple> triplePatterns = extractTriplePattern(query, false);

        Set<Var> vars = new HashSet<>();
        for (Triple tp : triplePatterns) {
            if (tp.getSubject().isVariable()) {
                vars.add(Var.alloc(tp.getSubject()));
            }
        }

        return vars;
    }

    /**
     * Returns all variables that occur as subject in a triple pattern of the SPARQL query.
     * @param query the query
     * @return
     */
    public static Set<Var> getSubjectVars(Query query) {
        final Set<Var> vars = new HashSet<>();

        ElementWalker.walk(query.getQueryPattern(), new ElementVisitorBase() {
            @Override
            public void visit(ElementTriplesBlock el) {
                Iterator<Triple> triples = el.patternElts();
                while (triples.hasNext()) {
                    Triple triple = triples.next();
                    if (triple.getSubject().isVariable()) {
                        vars.add(Var.alloc(triples.next().getSubject()));
                    }
                }
            }

            @Override
            public void visit(ElementPathBlock el) {
                Iterator<TriplePath> triples = el.patternElts();
                while (triples.hasNext()) {
                    TriplePath triple = triples.next();
                    if (triple.getSubject().isVariable()) {
                        vars.add(Var.alloc(triples.next().getSubject()));
                    }
                }
            }
        });

        return vars;
    }

    public static Set<Triple> getTriplePatterns(Query query) {
        final Set<Triple> triplePatterns = Sets.newHashSet();

        ElementWalker.walk(query.getQueryPattern(), new ElementVisitorBase() {
            @Override
            public void visit(ElementTriplesBlock el) {
                Iterator<Triple> triples = el.patternElts();
                while (triples.hasNext()) {
                    Triple triple = triples.next();
                    triplePatterns.add(triple);
                }
            }

            @Override
            public void visit(ElementPathBlock el) {
                Iterator<TriplePath> triplePaths = el.patternElts();
                while (triplePaths.hasNext()) {
                    TriplePath tp = triplePaths.next();
                    if (tp.isTriple()) {
                        Triple triple = tp.asTriple();
                        triplePatterns.add(triple);
                    }
                }
            }
        });
        return triplePatterns;
    }

    /**
     * Given a SPARQL query and a start node, return the outgoing
     * triple patterns.
     * @param query the query
     * @param source the start node
     * @return
     */
    public static Set<Triple> getOutgoingTriplePatterns(Query query, final Node source) {
        final Set<Triple> outgoingTriples = Sets.newHashSet();

        ElementWalker.walk(query.getQueryPattern(), new ElementVisitorBase() {
            @Override
            public void visit(ElementTriplesBlock el) {
                Iterator<Triple> triples = el.patternElts();
                while (triples.hasNext()) {
                    Triple triple = triples.next();
                    Node subject = triple.getSubject();
                    if (subject.equals(source)) {
                        outgoingTriples.add(triple);
                    }
                }
            }

            @Override
            public void visit(ElementPathBlock el) {
                Iterator<TriplePath> triplePaths = el.patternElts();
                while (triplePaths.hasNext()) {
                    TriplePath tp = triplePaths.next();
                    if (tp.isTriple()) {
                        Triple triple = tp.asTriple();
                        Node subject = triple.getSubject();
                        if (subject.equals(source)) {
                            outgoingTriples.add(triple);
                        }
                    }
                }
            }
        });

        return outgoingTriples;
    }

    /**
     * Given a SPARQL query and a start node, return the maximum subject-object
     * join depth.
     * @param query the query
     * @param source the start node
     * @return
     */
    public static int getSubjectObjectJoinDepth(Query query, final Node source) {
        final Set<Triple> outgoingTriples = Sets.newHashSet();

        ElementWalker.walk(query.getQueryPattern(), new ElementVisitorBase() {
            @Override
            public void visit(ElementTriplesBlock el) {
                Iterator<Triple> triples = el.patternElts();
                while (triples.hasNext()) {
                    Triple triple = triples.next();
                    Node subject = triple.getSubject();
                    if (subject.equals(source) && triple.getObject().isVariable()) {
                        outgoingTriples.add(triple);
                    }
                }
            }

            @Override
            public void visit(ElementPathBlock el) {
                Iterator<TriplePath> triplePaths = el.patternElts();
                while (triplePaths.hasNext()) {
                    TriplePath tp = triplePaths.next();
                    if (tp.isTriple()) {
                        Triple triple = tp.asTriple();
                        Node subject = triple.getSubject();
                        if (subject.equals(source) && triple.getObject().isVariable()) {
                            outgoingTriples.add(triple);
                        }
                    }
                }
            }
        });

        int maxDepth = 0;
        for (Triple triple : outgoingTriples) {
            maxDepth = Math.max(maxDepth, 1 + getSubjectObjectJoinDepth(query, triple.getObject()));
        }

        return maxDepth;
    }

    public static CBDStructureTree getOptimalCBDStructure(Query query) {
        CBDStructureTree tree = new CBDStructureTree("root");

        Var var = query.getProjectVars().get(0);

        getOptimalCBDStructure(query, tree, var.asNode(), null, "");

        return tree;
    }

    private static void getOptimalCBDStructure(Query query, CBDStructureTree structureTree, Node current,
            Node parent, String direction) {
        QueryUtils utils = new QueryUtils();

        // traverse the outgoing paths
        Set<Triple> tmp = utils.extractOutgoingTriplePatterns(query, current).stream()
                .filter(tp -> !direction.equals("in") || !tp.getObject().matches(parent))
                .collect(Collectors.toSet());
        if (!tmp.isEmpty()) {
            List<CBDStructureTree> outChildren = structureTree.getChildren().stream().filter(t -> t.isOutNode())
                    .collect(Collectors.toList());
            CBDStructureTree outChild;
            if (outChildren.isEmpty()) {
                outChild = structureTree.addOutNode();
            } else {
                outChild = outChildren.get(0);
            }
            tmp.stream().filter(tp -> tp.getObject().isVariable()).map(tp -> tp.getObject())
                    .forEach(node -> getOptimalCBDStructure(query, outChild, node, current, "out"));
        }
        // traverse the incoming paths
        tmp = utils.extractIncomingTriplePatterns(query, current).stream()
                .filter(tp -> !direction.equals("out") || !tp.getSubject().matches(parent))
                .collect(Collectors.toSet());
        if (!tmp.isEmpty()) {
            CBDStructureTree inChild = structureTree.addInNode();

            tmp.stream().filter(tp -> tp.getSubject().isVariable()).map(tp -> tp.getSubject())
                    .forEach(node -> getOptimalCBDStructure(query, inChild, node, current, "in"));
        }
    }

    /**
     * Returns all variables that occur as object in a triple pattern of the SPARQL query.
     * @param query the query
     * @return
     */
    public static Set<Var> getObjectVars(Query query) {
        return getTriplePatterns(query).stream().filter(tp -> tp.getObject().isVariable())
                .map(tp -> Var.alloc(tp.getObject())).collect(Collectors.toSet());
    }

    /**
     * Returns all variables that occur as subject in a triple pattern of the SPARQL query.
     * @param query the query
     * @return
     */
    public Set<Var> getObjectVariables(Query query) {
        return extractTriplePattern(query, false).stream().filter(tp -> tp.getObject().isVariable())
                .map(tp -> Var.alloc(tp.getObject())).collect(Collectors.toSet());
    }

    /**
     * Returns all triple patterns in given SPARQL query that have the given node in subject position, i.e. the outgoing
     * triple patterns.
     * @param query The SPARQL query.
     * @param node the node
     * @return
     */
    public Set<Triple> extractOutgoingTriplePatterns(Query query, Node node) {
        return extractTriplePattern(query, false).stream().filter(t -> t.subjectMatches(node))
                .collect(Collectors.toSet());
    }

    public Set<Triple> extractOutgoingTriplePatternsTrans(Query query, Node node) {
        return Stream.concat(extractOutgoingTriplePatterns(query, node).stream(),
                extractOutgoingTriplePatterns(query, node).stream()
                        .map(tp -> extractOutgoingTriplePatternsTrans(query, tp.getObject()))
                        .flatMap(set -> set.stream()))
                .collect(Collectors.toSet());
    }

    /**
     * Returns all triple patterns in given SPARQL query that have the given node in object position, i.e. the incoming
     * triple patterns.
     * @param query The SPARQL query.
     * @param node the node
     * @return
     */
    public Set<Triple> extractIncomingTriplePatterns(Query query, Node node) {
        return extractTriplePattern(query, false).stream().filter(tp -> tp.objectMatches(node))
                .collect(Collectors.toSet());
    }

    public Set<Triple> extractIncomingTriplePatternsTrans(Query query, Node node) {
        return Stream.concat(extractIncomingTriplePatterns(query, node).stream(),
                extractIncomingTriplePatterns(query, node).stream()
                        .map(tp -> extractIncomingTriplePatternsTrans(query, tp.getSubject()))
                        .flatMap(set -> set.stream()))
                .collect(Collectors.toSet());
    }

    /**
     * Returns all triple patterns in given SPARQL query that have the given node either in subject or in object position, i.e. 
     * the ingoing and outgoing triple patterns.
     * @param query The SPARQL query.
     * @param node the node
     * @return
     */
    public Set<Triple> extractTriplePatterns(Query query, Node node) {
        Set<Triple> triplePatterns = new HashSet<>();
        triplePatterns.addAll(extractIncomingTriplePatterns(query, node));
        triplePatterns.addAll(extractOutgoingTriplePatterns(query, node));
        return triplePatterns;
    }

    /**
     * Returns all triple patterns in given SPARQL query that contain the
     * given predicate. 
     * @param query the SPARQL query.
     * @param predicate the predicate
     * @return
     */
    public Set<Triple> extractTriplePatternsWithPredicate(Query query, Node predicate) {
        // get all triple patterns
        Set<Triple> triplePatterns = extractTriplePattern(query);

        // filter by predicate
        Iterator<Triple> iterator = triplePatterns.iterator();
        while (iterator.hasNext()) {
            Triple tp = iterator.next();
            if (!tp.predicateMatches(predicate)) {
                iterator.remove();
            }
        }

        return triplePatterns;
    }

    /**
     * Returns all triple patterns in given SPARQL query that have the given node either in subject or in object position, i.e. 
     * the incoming and outgoing triple patterns.
     * @param query The SPARQL query.
     * @param node the node
     * @return
     */
    public Set<Triple> extractNonOptionalTriplePatterns(Query query, Node node) {
        Set<Triple> triplePatterns = new HashSet<>();
        triplePatterns.addAll(extractIncomingTriplePatterns(query, node));
        triplePatterns.addAll(extractOutgoingTriplePatterns(query, node));
        triplePatterns.removeAll(optionalTriplePattern);
        return triplePatterns;
    }

    /**
     * Returns triple patterns for each projection variable v such that v is either in subject or object position.
     * @param query The SPARQL query.
     * @return
     */
    public Map<Var, Set<Triple>> extractTriplePatternsForProjectionVars(Query query) {
        Map<Var, Set<Triple>> var2TriplePatterns = new HashMap<>();
        for (Var var : query.getProjectVars()) {
            Set<Triple> triplePatterns = new HashSet<>();
            triplePatterns.addAll(extractIncomingTriplePatterns(query, var));
            triplePatterns.addAll(extractOutgoingTriplePatterns(query, var));
            var2TriplePatterns.put(var, triplePatterns);
        }
        return var2TriplePatterns;
    }

    /**
     * Returns triple patterns for each projection variable v such that v is in subject position.
     * @param query The SPARQL query.
     * @return
     */
    public Map<Var, Set<Triple>> extractOutgoingTriplePatternsForProjectionVars(Query query) {
        Map<Var, Set<Triple>> var2TriplePatterns = new HashMap<>();
        for (Var var : query.getProjectVars()) {
            Set<Triple> triplePatterns = new HashSet<>();
            triplePatterns.addAll(extractOutgoingTriplePatterns(query, var));
            var2TriplePatterns.put(var, triplePatterns);
        }
        return var2TriplePatterns;
    }

    /**
     * @return the optionalTriplePattern
     */
    public Set<Triple> getOptionalTriplePatterns() {
        return optionalTriplePattern;
    }

    /**
     * Returns triple patterns for each projection variable v such that v is in subject position.
     * @param query The SPARQL query.
     * @return
     */
    public Map<Var, Set<Triple>> extractIncomingTriplePatternsForProjectionVars(Query query) {
        Map<Var, Set<Triple>> var2TriplePatterns = new HashMap<>();
        for (Var var : query.getProjectVars()) {
            Set<Triple> triplePatterns = new HashSet<>();
            triplePatterns.addAll(extractIncomingTriplePatterns(query, var));
            var2TriplePatterns.put(var, triplePatterns);
        }
        return var2TriplePatterns;
    }

    /**
     * Returns triple patterns for each projection variable v such that v is in object position.
     * @param query The SPARQL query.
     * @return
     */
    public Map<Var, Set<Triple>> extractIngoingTriplePatternsForProjectionVars(Query query) {
        Map<Var, Set<Triple>> var2TriplePatterns = new HashMap<>();
        for (Var var : query.getProjectVars()) {
            Set<Triple> triplePatterns = new HashSet<>();
            triplePatterns.addAll(extractIncomingTriplePatterns(query, var));
            var2TriplePatterns.put(var, triplePatterns);
        }
        return var2TriplePatterns;
    }

    public Set<Triple> extractTriplePattern(Query query) {
        return extractTriplePattern(query, false);
    }

    public Set<Triple> extractTriplePattern(Query query, boolean ignoreOptionals) {
        triplePattern = new HashSet<>();
        optionalTriplePattern = new HashSet<>();

        query.getQueryPattern().visit(this);

        //postprocessing: triplepattern in OPTIONAL clause
        if (!ignoreOptionals) {
            if (query.isSelectType()) {
                for (Triple t : optionalTriplePattern) {
                    if (!ListUtils.intersection(new ArrayList<>(VarUtils.getVars(t)), query.getProjectVars())
                            .isEmpty()) {
                        triplePattern.add(t);
                    }
                }
            }
        }
        return triplePattern;
    }

    public boolean isOptional(Triple triple) {
        return optionalTriplePattern.contains(triple);
    }

    public Set<Triple> extractTriplePattern(ElementGroup group) {
        return extractTriplePattern(group, false);
    }

    public Set<Triple> extractTriplePattern(ElementGroup group, boolean ignoreOptionals) {
        triplePattern = new HashSet<>();
        optionalTriplePattern = new HashSet<>();

        group.visit(this);

        //postprocessing: triplepattern in OPTIONAL clause
        if (!ignoreOptionals) {
            for (Triple t : optionalTriplePattern) {
                triplePattern.add(t);
            }
        }

        return triplePattern;
    }

    public Query removeUnboundObjectVarTriples(Query query) {
        QueryUtils queryUtils = new QueryUtils();

        Var rootVar = query.getProjectVars().get(0);

        // 1. outgoing triple paths pruning
        Set<Triple> outgoingTriplePatterns = queryUtils.extractOutgoingTriplePatternsTrans(query, rootVar);
        Multimap<Var, Triple> var2OutgoingTriplePatterns = HashMultimap.create();

        // mapping from variable to triple pattern
        for (Triple tp : outgoingTriplePatterns) {
            var2OutgoingTriplePatterns.put(Var.alloc(tp.getSubject()), tp);
        }

        // remove triple patterns with object is var node and leaf node
        Iterator<Triple> iterator = outgoingTriplePatterns.iterator();
        while (iterator.hasNext()) {
            Triple triple = iterator.next();
            Node object = triple.getObject();
            if (object.isVariable() && !var2OutgoingTriplePatterns.containsKey(Var.alloc(object))) {
                iterator.remove();
            }
        }

        // 2. incoming triple paths pruning
        Set<Triple> incomingTriplePatterns = queryUtils.extractIncomingTriplePatternsTrans(query, rootVar);
        Multimap<Var, Triple> var2IncomingTriplePatterns = HashMultimap.create();

        // mapping from variable to triple pattern
        for (Triple tp : incomingTriplePatterns) {
            var2IncomingTriplePatterns.put(Var.alloc(tp.getObject()), tp);
        }

        // remove triple patterns with object is var node and leaf node
        iterator = incomingTriplePatterns.iterator();
        while (iterator.hasNext()) {
            Triple triple = iterator.next();
            Node s = triple.getSubject();
            if (s.isVariable() && !var2IncomingTriplePatterns.containsKey(Var.alloc(s))) {
                iterator.remove();
            }
        }

        Query newQuery = new Query();
        newQuery.addProjectVars(query.getProjectVars());
        ElementTriplesBlock el = new ElementTriplesBlock();
        for (Triple triple : Sets.union(outgoingTriplePatterns, incomingTriplePatterns)) {
            el.addTriple(triple);
        }
        newQuery.setQuerySelectType();
        newQuery.setDistinct(true);
        newQuery.setQueryPattern(el);

        return newQuery;
    }

    /**
     * Removes triple patterns of form (s rdf:type A) if there exists a
     * triple pattern (s rdf:type B) such that the underlying
     * knowledge base entails (B rdfs:subClassOf A).
     * @param qef the query execution factory
     * @param query the query
     */
    public void filterOutGeneralTypes(QueryExecutionFactory qef, Query query) {
        // extract all rdf:type triple patterns
        Set<Triple> typeTriplePatterns = extractTriplePatternsWithPredicate(query, RDF.type.asNode());

        // group by subject
        Multimap<Node, Triple> subject2TriplePatterns = HashMultimap.create();
        for (Triple tp : typeTriplePatterns) {
            subject2TriplePatterns.put(tp.getSubject(), tp);
        }

        // keep the most specific types for each subject
        for (Node subject : subject2TriplePatterns.keySet()) {
            Collection<Triple> triplePatterns = subject2TriplePatterns.get(subject);
            Collection<Triple> triplesPatterns2Remove = new HashSet<>();

            for (Triple tp : triplePatterns) {
                if (!triplesPatterns2Remove.contains(tp)) {
                    // get all super classes for the triple object
                    Set<Node> superClasses = getSuperClasses(qef, tp.getObject());

                    // remove triple patterns that have one of the super classes as object
                    for (Triple tp2 : triplePatterns) {
                        if (tp2 != tp && superClasses.contains(tp2.getObject())) {
                            triplesPatterns2Remove.add(tp2);
                        }
                    }
                }
            }

            // remove triple patterns
            triplePatterns.removeAll(triplesPatterns2Remove);
        }
    }

    public static DirectedGraph<Node, LabeledEdge> asJGraphT(Query query) {
        QueryUtils utils = new QueryUtils();

        Set<Triple> tps = utils.extractTriplePattern(query);

        DirectedGraph<Node, LabeledEdge> g = new DefaultDirectedGraph<Node, LabeledEdge>(LabeledEdge.class);

        tps.forEach(tp -> {
            g.addVertex(tp.getSubject());
            g.addVertex(tp.getObject());
            g.addEdge(tp.getSubject(), tp.getObject(),
                    new LabeledEdge(tp.getSubject(), tp.getObject(), tp.getPredicate()));
        });

        return g;
    }

    public static void exportAsGraph(Query query, File file) {
        DirectedGraph<Node, LabeledEdge> g = asJGraphT(query);
        System.out.println(g.edgeSet().size());

        JGraphXAdapter adapter = new JGraphXAdapter(g);

        // positioning via jgraphx layouts
        mxHierarchicalLayout layout = new mxHierarchicalLayout(adapter);
        layout.execute(adapter.getDefaultParent());

        Map<String, Object> edgeStyle = new HashMap<String, Object>();
        //edgeStyle.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ORTHOGONAL);
        edgeStyle.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_CONNECTOR);
        edgeStyle.put(mxConstants.STYLE_ENDARROW, mxConstants.ARROW_CLASSIC);
        edgeStyle.put(mxConstants.STYLE_STROKECOLOR, "#000000");
        edgeStyle.put(mxConstants.STYLE_FONTCOLOR, "#000000");
        edgeStyle.put(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, "#ffffff");

        Map<String, Object> nodeStyle = new HashMap<>();
        nodeStyle.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
        nodeStyle.put(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE);

        mxStylesheet stylesheet = new mxStylesheet();
        stylesheet.setDefaultEdgeStyle(edgeStyle);
        stylesheet.setDefaultVertexStyle(nodeStyle);

        adapter.setStylesheet(stylesheet);

        //      JFrame frame = new JFrame();
        //      frame.getContentPane().add(new mxGraphComponent(adapter));
        //      frame.pack();
        //      frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        //      frame.setVisible(true);

        BufferedImage image = mxCellRenderer.createBufferedImage(adapter, null, 1, Color.WHITE, true, null);
        mxPngEncodeParam param = mxPngEncodeParam.getDefaultEncodeParam(image);

        try {
            FileOutputStream outputStream = new FileOutputStream(file);
            mxPngImageEncoder encoder = new mxPngImageEncoder(outputStream, param);
            if (image != null) {
                encoder.encode(image);
            }
            outputStream.close();
            //         ImageIO.write(image, "PNG", file);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private Set<Node> getSuperClasses(QueryExecutionFactory qef, Node cls) {
        Set<Node> superClasses = new HashSet<>();

        superClassesQueryTemplate.setIri("sub", cls.getURI());

        String query = superClassesQueryTemplate.toString();

        try {
            QueryExecution qe = qef.createQueryExecution(query);
            ResultSet rs = qe.execSelect();
            while (rs.hasNext()) {
                QuerySolution qs = rs.next();
                superClasses.add(qs.getResource("sup").asNode());
            }
            qe.close();
        } catch (Exception e) {
            logger.error("ERROR. Getting super classes of " + cls + " failed.", e);
        }

        return superClasses;
    }

    @Override
    public void visit(ElementGroup el) {
        parents.push(el);
        for (Element e : el.getElements()) {
            e.visit(this);
        }
        parents.pop();
    }

    @Override
    public void visit(ElementOptional el) {
        optionalCount++;
        inOptionalClause = true;
        el.getOptionalElement().visit(this);
        inOptionalClause = false;
    }

    @Override
    public void visit(ElementTriplesBlock el) {
        for (Iterator<Triple> iterator = el.patternElts(); iterator.hasNext();) {
            Triple t = iterator.next();
            if (inOptionalClause) {
                optionalTriplePattern.add(t);
            } else {
                triplePattern.add(t);
            }
            if (!parents.isEmpty()) {
                triple2Parent.put(t, parents.peek());
            }
        }
    }

    @Override
    public void visit(ElementPathBlock el) {
        for (Iterator<TriplePath> iterator = el.patternElts(); iterator.hasNext();) {
            TriplePath tp = iterator.next();
            if (inOptionalClause) {
                if (tp.isTriple()) {
                    optionalTriplePattern.add(tp.asTriple());
                    if (!parents.isEmpty()) {
                        triple2Parent.put(tp.asTriple(), parents.peek());
                    }
                }
            } else {
                if (tp.isTriple()) {
                    triplePattern.add(tp.asTriple());
                    if (!parents.isEmpty()) {
                        triple2Parent.put(tp.asTriple(), parents.peek());
                    }
                }
            }

        }
    }

    @Override
    public void visit(ElementUnion el) {
        unionCount++;
        for (Element e : el.getElements()) {
            e.visit(this);
        }
    }

    @Override
    public void visit(ElementFilter el) {
        filterCount++;
    }

    public int getUnionCount() {
        return unionCount;
    }

    public int getOptionalCount() {
        return optionalCount;
    }

    public int getFilterCount() {
        return filterCount;
    }

    /**
     * Returns the ElementGroup object containing the triple pattern.
     * @param triple the triple patterm
     * @return
     */
    public ElementGroup getElementGroup(Triple triple) {
        return triple2Parent.get(triple);
    }

    static class LabeledEdge extends DefaultEdge {
        private final Node s;
        private final Node t;
        private final Node edge;

        public LabeledEdge(Node s, Node t, Node edge) {
            this.s = s;
            this.t = t;
            this.edge = edge;
        }

        public Node getEdge() {
            return edge;
        }

        @Override
        public String toString() {
            return edge.toString();
        }
    }

    public static void main(String[] args) throws Exception {
        Query q = QueryFactory.create(
                "PREFIX  dbp:  <http://dbpedia.org/resource/>\n" + "PREFIX  dbo: <http://dbpedia.org/ontology/>\n"
                        + "SELECT  ?thumbnail\n" + "WHERE\n" + "  { dbp:total dbo:thumbnail ?thumbnail }");
        QueryUtils queryUtils = new QueryUtils();
        System.out.println(queryUtils.extractOutgoingTriplePatterns(q, q.getProjectVars().get(0)));
        System.out.println(queryUtils.extractIncomingTriplePatterns(q, q.getProjectVars().get(0)));

        q = QueryFactory.create("SELECT DISTINCT  ?x0\n" + "WHERE\n"
                + "  { ?x0  <http://dbpedia.org/ontology/activeYearsEndYear>  ?date5 ;\n"
                + "         <http://dbpedia.org/ontology/activeYearsStartYear>  ?date4 ;\n"
                + "         <http://dbpedia.org/ontology/birthDate>  ?date0 ;\n"
                + "         <http://dbpedia.org/ontology/birthPlace>  <http://dbpedia.org/resource/Austria> ;\n"
                + "         <http://dbpedia.org/ontology/birthPlace>  <http://dbpedia.org/resource/Austria-Hungary> ;\n"
                + "         <http://dbpedia.org/ontology/birthPlace>  <http://dbpedia.org/resource/Vienna> ;\n"
                + "         <http://dbpedia.org/ontology/birthYear>  ?date3 ;\n"
                + "         <http://dbpedia.org/ontology/deathDate>  ?date2 ;\n"
                + "         <http://dbpedia.org/ontology/deathPlace>  <http://dbpedia.org/resource/Berlin> ;\n"
                + "         <http://dbpedia.org/ontology/deathPlace>  <http://dbpedia.org/resource/Germany> ;\n"
                + "         <http://dbpedia.org/ontology/deathYear>  ?date1 ;\n"
                + "         <http://dbpedia.org/ontology/occupation>  <http://dbpedia.org/resource/Hilde_K%C3%B6rber__1> ;\n"
                + "         <http://dbpedia.org/ontology/viafId>  \"32259546\" ;\n"
                + "         <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>  <http://dbpedia.org/ontology/Person> .\n"
                + "    FILTER ( ( str(?date0) = \"1906-07-03+02:00\" ) || ( str(?date0) = \"1906-07-03\" ) )\n"
                + "    FILTER ( ( str(?date1) = \"1969+02:00\" ) || ( str(?date1) = \"1969-01-01\" ) )\n"
                + "    FILTER ( ( str(?date2) = \"1969-05-31+02:00\" ) || ( str(?date2) = \"1969-05-31\" ) )\n"
                + "    FILTER ( ( str(?date3) = \"1906+02:00\" ) || ( str(?date3) = \"1906-01-01\" ) )\n"
                + "    FILTER ( ( str(?date4) = \"1930+02:00\" ) || ( str(?date4) = \"1930-01-01\" ) )\n"
                + "    FILTER ( ( str(?date5) = \"1964+02:00\" ) || ( str(?date5) = \"1964-01-01\" ) )\n" + "  }");

        System.out.println(queryUtils.removeUnboundObjectVarTriples(q));

        String query = "SELECT DISTINCT ?s WHERE {" + "?s a <http://dbpedia.org/ontology/BeautyQueen> ."
                + "?s <http://dbpedia.org/ontology/birthPlace> ?o0 ."
                + "?o0 <http://dbpedia.org/ontology/isPartOf> ?o1 ."
                + "?o1 <http://dbpedia.org/ontology/timeZone> <http://dbpedia.org/resource/Eastern_Time_Zone> .}";

        System.out.println(QueryUtils.getSubjectObjectJoinDepth(QueryFactory.create(query), Var.alloc("s")));

        System.out
                .println(queryUtils.extractOutgoingTriplePatternsTrans(QueryFactory.create(query), Var.alloc("s")));
    }

}