-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from biscuit-auth/3rd-party-blocks
3rd party block support
- Loading branch information
Showing
39 changed files
with
2,061 additions
and
986 deletions.
There are no files selected for viewing
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
14 changes: 0 additions & 14 deletions
14
src/main/java/com/clevercloud/biscuit/datalog/AuthorizedWorld.java
This file was deleted.
Oops, something went wrong.
200 changes: 120 additions & 80 deletions
200
src/main/java/com/clevercloud/biscuit/datalog/Combinator.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 |
---|---|---|
@@ -1,112 +1,152 @@ | ||
package com.clevercloud.biscuit.datalog; | ||
|
||
import com.clevercloud.biscuit.datalog.expressions.Expression; | ||
import io.vavr.Tuple2; | ||
import io.vavr.control.Option; | ||
|
||
import java.io.Serializable; | ||
import java.util.*; | ||
|
||
public final class Combinator implements Serializable { | ||
private final MatchedVariables variables; | ||
private final List<Predicate> next_predicates; | ||
private final Set<Fact> all_facts; | ||
private final Predicate pred; | ||
private final Iterator<Fact> fit; | ||
private Combinator current_it; | ||
import java.util.function.Supplier; | ||
import java.util.stream.Stream; | ||
|
||
public final class Combinator implements Serializable, Iterator<Tuple2<Origin, Map<Long, Term>>> { | ||
private MatchedVariables variables; | ||
private final Supplier<Stream<Tuple2<Origin, Fact>>> allFacts; | ||
private final List<Predicate> predicates; | ||
private final Iterator<Tuple2<Origin, Fact>> currentFacts; | ||
private Combinator currentIt; | ||
private final SymbolTable symbols; | ||
|
||
public Option<MatchedVariables> next() { | ||
while(true) { | ||
if (this.current_it != null) { | ||
Option<MatchedVariables> next_vars_opt = this.current_it.next(); | ||
// the iterator is empty, try with the next fact | ||
if(next_vars_opt.isEmpty()) { | ||
this.current_it = null; | ||
continue; | ||
} | ||
return next_vars_opt; | ||
} | ||
private Origin currentOrigin; | ||
|
||
// we iterate over the facts that match the current predicate | ||
if (this.fit.hasNext()) { | ||
final Fact current_fact = this.fit.next(); | ||
private Option<Tuple2<Origin, Map<Long, Term>>> nextElement; | ||
|
||
// create a new MatchedVariables in which we fix variables we could unify from our first predicate and the current fact | ||
MatchedVariables vars = this.variables.clone(); | ||
boolean match_ids = true; | ||
@Override | ||
public boolean hasNext() { | ||
if (this.nextElement != null && this.nextElement.isDefined()) { | ||
return true; | ||
} | ||
this.nextElement = getNext(); | ||
return this.nextElement.isDefined(); | ||
} | ||
|
||
@Override | ||
public Tuple2<Origin, Map<Long, Term>> next() { | ||
if (this.nextElement == null || !this.nextElement.isDefined()) { | ||
this.nextElement = getNext(); | ||
} | ||
if (this.nextElement == null || !this.nextElement.isDefined()) { | ||
throw new NoSuchElementException(); | ||
} else { | ||
Tuple2<Origin, Map<Long, Term>> t = this.nextElement.get(); | ||
this.nextElement = Option.none(); | ||
return t; | ||
} | ||
} | ||
|
||
// we know the fact matches the predicate's format so they have the same number of terms | ||
// fill the MatchedVariables before creating the next combinator | ||
for (int i = 0; i < pred.terms().size(); ++i) { | ||
final Term id = pred.terms().get(i); | ||
if (id instanceof Term.Variable) { | ||
final long key = ((Term.Variable) id).value(); | ||
final Term value = current_fact.predicate().terms().get(i); | ||
public Option<Tuple2<Origin, Map<Long, Term>>> getNext() { | ||
if (this.predicates.isEmpty()) { | ||
final Option<Map<Long, Term>> v_opt = this.variables.complete(); | ||
if (v_opt.isEmpty()) { | ||
return Option.none(); | ||
} else { | ||
Map<Long, Term> variables = v_opt.get(); | ||
// if there were no predicates, | ||
// we should return a value, but only once. To prevent further | ||
// successful calls, we create a set of variables that cannot | ||
// possibly be completed, so the next call will fail | ||
Set<Long> set = new HashSet<>(); | ||
set.add((long) 0); | ||
|
||
this.variables = new MatchedVariables(set); | ||
return Option.some(new Tuple2<>(new Origin(), variables)); | ||
} | ||
} | ||
|
||
if (!vars.insert(key, value)) { | ||
match_ids = false; | ||
while (true) { | ||
if (this.currentIt == null) { | ||
Predicate predicate = this.predicates.get(0); | ||
|
||
while (true) { | ||
// we iterate over the facts that match the current predicate | ||
if (this.currentFacts.hasNext()) { | ||
final Tuple2<Origin, Fact> t = this.currentFacts.next(); | ||
Origin currentOrigin = t._1.clone(); | ||
Fact fact = t._2; | ||
|
||
// create a new MatchedVariables in which we fix variables we could unify from our first predicate and the current fact | ||
MatchedVariables vars = this.variables.clone(); | ||
boolean matchTerms = true; | ||
|
||
// we know the fact matches the predicate's format so they have the same number of terms | ||
// fill the MatchedVariables before creating the next combinator | ||
for (int i = 0; i < predicate.terms().size(); ++i) { | ||
final Term term = predicate.terms().get(i); | ||
if (term instanceof Term.Variable) { | ||
final long key = ((Term.Variable) term).value(); | ||
final Term value = fact.predicate().terms().get(i); | ||
|
||
if (!vars.insert(key, value)) { | ||
matchTerms = false; | ||
} | ||
if (!matchTerms) { | ||
break; | ||
} | ||
} | ||
} | ||
if (!match_ids) { | ||
break; | ||
|
||
// the fact did not match the predicate, try the next one | ||
if (!matchTerms) { | ||
continue; | ||
} | ||
} | ||
} | ||
|
||
if (!match_ids) { | ||
continue; | ||
} | ||
// there are no more predicates to check | ||
if (this.predicates.size() == 1) { | ||
final Option<Map<Long, Term>> v_opt = vars.complete(); | ||
if (v_opt.isEmpty()) { | ||
continue; | ||
} else { | ||
return Option.some(new Tuple2<>(currentOrigin, v_opt.get())); | ||
} | ||
} else { | ||
this.currentOrigin = currentOrigin; | ||
// we found a matching fact, we create a new combinator over the rest of the predicates | ||
// no need to copy all the expressions at all levels | ||
this.currentIt = new Combinator(vars, predicates.subList(1, predicates.size()), this.allFacts, this.symbols); | ||
} | ||
break; | ||
|
||
// there are no more predicates to check | ||
if (next_predicates.isEmpty()) { | ||
final Option<Map<Long, Term>> v_opt = vars.complete(); | ||
if(v_opt.isEmpty()) { | ||
continue; | ||
} else { | ||
return Option.some(vars); | ||
return Option.none(); | ||
} | ||
} else { | ||
// we found a matching fact, we create a new combinator over the rest of the predicates | ||
// no need to copy all of the expressions at all levels | ||
this.current_it = new Combinator(vars, next_predicates, this.all_facts, this.symbols); | ||
} | ||
} else { | ||
break; | ||
} | ||
} | ||
|
||
return Option.none(); | ||
} | ||
|
||
public List<Map<Long, Term>> combine() { | ||
final List<Map<Long, Term>> variables = new ArrayList<>(); | ||
|
||
while(true) { | ||
Option<MatchedVariables> res = this.next(); | ||
|
||
if(res.isEmpty()) { | ||
return variables; | ||
if (this.currentIt == null) { | ||
return Option.none(); | ||
} | ||
|
||
Option<Map<Long, Term>> vars = res.get().complete(); | ||
if(vars.isDefined()) { | ||
variables.add(vars.get()); | ||
Option<Tuple2<Origin, Map<Long, Term>>> opt = this.currentIt.getNext(); | ||
|
||
if (opt.isDefined()) { | ||
Tuple2<Origin, Map<Long, Term>> t = opt.get(); | ||
return Option.some(new Tuple2<>(t._1.union(currentOrigin), t._2)); | ||
} else { | ||
currentOrigin = null; | ||
currentIt = null; | ||
} | ||
} | ||
} | ||
|
||
|
||
public Combinator(final MatchedVariables variables, final List<Predicate> predicates, | ||
final Set<Fact> all_facts, final SymbolTable symbols) { | ||
Supplier<Stream<Tuple2<Origin, Fact>>> all_facts, final SymbolTable symbols) { | ||
this.variables = variables; | ||
this.all_facts = all_facts; | ||
this.current_it = null; | ||
this.pred = predicates.get(0); | ||
this.fit = all_facts.stream().filter((fact) -> fact.match_predicate(predicates.get(0))).iterator(); | ||
this.allFacts = all_facts; | ||
this.currentIt = null; | ||
this.predicates = predicates; | ||
this.currentFacts = all_facts.get().filter((tuple) -> tuple._2.match_predicate(predicates.get(0))).iterator(); | ||
this.symbols = symbols; | ||
|
||
final List<Predicate> next_predicates = new ArrayList<>(); | ||
for (int i = 1; i < predicates.size(); ++i) { | ||
next_predicates.add(predicates.get(i)); | ||
} | ||
this.next_predicates = next_predicates; | ||
this.currentOrigin = null; | ||
this.nextElement = null; | ||
} | ||
} |
Oops, something went wrong.