Skip to contentMethod: getParameterValue(Parameter)
1: /*
2: * JOPA
3: * Copyright (C) 2024 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.jopa.query.sparql;
19:
20: import cz.cvut.kbss.jopa.model.query.Parameter;
21: import cz.cvut.kbss.jopa.query.QueryHolder;
22: import cz.cvut.kbss.jopa.query.QueryParameter;
23:
24: import java.util.ArrayList;
25: import java.util.Collections;
26: import java.util.HashMap;
27: import java.util.LinkedHashSet;
28: import java.util.List;
29: import java.util.Map;
30: import java.util.Objects;
31: import java.util.Optional;
32: import java.util.Set;
33:
34: public class SparqlQueryHolder implements QueryHolder {
35:
36: private static final String SPARQL_LIMIT = " LIMIT ";
37: private static final String SPARQL_OFFSET = " OFFSET ";
38:
39: // Original query string
40: private final String query;
41:
42: private final Map<Parameter<?>, QueryParameter<?>> parameterSet;
43: private final Map<Object, QueryParameter<?>> identifiersToParameters;
44: // These parameters are in order matching the query parts and can appear multiple times in the list
45: private final List<QueryParameter<?>> parameters;
46: private final List<String> queryParts;
47:
48: private int offset = 0;
49:
50: private int limit = Integer.MAX_VALUE;
51:
52: public SparqlQueryHolder(String query, List<String> parts, List<QueryParameter<?>> parameters) {
53: this.query = query;
54: this.parameters = parameters;
55: this.queryParts = parts;
56: this.parameterSet = new HashMap<>();
57: parameters.forEach(p -> parameterSet.put(p, p));
58: this.identifiersToParameters = new HashMap<>(parameterSet.size());
59: parameterSet.values().forEach(p -> identifiersToParameters.put(p.getIdentifier(), p));
60: }
61:
62: @Override
63: public String getQuery() {
64: return query;
65: }
66:
67: @Override
68: public Set<Parameter<?>> getParameters() {
69: return Collections.unmodifiableSet(parameterSet.keySet());
70: }
71:
72: @Override
73: public boolean hasParameter(String name) {
74: return identifiersToParameters.containsKey(name);
75: }
76:
77: @Override
78: public boolean hasParameter(int position) {
79: return identifiersToParameters.containsKey(position);
80: }
81:
82: @Override
83: public QueryParameter<?> getParameter(String name) {
84: if (!hasParameter(name)) {
85: throw unknownParameter(name);
86: }
87: return identifiersToParameters.get(name);
88: }
89:
90: private static IllegalArgumentException unknownParameter(Object p) {
91: return new IllegalArgumentException("Parameter '" + p + "' does not exist in this query.");
92: }
93:
94: @Override
95: public Parameter<?> getParameter(int position) {
96: if (!hasParameter(position)) {
97: throw unknownParameter(position);
98: }
99: return identifiersToParameters.get(position);
100: }
101:
102: @Override
103: public Object getParameterValue(Parameter<?> parameter) {
104:• assert getInternalParameter(parameter).getValue() != null;
105: return getInternalParameter(parameter).getValue().getValue();
106: }
107:
108: private QueryParameter<?> getInternalParameter(Parameter<?> p) {
109: Objects.requireNonNull(p);
110: if (!parameterSet.containsKey(p)) {
111: throw unknownParameter(p);
112: }
113: return parameterSet.get(p);
114: }
115:
116: @Override
117: public <T> void setParameter(Parameter<T> parameter, Object value) {
118: Objects.requireNonNull(value);
119: getInternalParameter(parameter).setValue(value);
120: }
121:
122: @Override
123: public <T> void setParameter(Parameter<T> parameter, String value, String language) {
124: Objects.requireNonNull(value);
125: getInternalParameter(parameter).setValue(value, language);
126: }
127:
128: @Override
129: public <T> void setUntypedParameter(Parameter<T> parameter, Object value) {
130: Objects.requireNonNull(value);
131: getInternalParameter(parameter).setUntypedValue(value);
132: }
133:
134: @Override
135: public void setFirstResult(int startPosition) {
136: this.offset = startPosition;
137: }
138:
139: @Override
140: public int getFirstResult() {
141: return offset;
142: }
143:
144: @Override
145: public void setMaxResults(int maxResults) {
146: this.limit = maxResults;
147: }
148:
149: @Override
150: public int getMaxResults() {
151: return limit;
152: }
153:
154: @Override
155: public void clearParameter(Parameter<?> parameter) {
156: getInternalParameter(parameter).resetValue();
157: }
158:
159: @Override
160: public void clearParameters() {
161: parameterSet.values().forEach(QueryParameter::resetValue);
162: }
163:
164: @Override
165: public String assembleQuery() {
166: final StringBuilder sb = new StringBuilder();
167: final Set<QueryParameter<?>> projectedParams = new LinkedHashSet<>();
168: for (int i = 0; i < parameters.size(); i++) {
169: sb.append(queryParts.get(i));
170: final QueryParameter<?> qp = parameters.get(i);
171: if (qp.isProjected() && qp.getValue().isSet()) {
172: projectedParams.add(qp);
173: sb.append(qp.getIdentifierAsQueryString());
174: } else {
175: sb.append(qp.getValue().getQueryString());
176: }
177: }
178: if (queryParts.size() > parameters.size()) {
179: sb.append(queryParts.get(parameters.size()));
180: }
181: if (limit != Integer.MAX_VALUE) {
182: sb.append(SPARQL_LIMIT).append(limit);
183: }
184: if (offset != 0) {
185: sb.append(SPARQL_OFFSET).append(offset);
186: }
187: assembleValuesClause(projectedParams).ifPresent(sb::append);
188: return sb.toString();
189: }
190:
191: /**
192: * Generates a VALUES clause for query parameters that are set and appear in SELECT projection.
193: * <p>
194: * TODO Note that the current implementation does not support collection-valued parameters.
195: *
196: * @param parameters Projected parameters to output into query as VALUES clause
197: * @return VALUES clause, if there were any set parameters
198: */
199: private static Optional<String> assembleValuesClause(Set<QueryParameter<?>> parameters) {
200: if (parameters.isEmpty()) {
201: return Optional.empty();
202: }
203: final StringBuilder variables = new StringBuilder();
204: final int tableSize = maxValueCount(parameters);
205: final List<List<String>> valueTable = new ArrayList<>(parameters.size());
206: for (QueryParameter<?> qp : parameters) {
207: if (!variables.isEmpty()) {
208: variables.append(' ');
209: }
210: variables.append(qp.getIdentifierAsQueryString());
211: valueTable.add(qp.getValue().toQueryValues(tableSize));
212: }
213: return Optional.of(" VALUES (" + variables + ") { " + valueTableToString(valueTable, tableSize) + "}");
214: }
215:
216: private static int maxValueCount(Set<QueryParameter<?>> parameters) {
217: return parameters.stream().map(p -> p.getValue().valueCount()).max(Integer::compareTo).orElse(1);
218: }
219:
220: private static String valueTableToString(List<List<String>> valueTable, int rowSize) {
221: final StringBuilder sb = new StringBuilder();
222: for (int i = 0; i < rowSize; i++) {
223: sb.append("( ");
224: for (List<String> row : valueTable) {
225: sb.append(row.get(i)).append(" ");
226: }
227: sb.append(") ");
228: }
229: return sb.toString();
230: }
231:
232: @Override
233: public String toString() {
234: return assembleQuery();
235: }
236: }