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

knowledgepixels / nanodash / 17675472756

12 Sep 2025 01:10PM UTC coverage: 13.79% (+0.05%) from 13.739%
17675472756

push

github

tkuhn
test: Fix GrlcQueryTest

433 of 3980 branches covered (10.88%)

Branch coverage included in aggregate %.

1112 of 7224 relevant lines covered (15.39%)

0.68 hits per line

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

83.76
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.slf4j.Logger;
24
import org.slf4j.LoggerFactory;
25

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

28
import net.trustyuri.TrustyUriUtils;
29

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

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

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

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

60
    /**
61
     * The IRI for the GRLC query class and properties.
62
     */
63
    public final static IRI GRLC_QUERY_CLASS = Utils.vf.createIRI("https://w3id.org/kpxl/grlc/grlc-query");
4✔
64

65
    /**
66
     * The IRI for the SPARQL property and endpoint property in GRLC queries.
67
     */
68
    public final static IRI GRLC_HAS_SPARQL = Utils.vf.createIRI("https://w3id.org/kpxl/grlc/sparql");
4✔
69

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

75
    private final String queryId;
76
    private final String artifactCode;
77
    private final String querySuffix;
78
    private final Nanopub nanopub;
79
    private IRI queryUri;
80
    private String sparql;
81
    private IRI endpoint;
82
    private String label;
83
    private String description;
84
    private final List<String> placeholdersList;
85

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

135
        final Set<String> placeholders = new HashSet<>();
4✔
136
        ParsedQuery query = new SPARQLParser().parseQuery(sparql, null);
8✔
137
        query.getTupleExpr().visitChildren(new AbstractSimpleQueryModelVisitor<>() {
14✔
138

139
            @Override
140
            public void meet(Var node) throws RuntimeException {
141
                super.meet(node);
3✔
142
                if (!node.isConstant() && !node.isAnonymous() && node.getName().startsWith("_")) {
11!
143
                    placeholders.add(node.getName());
×
144
                }
145
            }
1✔
146

147
        });
148
        List<String> placeholdersListPre = new ArrayList<>(placeholders);
5✔
149
        Collections.sort(placeholdersListPre);
2✔
150
        placeholdersList = Collections.unmodifiableList(placeholdersListPre);
4✔
151
    }
1✔
152

153
    /**
154
     * Returns the unique query ID.
155
     *
156
     * @return The query ID.
157
     */
158
    public String getQueryId() {
159
        return queryId;
3✔
160
    }
161

162
    /**
163
     * Returns the artifact code extracted from the nanopublication.
164
     *
165
     * @return The artifact code.
166
     */
167
    public String getArtifactCode() {
168
        return artifactCode;
3✔
169
    }
170

171
    /**
172
     * Returns the suffix of the query.
173
     *
174
     * @return The query suffix.
175
     */
176
    public String getQuerySuffix() {
177
        return querySuffix;
3✔
178
    }
179

180
    /**
181
     * Returns the nanopublication containing the query.
182
     *
183
     * @return The nanopublication.
184
     */
185
    public Nanopub getNanopub() {
186
        return nanopub;
3✔
187
    }
188

189
    /**
190
     * Returns the URI of the query.
191
     *
192
     * @return The query URI.
193
     */
194
    public IRI getQueryUri() {
195
        return queryUri;
3✔
196
    }
197

198
    /**
199
     * Returns the SPARQL query string.
200
     *
201
     * @return The SPARQL query.
202
     */
203
    public String getSparql() {
204
        return sparql;
3✔
205
    }
206

207
    /**
208
     * Returns the endpoint URI for the query.
209
     *
210
     * @return The endpoint URI.
211
     */
212
    public IRI getEndpoint() {
213
        return endpoint;
3✔
214
    }
215

216
    /**
217
     * Returns the label of the query.
218
     *
219
     * @return The query label.
220
     */
221
    public String getLabel() {
222
        return label;
3✔
223
    }
224

225
    /**
226
     * Returns the description of the query.
227
     *
228
     * @return The query description.
229
     */
230
    public String getDescription() {
231
        return description;
3✔
232
    }
233

234
    /**
235
     * Returns a list of placeholders in the query.
236
     *
237
     * @return The list of placeholders.
238
     */
239
    public List<String> getPlaceholdersList() {
240
        return placeholdersList;
3✔
241
    }
242

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

257
}
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