001/**
002 * Copyright (C) 2007 - 2016, Jens Lehmann
003 *
004 * This file is part of DL-Learner.
005 *
006 * DL-Learner is free software; you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation; either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * DL-Learner is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.dllearner.algorithms.qtl.impl;
020
021import java.util.*;
022import java.util.function.Predicate;
023
024import com.google.common.collect.Sets;
025import org.apache.jena.graph.Node;
026import org.apache.jena.rdf.model.Model;
027import org.apache.jena.rdf.model.RDFNode;
028import org.apache.jena.rdf.model.Resource;
029import org.apache.jena.rdf.model.Statement;
030import org.apache.jena.sparql.vocabulary.FOAF;
031import org.apache.jena.util.iterator.ExtendedIterator;
032import org.apache.jena.vocabulary.RDF;
033import org.dllearner.algorithms.qtl.QueryTreeUtils;
034import org.dllearner.algorithms.qtl.datastructures.NodeInv;
035import org.dllearner.algorithms.qtl.datastructures.impl.RDFResourceTree;
036import org.dllearner.algorithms.qtl.util.StatementComparator;
037import org.dllearner.algorithms.qtl.util.StopURIsDBpedia;
038import org.dllearner.algorithms.qtl.util.StopURIsOWL;
039import org.dllearner.algorithms.qtl.util.StopURIsRDFS;
040import org.dllearner.algorithms.qtl.util.filters.NamespaceDropStatementFilter;
041import org.dllearner.algorithms.qtl.util.filters.ObjectDropStatementFilter;
042import org.dllearner.algorithms.qtl.util.filters.PredicateDropStatementFilter;
043import org.dllearner.kb.sparql.ConciseBoundedDescriptionGenerator;
044import org.dllearner.kb.sparql.SparqlEndpoint;
045import org.dllearner.kb.sparql.SymmetricConciseBoundedDescriptionGeneratorImpl;
046
047/**
048 * A factory for query trees that also considers incoming triples.
049 *
050 * @author Lorenz Bühmann
051 *
052 */
053public class QueryTreeFactoryBaseInv implements QueryTreeFactory {
054
055        private int nodeId;
056        private final Comparator<Statement> comparator = new StatementComparator();
057
058        private int maxDepth = 3;
059
060        private Set<Predicate<Statement>> dropFilters = new HashSet<>();
061
062        /* (non-Javadoc)
063         * @see org.dllearner.algorithms.qtl.impl.QueryTreeFactory#setMaxDepth(int)
064         */
065        @Override
066        public void setMaxDepth(int maxDepth) {
067                this.maxDepth = maxDepth;
068        }
069
070        @Override
071        public int maxDepth() {
072                return maxDepth;
073        }
074
075        /* (non-Javadoc)
076                 * @see org.dllearner.algorithms.qtl.impl.QueryTreeFactory#getQueryTree(org.apache.jena.rdf.model.Resource, org.apache.jena.rdf.model.Model, int)
077                 */
078        @Override
079        public RDFResourceTree getQueryTree(Resource resource, Model model, int maxDepth) {
080                return createTree(resource, model, maxDepth);
081        }
082        
083        /* (non-Javadoc)
084         * @see org.dllearner.algorithms.qtl.impl.QueryTreeFactory#addDropFilters(org.apache.jena.util.iterator.Filter)
085         */
086        @Override
087        @SuppressWarnings("unchecked")
088        public void addDropFilters(Predicate<Statement>... dropFilters) {
089                this.dropFilters.addAll(Arrays.asList(dropFilters));
090        }
091
092        private RDFResourceTree createTree(Resource resource, Model model, int maxDepth) {
093                nodeId = 0;
094
095                // create mapping from resources to statements, both in subject an object position
096                Map<Resource, SortedSet<Statement>> resource2InStatements = new HashMap<>();
097                Map<Resource, SortedSet<Statement>> resource2OutStatements = new HashMap<>();
098                fillMaps(resource, model, resource2InStatements, resource2OutStatements, 0, maxDepth);
099
100                // start with an empty tree whose root is the resource
101                RDFResourceTree tree = new RDFResourceTree(resource.asNode());
102
103                // fill the tree
104                fillTree(resource, null, tree, resource2InStatements, resource2OutStatements, 0, maxDepth);
105
106                return tree;
107        }
108
109        @SuppressWarnings("unchecked")
110        private void fillMaps(Resource s, Model model,
111                                                  Map<Resource, SortedSet<Statement>> resource2InStatements,
112                                                  Map<Resource, SortedSet<Statement>> resource2OutStatements,
113                                                  int currentDepth, int maxDepth) {
114
115                if(currentDepth < maxDepth) {
116                        // incoming triples
117                        SortedSet<Statement> statements = resource2InStatements.computeIfAbsent(s, k -> new TreeSet<>(comparator));
118                        ExtendedIterator stmtIterator = model.listStatements(null, null, s);
119                        for (Predicate<Statement> filter : dropFilters) {
120                                stmtIterator = stmtIterator.filterKeep(filter);
121                        }
122                        statements.addAll(stmtIterator.toSet());
123                        statements.forEach(st -> fillMaps(st.getSubject(), model, resource2InStatements, resource2OutStatements, currentDepth + 1, maxDepth));
124
125                        // outgoing triples
126                        statements = resource2OutStatements.computeIfAbsent(s, k -> new TreeSet<>(comparator));
127                        stmtIterator = model.listStatements(s, null, (RDFNode) null);
128                        for (Predicate<Statement> filter : dropFilters) {
129                                stmtIterator = stmtIterator.filterKeep(filter);
130                        }
131                        statements.addAll(stmtIterator.toSet());
132                        statements.stream().filter(st -> st.getObject().isResource()).forEach(st ->
133                                fillMaps(st.getObject().asResource(), model, resource2InStatements, resource2OutStatements, currentDepth + 1, maxDepth)
134                        );
135                }
136        }
137
138        private int nextNodeId() {
139                return nodeId++;
140        }
141
142        private void fillTree(Resource root, Statement statementFromParent, RDFResourceTree tree,
143                                                  Map<Resource, SortedSet<Statement>> resource2InStatements,
144                                                  Map<Resource, SortedSet<Statement>> resource2OutStatements,
145                                                  int currentDepth, int maxDepth) {
146                if(resource2InStatements.containsKey(root)) {
147                        resource2InStatements.get(root).stream().filter(st -> !st.equals(statementFromParent)).forEach(st -> {
148
149                                // check if path to parent is rdf:type, i.e. we have a class node
150                                // if so, we avoid incoming edges
151                                if(!tree.isRoot() && tree.getEdgeToParent().matches(RDF.type.asNode())) {
152                                        return;
153                                }
154
155                                Node predicate = new NodeInv(st.getPredicate().asNode());
156
157                                RDFNode data = st.getSubject();
158
159                                // create the subtree
160                                RDFResourceTree subTree = new RDFResourceTree(nextNodeId(), data.asNode());
161                                tree.addChild(subTree, predicate);
162
163                                // if current depth is < max depth recursive call
164                                if (currentDepth + 1 < maxDepth) {
165                                        fillTree(data.asResource(), st, subTree, resource2InStatements, resource2OutStatements, currentDepth + 1, maxDepth);
166                                }
167                        });
168                }
169                if(resource2OutStatements.containsKey(root)) {
170                        resource2OutStatements.get(root).stream().filter(st -> !st.equals(statementFromParent)).forEach(st -> {
171                                Node predicate = st.getPredicate().asNode();
172
173                                RDFNode data = st.getObject();
174
175                                // create the subtree
176                                RDFResourceTree subTree = new RDFResourceTree(nextNodeId(), data.asNode());
177                                tree.addChild(subTree, predicate);
178
179                                // if root of subtree is not a literal and current depth is < max depth recursive call
180                                if (!data.isLiteral() && (currentDepth + 1  < maxDepth)) {
181                                        fillTree(data.asResource(), st, subTree, resource2InStatements, resource2OutStatements, currentDepth + 1, maxDepth);
182                                }
183                        });
184                }
185        }
186
187        public static void main(String[] args) throws Exception {
188                QueryTreeFactory factory = new QueryTreeFactoryBaseInv();
189                factory.setMaxDepth(2);
190                factory.addDropFilters(
191                                new PredicateDropStatementFilter(Sets.union(Sets.union(StopURIsDBpedia.get(), StopURIsRDFS.get()), StopURIsOWL.get())),
192                                new ObjectDropStatementFilter(StopURIsOWL.get()),
193                                new NamespaceDropStatementFilter(
194                                                Sets.newHashSet(
195                                                                "http://dbpedia.org/property/", 
196                                                                "http://purl.org/dc/terms/",
197                                                                "http://dbpedia.org/class/yago/",
198                                                                FOAF.getURI()
199                                                                )
200                                                                )
201                                );
202                ConciseBoundedDescriptionGenerator cbdGen = new SymmetricConciseBoundedDescriptionGeneratorImpl(
203                                SparqlEndpoint.getEndpointDBpedia());
204                String resourceURI = "http://dbpedia.org/resource/Athens";
205                Model cbd = cbdGen.getConciseBoundedDescription(resourceURI, 1);
206                RDFResourceTree queryTree = factory.getQueryTree(resourceURI, cbd);
207                System.out.println(queryTree.getStringRepresentation());
208                System.out.println(QueryTreeUtils.toSPARQLQuery(queryTree));
209        }
210
211}