• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanodash / 17852532121

19 Sep 2025 08:10AM UTC coverage: 13.568% (-0.3%) from 13.87%
17852532121

push

github

tkuhn
feat: Switch to QueryRef provided by nanopub, using multimap

428 of 4008 branches covered (10.68%)

Branch coverage included in aggregate %.

1104 of 7283 relevant lines covered (15.16%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.33
src/main/java/com/knowledgepixels/nanodash/GrlcQuery.java
1
package com.knowledgepixels.nanodash;
2

3
import java.io.Serializable;
4
import java.util.ArrayList;
5
import java.util.Collections;
6
import java.util.HashMap;
7
import java.util.HashSet;
8
import java.util.List;
9
import java.util.Map;
10
import java.util.Set;
11

12
import org.eclipse.rdf4j.model.IRI;
13
import org.eclipse.rdf4j.model.Literal;
14
import org.eclipse.rdf4j.model.Statement;
15
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
16
import org.eclipse.rdf4j.model.vocabulary.RDF;
17
import org.eclipse.rdf4j.model.vocabulary.RDFS;
18
import org.eclipse.rdf4j.query.algebra.Var;
19
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
20
import org.eclipse.rdf4j.query.parser.ParsedQuery;
21
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
22
import org.nanopub.Nanopub;
23
import org.nanopub.extra.services.QueryRef;
24
import org.slf4j.Logger;
25
import org.slf4j.LoggerFactory;
26

27
import com.knowledgepixels.nanodash.component.QueryParamField;
28

29
import net.trustyuri.TrustyUriUtils;
30

31
/**
32
 * Represents a GRLC query extracted from a nanopublication.
33
 * This class parses the query details, including SPARQL, endpoint, label, description, and placeholders.
34
 */
35
public class GrlcQuery implements Serializable {
36

37
    private static final Logger logger = LoggerFactory.getLogger(GrlcQuery.class);
3✔
38

39
    private static Map<String, GrlcQuery> instanceMap = new HashMap<>();
4✔
40

41
    public static GrlcQuery get(QueryRef ref) {
42
        return get(ref.getName());
×
43
    }
44

45
    /**
46
     * Returns a singleton instance of GrlcQuery for the given query ID.
47
     *
48
     * @param id the unique identifier or URI of the query
49
     * @return a GrlcQuery instance
50
     */
51
    public static GrlcQuery get(String id) {
52
        if (!instanceMap.containsKey(id)) {
4!
53
            try {
54
                GrlcQuery q = new GrlcQuery(id);
5✔
55
                id = q.getQueryId();
3✔
56
                if (instanceMap.containsKey(id)) return instanceMap.get(id);
9✔
57
                instanceMap.put(id, q);
5✔
58
            } catch (Exception ex) {
1✔
59
                logger.error("Could not load query: {}", id, ex);
5✔
60
            }
1✔
61
        }
62
        return instanceMap.get(id);
5✔
63
    }
64

65
    /**
66
     * The IRI for the GRLC query class and properties.
67
     */
68
    public final static IRI GRLC_QUERY_CLASS = Utils.vf.createIRI("https://w3id.org/kpxl/grlc/grlc-query");
4✔
69

70
    /**
71
     * The IRI for the SPARQL property and endpoint property in GRLC queries.
72
     */
73
    public final static IRI GRLC_HAS_SPARQL = Utils.vf.createIRI("https://w3id.org/kpxl/grlc/sparql");
4✔
74

75
    /**
76
     * The IRI for the endpoint property in GRLC queries.
77
     */
78
    public final static IRI GRLC_HAS_ENDPOINT = Utils.vf.createIRI("https://w3id.org/kpxl/grlc/endpoint");
5✔
79

80
    private final String queryId;
81
    private final String artifactCode;
82
    private final String querySuffix;
83
    private final Nanopub nanopub;
84
    private IRI queryUri;
85
    private String sparql;
86
    private IRI endpoint;
87
    private String label;
88
    private String description;
89
    private final List<String> placeholdersList;
90

91
    /**
92
     * Constructs a GrlcQuery object by parsing the provided query ID or URI.
93
     *
94
     * @param id The query ID or URI.
95
     * @throws IllegalArgumentException If the ID is null, invalid, or the nanopublication defines multiple queries.
96
     */
97
    private GrlcQuery(String id) {
2✔
98
        if (id == null) {
2✔
99
            throw new IllegalArgumentException("Null value for query ID");
5✔
100
        }
101
        if (TrustyUriUtils.isPotentialTrustyUri(id)) {
3!
102
            artifactCode = TrustyUriUtils.getArtifactCode(id);
4✔
103
            nanopub = Utils.getNanopub(artifactCode);
5✔
104
            for (Statement st : nanopub.getAssertion()) {
12✔
105
                if (st.getPredicate().equals(RDF.TYPE) && st.getObject().equals(GRLC_QUERY_CLASS)) {
10!
106
                    if (queryUri != null) {
3✔
107
                        throw new IllegalArgumentException("Nanopublication defines more than one query: " + id);
6✔
108
                    }
109
                    queryUri = (IRI) st.getSubject();
5✔
110
                }
111
            }
1✔
112
            if (queryUri == null) {
3✔
113
                throw new IllegalArgumentException("No query found in nanopublication: " + id);
6✔
114
            }
115
            queryId = queryUri.stringValue().replaceFirst("^https?://.*[^A-Za-z0-9-_](RA[A-Za-z0-9-_]{43}[/#][^/#]+)$", "$1").replace("#", "/");
12✔
116
        } else {
117
            if (id.matches("https?://.*[^A-Za-z0-9-_]RA[A-Za-z0-9-_]{43}[/#][^/#]+")) {
×
118
                queryId = id.replaceFirst("^https?://.*[^A-Za-z0-9-_](RA[A-Za-z0-9-_]{43}[/#][^/#]+)$", "$1").replace("#", "/");
×
119
            } else if (id.matches("RA[A-Za-z0-9-_]{43}[/#][^/#]+")) {
×
120
                queryId = id;
×
121
            } else {
122
                throw new IllegalArgumentException("Not a valid query ID or URI: " + id);
×
123
            }
124
            artifactCode = queryId.replaceFirst("[/#].*$", "");
×
125
            nanopub = Utils.getNanopub(artifactCode);
×
126
        }
127
        querySuffix = queryId.replaceFirst("^.*[/#]", "");
7✔
128
        for (Statement st : nanopub.getAssertion()) {
12✔
129
            if (!st.getSubject().stringValue().replace("#", "/").endsWith(queryId)) continue;
10!
130
            queryUri = (IRI) st.getSubject();
5✔
131
            if (st.getPredicate().equals(GRLC_HAS_SPARQL) && st.getObject() instanceof Literal objLiteral) {
14!
132
                sparql = objLiteral.stringValue();
5✔
133
            } else if (st.getPredicate().equals(GRLC_HAS_ENDPOINT) && st.getObject() instanceof IRI objIri) {
14!
134
                endpoint = objIri;
4✔
135
            } else if (st.getPredicate().equals(RDFS.LABEL)) {
5✔
136
                label = st.getObject().stringValue();
6✔
137
            } else if (st.getPredicate().equals(DCTERMS.DESCRIPTION)) {
5✔
138
                description = st.getObject().stringValue();
5✔
139
            }
140
        }
1✔
141

142
        final Set<String> placeholders = new HashSet<>();
4✔
143
        ParsedQuery query = new SPARQLParser().parseQuery(sparql, null);
8✔
144
        query.getTupleExpr().visitChildren(new AbstractSimpleQueryModelVisitor<>() {
14✔
145

146
            @Override
147
            public void meet(Var node) throws RuntimeException {
148
                super.meet(node);
3✔
149
                if (!node.isConstant() && !node.isAnonymous() && node.getName().startsWith("_")) {
11!
150
                    placeholders.add(node.getName());
×
151
                }
152
            }
1✔
153

154
        });
155
        List<String> placeholdersListPre = new ArrayList<>(placeholders);
5✔
156
        Collections.sort(placeholdersListPre);
2✔
157
        placeholdersList = Collections.unmodifiableList(placeholdersListPre);
4✔
158
    }
1✔
159

160
    /**
161
     * Returns the unique query ID.
162
     *
163
     * @return The query ID.
164
     */
165
    public String getQueryId() {
166
        return queryId;
3✔
167
    }
168

169
    /**
170
     * Returns the artifact code extracted from the nanopublication.
171
     *
172
     * @return The artifact code.
173
     */
174
    public String getArtifactCode() {
175
        return artifactCode;
3✔
176
    }
177

178
    /**
179
     * Returns the suffix of the query.
180
     *
181
     * @return The query suffix.
182
     */
183
    public String getQuerySuffix() {
184
        return querySuffix;
3✔
185
    }
186

187
    /**
188
     * Returns the nanopublication containing the query.
189
     *
190
     * @return The nanopublication.
191
     */
192
    public Nanopub getNanopub() {
193
        return nanopub;
3✔
194
    }
195

196
    /**
197
     * Returns the URI of the query.
198
     *
199
     * @return The query URI.
200
     */
201
    public IRI getQueryUri() {
202
        return queryUri;
3✔
203
    }
204

205
    /**
206
     * Returns the SPARQL query string.
207
     *
208
     * @return The SPARQL query.
209
     */
210
    public String getSparql() {
211
        return sparql;
3✔
212
    }
213

214
    /**
215
     * Returns the endpoint URI for the query.
216
     *
217
     * @return The endpoint URI.
218
     */
219
    public IRI getEndpoint() {
220
        return endpoint;
3✔
221
    }
222

223
    /**
224
     * Returns the label of the query.
225
     *
226
     * @return The query label.
227
     */
228
    public String getLabel() {
229
        return label;
3✔
230
    }
231

232
    /**
233
     * Returns the description of the query.
234
     *
235
     * @return The query description.
236
     */
237
    public String getDescription() {
238
        return description;
3✔
239
    }
240

241
    /**
242
     * Returns a list of placeholders in the query.
243
     *
244
     * @return The list of placeholders.
245
     */
246
    public List<String> getPlaceholdersList() {
247
        return placeholdersList;
3✔
248
    }
249

250
    /**
251
     * Creates a list of query parameter fields for the placeholders in the query.
252
     *
253
     * @param markupId The markup ID for the fields.
254
     * @return A list of query parameter fields.
255
     */
256
    public List<QueryParamField> createParamFields(String markupId) {
257
        List<QueryParamField> l = new ArrayList<>();
4✔
258
        for (String s : placeholdersList) {
7!
259
            l.add(new QueryParamField(markupId, s));
×
260
        }
×
261
        return l;
2✔
262
    }
263

264
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc