-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
392 additions
and
6 deletions.
There are no files selected for viewing
217 changes: 217 additions & 0 deletions
217
core/sparqlbuilder/src/main/java/org/eclipse/rdf4j/sparqlbuilder/constraint/Values.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2024 Eclipse RDF4J contributors. | ||
* | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Distribution License v1.0 | ||
* which accompanies this distribution, and is available at | ||
* http://www.eclipse.org/org/documents/edl-v10.php. | ||
* | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*******************************************************************************/ | ||
package org.eclipse.rdf4j.sparqlbuilder.constraint; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import org.eclipse.rdf4j.model.IRI; | ||
import org.eclipse.rdf4j.sparqlbuilder.core.Variable; | ||
import org.eclipse.rdf4j.sparqlbuilder.graphpattern.GraphPattern; | ||
import org.eclipse.rdf4j.sparqlbuilder.rdf.Rdf; | ||
import org.eclipse.rdf4j.sparqlbuilder.rdf.RdfValue; | ||
|
||
public class Values implements GraphPattern { | ||
Variable[] variables; | ||
RdfValue[][] solutionSequence; | ||
|
||
private static final RdfValue UNDEF = new RdfValue() { | ||
@Override | ||
public String getQueryString() { | ||
return "UNDEF"; | ||
} | ||
}; | ||
|
||
public Values(Variable[] variables, RdfValue[][] solutionSequence) { | ||
Objects.requireNonNull(solutionSequence); | ||
Objects.requireNonNull(solutionSequence); | ||
if (variables.length == 0) { | ||
throw new IllegalArgumentException("no variables provided for VALUES clause"); | ||
} | ||
if (solutionSequence.length == 0 | ||
|| solutionSequence[0] == null | ||
|| solutionSequence[0].length == 0) { | ||
throw new IllegalArgumentException("no values provided for VALUES clause"); | ||
} | ||
if (solutionSequence[0].length != variables.length) { | ||
throw new IllegalArgumentException( | ||
solutionSequence[0].length | ||
+ " values provided for " | ||
+ variables.length | ||
+ variables); | ||
} | ||
this.solutionSequence = solutionSequence; | ||
this.variables = variables; | ||
} | ||
|
||
@Override | ||
public String getQueryString() { | ||
StringBuilder sb = new StringBuilder(); | ||
String parOpen = this.variables.length > 1 ? "( " : ""; | ||
String parClose = this.variables.length > 1 ? ") " : ""; | ||
sb.append("VALUES ").append(parOpen); | ||
for (int i = 0; i < variables.length; i++) { | ||
sb.append(variables[i].getQueryString()).append(" "); | ||
} | ||
sb.append(parClose).append("{").append(System.lineSeparator()); | ||
for (int i = 0; i < solutionSequence.length; i++) { | ||
sb.append(" ").append(parOpen); | ||
for (int j = 0; j < solutionSequence[i].length; j++) { | ||
sb.append(solutionSequence[i][j].getQueryString()).append(" "); | ||
} | ||
sb.append(parClose).append(System.lineSeparator()); | ||
} | ||
sb.append("}").append(System.lineSeparator()); | ||
return sb.toString(); | ||
} | ||
|
||
public static VariablesBuilder builder() { | ||
return new Builder(); | ||
} | ||
|
||
public static class Builder implements VariablesBuilder, ValuesBuilder { | ||
public Builder() { | ||
} | ||
|
||
private List<Variable> variables = new ArrayList<>(); | ||
|
||
private List<List<RdfValue>> values = new ArrayList<>(); | ||
|
||
private List<RdfValue> currentValues = new ArrayList<>(); | ||
|
||
@Override | ||
public VariablesBuilder variables(Variable... variable) { | ||
Arrays.stream(variable).forEach(this.variables::add); | ||
return this; | ||
} | ||
|
||
/** | ||
* Provide another value. This will fill up the current solution sequence. If this value is the last one (i.e. | ||
* the solution sequence now is of the same length as the list of variables), the current solution sequence is | ||
* recorded and a new solution sequence begins. | ||
* | ||
* @param value | ||
* @return | ||
*/ | ||
@Override | ||
public ValuesBuilder value(RdfValue value) { | ||
this.currentValues.add(valueOrUndef(value)); | ||
if (currentValues.size() >= variables.size()) { | ||
this.values.add(currentValues); | ||
currentValues = new ArrayList<>(); | ||
} | ||
return this; | ||
} | ||
|
||
@Override | ||
public ValuesBuilder values(RdfValue... values) { | ||
if (this.variables.size() == 1) { | ||
for (int i = 0; i < values.length; i++) { | ||
this.values.add(List.of(valueOrUndef(values[i]))); | ||
} | ||
} else if (this.variables.size() == values.length) { | ||
this.values.add(Stream.of(values).map(Values::valueOrUndef).collect(Collectors.toList())); | ||
} else { | ||
throw new IllegalArgumentException( | ||
"Provided list of values must match length of variables, or there must be only one variable."); | ||
} | ||
return this; | ||
} | ||
|
||
@Override | ||
public ValuesBuilder values(Collection<RdfValue> values) { | ||
return values(values.toArray(i -> new RdfValue[i])); | ||
} | ||
|
||
@Override | ||
public ValuesBuilder iriValue(IRI value) { | ||
return value(Rdf.iri(value)); | ||
} | ||
|
||
@Override | ||
public ValuesBuilder iriValues(IRI... values) { | ||
return values(Stream.of(values).map(Rdf::iri).toArray(i -> new RdfValue[i])); | ||
} | ||
|
||
@Override | ||
public ValuesBuilder iriValues(Collection<IRI> values) { | ||
return iriValues(values.toArray(i -> new IRI[i])); | ||
} | ||
|
||
@Override | ||
public Values build() { | ||
if (this.values.isEmpty()) { | ||
throw new IllegalArgumentException("No values provided"); | ||
} | ||
if (!this.currentValues.isEmpty()) { | ||
throw new IllegalArgumentException( | ||
"Current solution sequence is not finished - you added too few or too many values."); | ||
} | ||
RdfValue[][] values = new RdfValue[this.values.size()][this.variables.size()]; | ||
for (int i = 0; i < this.values.size(); i++) { | ||
List<RdfValue> current = this.values.get(i); | ||
if (current.size() != this.variables.size()) { | ||
throw new IllegalArgumentException( | ||
String.format( | ||
"You provided $d values for $d variables", | ||
current.size(), | ||
this.variables.size())); | ||
} | ||
for (int j = 0; j < current.size(); j++) { | ||
values[i][j] = current.get(j); | ||
} | ||
} | ||
return new Values(this.variables.toArray(size -> new Variable[size]), values); | ||
} | ||
} | ||
|
||
public interface VariablesBuilder { | ||
|
||
public VariablesBuilder variables(Variable... variable); | ||
|
||
public ValuesBuilder value(RdfValue value); | ||
|
||
public ValuesBuilder values(RdfValue... values); | ||
|
||
public ValuesBuilder values(Collection<RdfValue> values); | ||
|
||
public ValuesBuilder iriValue(IRI value); | ||
|
||
public ValuesBuilder iriValues(IRI... values); | ||
|
||
public ValuesBuilder iriValues(Collection<IRI> values); | ||
} | ||
|
||
public interface ValuesBuilder { | ||
public ValuesBuilder value(RdfValue value); | ||
|
||
public ValuesBuilder values(RdfValue... values); | ||
|
||
public ValuesBuilder values(Collection<RdfValue> values); | ||
|
||
public ValuesBuilder iriValue(IRI value); | ||
|
||
public ValuesBuilder iriValues(IRI... values); | ||
|
||
public ValuesBuilder iriValues(Collection<IRI> values); | ||
|
||
public Values build(); | ||
} | ||
|
||
private static RdfValue valueOrUndef(RdfValue value) { | ||
if (value == null) { | ||
return UNDEF; | ||
} | ||
return value; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.