1111package org .eclipse .rdf4j .sail .shacl .ast ;
1212
1313import java .util .ArrayList ;
14+ import java .util .Collections ;
1415import java .util .HashSet ;
16+ import java .util .LinkedHashSet ;
1517import java .util .List ;
1618import java .util .Objects ;
1719import java .util .Set ;
@@ -31,10 +33,10 @@ public class StatementMatcher {
3133 private final Variable <IRI > predicate ;
3234 private final Variable <? extends Value > object ;
3335
34- // private final Set<String> varNames;
36+ // private final Set<String> varNames;
3537 private final Targetable origin ;
3638
37- private final Set <String > inheritedVarNames ;
39+ private Set <String > inheritedVarNames ;
3840
3941 private List <StatementMatcher > subset = List .of ();
4042
@@ -171,7 +173,7 @@ private void addSubset(StatementMatcher s) {
171173
172174 public static List <StatementMatcher > swap (List <StatementMatcher > statementMatchers , Variable <?> existingVariable ,
173175 Variable <?> newVariable ) {
174- if (statementMatchers .size () == 0 ) {
176+ if (statementMatchers .isEmpty () ) {
175177 return List .of ();
176178 }
177179 if (statementMatchers .size () == 1 ) {
@@ -216,37 +218,47 @@ private static String formatForToString(String field, String name, Value value)
216218
217219 private StatementMatcher swap (Variable <?> existingVariable , Variable <?> newVariable ) {
218220 String subjectName = getSubjectName ();
221+ String subjectBasename = getSubjectBasename ();
219222 Resource subjectValue = getSubjectValue ();
223+
220224 String predicateName = getPredicateName ();
225+ String predicateBasename = getPredicateBasename ();
221226 IRI predicateValue = getPredicateValue ();
227+
222228 String objectName = getObjectName ();
229+ String objectBasename = getObjectBasename ();
223230 Value objectValue = getObjectValue ();
231+
224232 boolean changed = false ;
225233
226234 if (Objects .equals (existingVariable .name , subjectName )
227235 && Objects .equals (existingVariable .value , subjectValue )) {
228236 changed = true ;
229237 subjectName = newVariable .name ;
230238 subjectValue = (Resource ) newVariable .value ;
239+ subjectBasename = newVariable .baseName ;
231240 }
232241
233242 if (Objects .equals (existingVariable .name , predicateName )
234243 && Objects .equals (existingVariable .value , predicateValue )) {
235244 changed = true ;
236245 predicateName = newVariable .name ;
237246 predicateValue = (IRI ) newVariable .value ;
247+ predicateBasename = newVariable .baseName ;
238248 }
239249
240250 if (Objects .equals (existingVariable .name , objectName ) && Objects .equals (existingVariable .value , objectValue )) {
241251 changed = true ;
242252 objectName = newVariable .name ;
243253 objectValue = newVariable .value ;
254+ objectBasename = newVariable .baseName ;
244255 }
245256
246257 if (changed ) {
247258 assert subset .isEmpty ();
248- return new StatementMatcher (new Variable <>(subjectName , subjectValue ),
249- new Variable <>(predicateName , predicateValue ), new Variable <>(objectName , objectValue ), origin ,
259+ return new StatementMatcher (new Variable <>(subjectName , subjectValue , subjectBasename ),
260+ new Variable <>(predicateName , predicateValue , predicateBasename ),
261+ new Variable <>(objectName , objectValue , objectBasename ), origin ,
250262 inheritedVarNames );
251263 }
252264 return this ;
@@ -268,6 +280,10 @@ public String getSubjectName() {
268280 return subject .name ;
269281 }
270282
283+ public String getSubjectBasename () {
284+ return subject .baseName ;
285+ }
286+
271287 public Resource getSubjectValue () {
272288 return subject .value ;
273289 }
@@ -280,6 +296,10 @@ public String getPredicateName() {
280296 return predicate .name ;
281297 }
282298
299+ public String getPredicateBasename () {
300+ return predicate .baseName ;
301+ }
302+
283303 public IRI getPredicateValue () {
284304 return predicate .value ;
285305 }
@@ -292,6 +312,10 @@ public String getObjectName() {
292312 return object .name ;
293313 }
294314
315+ public String getObjectBasename () {
316+ return object .baseName ;
317+ }
318+
295319 public Value getObjectValue () {
296320 return object .value ;
297321 }
@@ -324,6 +348,7 @@ public int hashCode() {
324348
325349 public String getSparqlValuesDecl (Set <String > varNamesRestriction , boolean addInheritedVarNames ,
326350 Set <String > varNamesInQueryFragment ) {
351+
327352 StringBuilder sb = new StringBuilder ("VALUES ( " );
328353 if (subject .name != null && varNamesRestriction .contains (subject .name ) ||
329354 subject .baseName != null && varNamesRestriction .contains (subject .baseName )) {
@@ -362,13 +387,13 @@ public String getSparqlValuesDecl(Set<String> varNamesRestriction, boolean addIn
362387 return sb .toString ();
363388 }
364389
365- public Set <String > getVarNames (Set <String > varNamesRestriction , boolean addInheritedVarNames ,
390+ public LinkedHashSet <String > getVarNames (Set <String > varNamesRestriction , boolean addInheritedVarNames ,
366391 Set <String > varNamesInQueryFragment ) {
367392 if (varNamesRestriction .isEmpty ()) {
368- return Set . of ();
393+ return new LinkedHashSet <> ();
369394 }
370395
371- HashSet <String > ret = new HashSet <>();
396+ LinkedHashSet <String > ret = new LinkedHashSet <>();
372397 if (subject .name != null && varNamesRestriction .contains (subject .name )
373398 && varNamesInQueryFragment .contains (subject .name )) {
374399 ret .add (subject .name );
@@ -448,6 +473,26 @@ public boolean hasObject(Variable<Value> variable) {
448473 return variable .name .equals (object .name );
449474 }
450475
476+ public Set <String > getInheritedVarNames () {
477+ return Set .copyOf (inheritedVarNames );
478+ }
479+
480+ public Set <String > getVarNames () {
481+ Set <String > varNames = new HashSet <>();
482+
483+ if (subject .name != null ) {
484+ varNames .add (subject .name );
485+ }
486+ if (predicate .name != null ) {
487+ varNames .add (predicate .name );
488+ }
489+ if (object .name != null ) {
490+ varNames .add (object .name );
491+ }
492+
493+ return Collections .unmodifiableSet (varNames );
494+ }
495+
451496 public static class StableRandomVariableProvider {
452497
453498 // We just need a random base that isn't used elsewhere in the ShaclSail, but we don't want it to be stable so
@@ -471,9 +516,12 @@ public StableRandomVariableProvider(String prefix) {
471516 * increments of one.
472517 *
473518 * @param inputQuery the query string that should be normalized
519+ * @param union
474520 * @return a normalized query string
475521 */
476- public static String normalize (String inputQuery ) {
522+ public static String normalize (String inputQuery , List <? extends Variable > protectedVars ,
523+ List <StatementMatcher > union ) {
524+
477525 if (!inputQuery .contains (BASE )) {
478526 return inputQuery ;
479527 }
@@ -499,18 +547,30 @@ public static String normalize(String inputQuery) {
499547 if (lowest == 0 && incrementsOfOne ) {
500548 return inputQuery ;
501549 }
550+ String joinedProtectedVars = protectedVars .stream ()
551+ .map (Variable ::getName )
552+ .filter (Objects ::nonNull )
553+ .filter (s -> s .contains (BASE ))
554+ .collect (Collectors .joining ());
502555
503- return normalizeRange (inputQuery , lowest , highest );
556+ return normalizeRange (inputQuery , lowest , highest , joinedProtectedVars , union );
504557 }
505558
506- private static String normalizeRange (String inputQuery , int lowest , int highest ) {
559+ private static String normalizeRange (String inputQuery , int lowest , int highest , String joinedProtectedVars ,
560+ List <StatementMatcher > union ) {
507561
508562 String normalizedQuery = inputQuery ;
509563 for (int i = 0 ; i <= highest ; i ++) {
510- if (!normalizedQuery .contains (BASE + i + "_" )) {
564+ String replacement = BASE + i + "_" ;
565+ if (!normalizedQuery .contains (replacement )) {
511566 for (int j = Math .max (i + 1 , lowest ); j <= highest ; j ++) {
512- if (normalizedQuery .contains (BASE + j + "_" )) {
513- normalizedQuery = normalizedQuery .replace (BASE + j + "_" , BASE + i + "_" );
567+ String original = BASE + j + "_" ;
568+ if (normalizedQuery .contains (original )) {
569+ if (joinedProtectedVars .contains (original )) {
570+ continue ;
571+ }
572+ normalizedQuery = normalizedQuery .replace (original , replacement );
573+ replaceInStatementMatcher (union , original , replacement );
514574 break ;
515575 }
516576 }
@@ -520,6 +580,13 @@ private static String normalizeRange(String inputQuery, int lowest, int highest)
520580 return normalizedQuery ;
521581 }
522582
583+ private static void replaceInStatementMatcher (List <StatementMatcher > statementMatchers , String original ,
584+ String replacement ) {
585+ for (StatementMatcher statementMatcher : statementMatchers ) {
586+ statementMatcher .replaceVariableName (original , replacement );
587+ }
588+ }
589+
523590 public Variable <Value > next () {
524591 counter ++;
525592
@@ -538,6 +605,44 @@ public Variable<Value> current() {
538605 }
539606 }
540607
608+ private void replaceVariableName (String original , String replacement ) {
609+
610+ if (subject .name != null && subject .name .contains (original )) {
611+ subject .name = subject .name .replace (original , replacement );
612+ }
613+ if (subject .baseName != null && subject .baseName .contains (original )) {
614+ subject .baseName = subject .baseName .replace (original , replacement );
615+ }
616+ if (predicate .name != null && predicate .name .contains (original )) {
617+ predicate .name = predicate .name .replace (original , replacement );
618+ }
619+ if (predicate .baseName != null && predicate .baseName .contains (original )) {
620+ predicate .baseName = predicate .baseName .replace (original , replacement );
621+ }
622+ if (object .name != null && object .name .contains (original )) {
623+ object .name = object .name .replace (original , replacement );
624+ }
625+ if (object .baseName != null && object .baseName .contains (original )) {
626+ object .baseName = object .baseName .replace (original , replacement );
627+ }
628+
629+ boolean contains = false ;
630+ for (String inheritedVarName : inheritedVarNames ) {
631+ if (inheritedVarName .contains (original )) {
632+ contains = true ;
633+ break ;
634+ }
635+ }
636+ if (contains ) {
637+ HashSet <String > newInheritedVarNames = new HashSet <>();
638+ for (String inheritedVarName : inheritedVarNames ) {
639+ newInheritedVarNames .add (inheritedVarName .replace (original , replacement ));
640+ }
641+ inheritedVarNames = newInheritedVarNames ;
642+ }
643+
644+ }
645+
541646 public static class Variable <T extends Value > {
542647 public static final Variable <Value > VALUE = new Variable <>("value" );
543648 public static final Variable <Value > THIS = new Variable <>("this" );
@@ -562,6 +667,12 @@ public Variable(Variable<?> baseVariable, String name) {
562667 this .baseName = baseVariable .name ;
563668 }
564669
670+ public Variable (String name , T value , String baseName ) {
671+ this .name = name ;
672+ this .value = value ;
673+ this .baseName = baseName ;
674+ }
675+
565676 public Variable (T value ) {
566677 this .value = value ;
567678 }
0 commit comments