Package lupos.rif.visitor

Source Code of lupos.rif.visitor.BuildOperatorGraphRuleVisitor

/**
* Copyright (c) 2013, Institute of Information Systems (Sven Groppe and contributors of LUPOSDATE), University of Luebeck
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
*   - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
*     disclaimer.
*   - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
*     following disclaimer in the documentation and/or other materials provided with the distribution.
*   - Neither the name of the University of Luebeck nor the names of its contributors may be used to endorse or promote
*     products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lupos.rif.visitor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import lupos.datastructures.items.Item;
import lupos.datastructures.items.Triple;
import lupos.datastructures.items.Variable;
import lupos.datastructures.items.literal.URILiteral;
import lupos.engine.operators.BasicOperator;
import lupos.engine.operators.OperatorIDTuple;
import lupos.engine.operators.index.BasicIndexScan;
import lupos.engine.operators.index.EmptyIndexScan;
import lupos.engine.operators.index.Root;
import lupos.engine.operators.multiinput.Union;
import lupos.engine.operators.multiinput.join.IndexJoinWithDuplicateElimination;
import lupos.engine.operators.multiinput.join.Join;
import lupos.engine.operators.multiinput.minus.Minus;
import lupos.engine.operators.singleinput.Construct;
import lupos.engine.operators.singleinput.MakeBooleanResult;
import lupos.engine.operators.singleinput.Result;
import lupos.engine.operators.singleinput.generate.Generate;
import lupos.engine.operators.singleinput.modifiers.distinct.Distinct;
import lupos.engine.operators.tripleoperator.TriplePattern;
import lupos.misc.util.ImmutableIterator;
import lupos.rif.IExpression;
import lupos.rif.RIFException;
import lupos.rif.builtin.RIFBuiltinFactory;
import lupos.rif.datatypes.Predicate;
import lupos.rif.model.Conjunction;
import lupos.rif.model.Constant;
import lupos.rif.model.Disjunction;
import lupos.rif.model.Document;
import lupos.rif.model.Equality;
import lupos.rif.model.ExistExpression;
import lupos.rif.model.External;
import lupos.rif.model.Rule;
import lupos.rif.model.RulePredicate;
import lupos.rif.model.Uniterm;
import lupos.rif.operator.BooleanIndexScan;
import lupos.rif.operator.ConstructEquality;
import lupos.rif.operator.ConstructPredicate;
import lupos.rif.operator.EqualityFilter;
import lupos.rif.operator.InsertTripleIndexScan;
import lupos.rif.operator.IteratorIndexScan;
import lupos.rif.operator.PredicateIndexScan;
import lupos.rif.operator.PredicatePattern;
import lupos.rif.operator.RuleFilter;
import lupos.sparql1_1.operatorgraph.helper.IndexScanCreatorInterface;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

public class BuildOperatorGraphRuleVisitor extends BaseGraphBuilder {
  protected final String VARIABLE_PREDICATE = "?";

  protected final Map<KEY, Set<BasicOperator>> tripleProducer = new LinkedHashMap<KEY, Set<BasicOperator>>();
  protected final Map<KEY, Set<BasicOperator>> tripleConsumer = new LinkedHashMap<KEY, Set<BasicOperator>>();
  protected Multimap<IExpression, IExpression> equalityMap;
  protected boolean usesEqualities = false;

  public static abstract class KEY implements Iterable<KEY>{
    // used as unifying superclass for the keys of triple patterns, predicates and equality!
    protected abstract int getNumberOfPossibleMatchingKeys();

    @Override
    public Iterator<KEY> iterator(){
      // consider all possible combinations!
      return new ImmutableIterator<KEY>(){
        final int max = 1 << KEY.this.getNumberOfPossibleMatchingKeys();
        int current = 0;
        @Override
        public boolean hasNext() {
          return this.current < this.max;
        }
        @Override
        public KEY next() {
          if(this.hasNext()){
            final KEY result = KEY.this.getKey(this.current);
            this.current++;
            return result;
          } else {
            return null;
          }
        }
      };
    }

    protected abstract KEY getKey(int current);

    protected static Variable dummyVariable = new Variable("d");
  }

  public static class KeyEquality extends KEY {
    private final static int hashValue = 157639892; // just take arbitrary number for hash vale
    @Override
    public int hashCode(){
      return KeyEquality.hashValue;
    }
    @Override
    public boolean equals(final Object other){
      return (other instanceof KeyEquality);
    }
    @Override
    protected int getNumberOfPossibleMatchingKeys() {
      return 1;
    }
    @Override
    protected KEY getKey(final int current) {
      return this;
    }
  }

  private final static KeyEquality keyEquality = new KeyEquality();

  public static class KeyTriplePattern extends KEY {
    private final TriplePattern triplePattern;
    public KeyTriplePattern(final TriplePattern triplePattern){
      this.triplePattern = triplePattern;
    }
    @Override
    public int hashCode(){
      int result =0;
      for(final Item item: this.triplePattern){
        if(!item.isVariable()){
          result = (int)((long) result + item.hashCode()) % Integer.MAX_VALUE;
        }
      }
      return result;
    }
    @Override
    public boolean equals(final Object object){
      if(object instanceof KeyTriplePattern){
        final KeyTriplePattern other = (KeyTriplePattern) object;
        for(int i=0; i<3; i++){
          final Item thisItem = this.triplePattern.getPos(i);
          final Item otherItem = other.triplePattern.getPos(i);
          if(!(thisItem.isVariable() == otherItem.isVariable() && (thisItem.isVariable()
              && otherItem.isVariable() || thisItem.equals(otherItem)))){
            return false;
          }
        }
        return true;
      } else {
        return false;
      }
    }

    public boolean mayConsume(final KeyTriplePattern other){
      final Iterator<Item> thisIterator = this.triplePattern.iterator();
      final Iterator<Item> otherIterator = other.triplePattern.iterator();
      while(thisIterator.hasNext()){
        final Item thisItem = thisIterator.next();
        final Item otherItem = otherIterator.next();
        if(!(otherItem.isVariable() || thisItem.isVariable() || thisItem.equals(otherItem))){
          return false;
        }
      }
      return true;
    }

    @Override
    protected int getNumberOfPossibleMatchingKeys() {
      int number=0;
      for(final Item item: this.triplePattern){
        if(!item.isVariable()) {
          number++;
        }
      }
      return number;
    }
    @Override
    protected KEY getKey(final int current) {
      final Item[] items = new Item[3];
      int bitValue = 1;
      int index = 0;
      for(final Item item: this.triplePattern){
        if(item.isVariable()){
          items[index] = KEY.dummyVariable;
        } else {
          items[index] = ((current / bitValue) % 2 == 0)? KEY.dummyVariable : item;
          bitValue*=2;
        }
        index++;
      }
      return new KeyTriplePattern(new TriplePattern(items));
    }
  }

  public static class KeyPredicatePattern extends KEY {
    private final PredicatePattern predicatePattern;
    public KeyPredicatePattern(final PredicatePattern predicatePattern){
      this.predicatePattern = predicatePattern;
    }
    @Override
    public int hashCode(){
      int result =0;
      for(final Item item: this.predicatePattern){
        if(!item.isVariable()){
          result = (int)((long) result + item.hashCode()) % Integer.MAX_VALUE;
        }
      }
      return result;
    }
    @Override
    public boolean equals(final Object object){
      if(object instanceof KeyPredicatePattern){
        final KeyPredicatePattern other = (KeyPredicatePattern) object;
        final Iterator<Item> thisIterator = this.predicatePattern.iterator();
        final Iterator<Item> otherIterator = other.predicatePattern.iterator();
        while(thisIterator.hasNext()){
          final Item thisItem = thisIterator.next();
          final Item otherItem = otherIterator.next();
          if(!(thisItem.isVariable() == otherItem.isVariable() && (thisItem.isVariable()
              && otherItem.isVariable() || thisItem.equals(otherItem)))){
            return false;
          }
        }
        return true;
      } else {
        return false;
      }
    }
    @Override
    protected int getNumberOfPossibleMatchingKeys() {
      int number=-1;
      for(final Item item: this.predicatePattern){
        if(!item.isVariable()) {
          number++;
        }
      }
      return number;
    }

    @Override
    protected KEY getKey(final int current) {
      final Item[] items = new Item[this.predicatePattern.getPatternItems().size()];
      int bitValue = 1;
      int index = 0;
      for(final Item item: this.predicatePattern.getPatternItems()){
        if(item.isVariable()){
          items[index] = KEY.dummyVariable;
        } else {
          items[index] = ((current / bitValue) % 2 == 0)? KEY.dummyVariable : item;
          bitValue*=2;
        }
        index++;
      }
      return new KeyPredicatePattern(new PredicatePattern(this.predicatePattern.getPredicateName(), items));
    }
  }

  public BuildOperatorGraphRuleVisitor(final IndexScanCreatorInterface indexScanCreator) {
    super(indexScanCreator);
  }


  @Override
  public Object visit(final Document obj, final Object arg) throws RIFException {
    this.tripleProducer.clear();
    this.tripleConsumer.clear();
    this.equalityMap = HashMultimap.create();
    this.usesEqualities = false;
    // 1. Fakten muessen als erstes ausgewertet werden und dann in allen
    // Operatorbaeumen beruecksichtigt werden.
    this.predicateIndex = null;
    InsertTripleIndexScan insertTripleIndex = null;
    for (final IExpression fact : obj.getFacts()) {
      if (fact instanceof Equality) {
        final Equality eq = (Equality) fact;
        this.equalityMap.put(eq.leftExpr, eq.rightExpr);
        this.equalityMap.put(eq.rightExpr, eq.leftExpr);
        this.usesEqualities = true;
      } else {
        final Object item = ((RulePredicate) fact).toDataStructure();
        if (item instanceof Triple) {
          insertTripleIndex = insertTripleIndex == null ? new InsertTripleIndexScan(this.indexScanCreator) : insertTripleIndex;
          insertTripleIndex.addTripleFact((Triple) item);
        } else if (item instanceof Predicate) {
          this.predicateIndex = this.predicateIndex == null ? new PredicateIndexScan() : this.predicateIndex;
          this.predicateIndex.addPredicateFact((Predicate) item);
        }
      }
    }
    if (insertTripleIndex != null){
      this.indexScanCreator.getRoot().addSucceedingOperator(new OperatorIDTuple(insertTripleIndex, 0));
      insertTripleIndex.addPrecedingOperator(this.indexScanCreator.getRoot());
    }
    if (this.predicateIndex != null){
      this.indexScanCreator.getRoot().addSucceedingOperator(new OperatorIDTuple(this.predicateIndex, 1));
      this.predicateIndex.addPrecedingOperator(this.indexScanCreator.getRoot());
    }

    // 2. ueber alle Regeln gehen und mitschreiben, welche
    // Tripel-Praedikate
    // produziert werden, Externals koennen nicht vorkommen
    for (final Rule rule : obj.getRules()) {
      if (rule.isImplication()) {
        for (final IExpression expr : rule.getHeadExpressions()) {
          // Tripel mit variablen Praedikat wird generiert
          if (expr instanceof RulePredicate) {
            final RulePredicate pred = (RulePredicate) expr;
            final BasicOperator pattern = this.generatePattern(pred, arg);
            final KEY key = (pattern instanceof TriplePattern)?
                new KeyTriplePattern((TriplePattern)pattern):
                new KeyPredicatePattern((PredicatePattern)pattern);
            this.tripleProducer.put(key, new LinkedHashSet<BasicOperator>());
          } else if (expr instanceof Equality) {
            this.tripleProducer.put(BuildOperatorGraphRuleVisitor.keyEquality, new LinkedHashSet<BasicOperator>());
            this.usesEqualities = true;
          }
        }
      }
    }

    // 3. Operatorgraphen fuer einzelne Regeln berechnen
    final List<BasicOperator> subOperators = new ArrayList<BasicOperator>();
    for (final Rule rule : obj.getRules()) {
      if (rule.isImplication()) {
        final BasicOperator result = (BasicOperator) rule.accept(this, arg);
        subOperators.add(result);
      }
    }

    // 4. Rekursive Verbindungen aufloesen
    for (final Entry<KEY, Set<BasicOperator>> entry : this.tripleProducer.entrySet()) {
      boolean consumerExists = false;
      // find all matching consumers by just getting the previous determined tripleConsumers...
      final Set<BasicOperator> consumers = this.tripleConsumer.get(entry.getKey());
      if(consumers!=null){
        consumerExists = true;
        // Kreuzverbindungen zwischen Produzenten und Konsumenten
        // herstellen
        for (final BasicOperator producer : entry.getValue()) {
          for (final BasicOperator consumer : consumers) {
            producer.addSucceedingOperator(new OperatorIDTuple(consumer, producer.getSucceedingOperators().size()));
            consumer.addPrecedingOperator(producer);
            // Sonderfall: Falls PredicatePattern ->
            // Dann: PredicatePattern -> Distinct ->
            // -----> should now be unnecessary with our new joins with duplicate elimination integrated! <----
//            if (consumer instanceof PredicatePattern) {
//              boolean distinctFound = false;
//              for (OperatorIDTuple opid : consumer
//                  .getSucceedingOperators())
//                if (opid.getOperator() instanceof Distinct)
//                  distinctFound = true;
//              if (!distinctFound) {
//                final Distinct distinct = new Distinct();
//                for(OperatorIDTuple opID: consumer.getSucceedingOperators()){
//                  distinct.getSucceedingOperators().add(new OperatorIDTuple(opID));
//                }
//                consumer.setSucceedingOperator(new OperatorIDTuple(distinct, 0));
//              }
//            }
          }
        }
      }
      // Wenn keine Konsumenten, dann Produzenten entfernen
      if (!consumerExists) {
        for (final BasicOperator producer : entry.getValue()) {
          if (producer instanceof Generate) {
            producer.removeFromOperatorGraph();
          }
        }
      }
    }

    // 5. Ergebniss aller Regeln in einem Result zusammenfuehren
    BasicOperator[] finalResults;
    if (obj.getConclusion() == null) {
      finalResults = new BasicOperator[1];
      finalResults[0] = new Result();
    } else {
      finalResults = this.patternFromConclusion(obj.getConclusion());
    }

    for(final BasicOperator finalResult: finalResults){
      // Verbindungen zum Endergebniss herstellen
      for (final BasicOperator subOperator : subOperators) {
        // Result immer auf linker Seite, damit keine Linksrekursion
        // auftreten kann
        if(!(finalResult instanceof TriplePattern && subOperator instanceof ConstructPredicate)
            &&!(finalResult instanceof PredicatePattern && (subOperator instanceof Construct || subOperator instanceof Generate))){
          if (!subOperator.getSucceedingOperators().isEmpty()) {
            final OperatorIDTuple temp = subOperator.getSucceedingOperators().get(0);
            subOperator.getSucceedingOperators().set(0,new OperatorIDTuple(finalResult, 0));
            finalResult.addPrecedingOperator(subOperator);
            subOperator.addSucceedingOperator(temp);
          } else {
            subOperator.setSucceedingOperator(new OperatorIDTuple(finalResult, 0));
            finalResult.addPrecedingOperator(subOperator);
          }
        }
      }
    }
    for(int i=0; i<finalResults.length; i++){
      BasicOperator finalResult = finalResults[i];
      if (subOperators.isEmpty()) {
        // Root verweist auf EmptyIndex und der direkt auf Result
        if (finalResult instanceof PredicatePattern || finalResult instanceof TriplePattern) {
          finalResult.removeFromOperatorGraph();
          finalResult = null;
        }
        final EmptyIndexScan empty = new EmptyIndexScan(finalResult == null ? null : new OperatorIDTuple(finalResult, 0));
        this.indexScanCreator.getRoot().addSucceedingOperator(new OperatorIDTuple(empty, this.indexScanCreator.getRoot().getSucceedingOperators().size()));
        empty.addPrecedingOperator(this.indexScanCreator.getRoot());
        if (finalResult == null) {
          finalResults[i] = empty;
        }
      }
    }

    if (this.booleanIndex != null
        && this.booleanIndex.getSucceedingOperators().isEmpty()){
      this.indexScanCreator.getRoot().removeSucceedingOperator(this.booleanIndex);
      this.booleanIndex.removePrecedingOperator(this.indexScanCreator.getRoot());
    }

    if(finalResults.length==1 && (finalResults[0] instanceof Result)){
      return finalResults[0];
    }

    // Falls Conclusion vorhanden, noch Result anhaengen, zum Sammeln der Ergebnisse
    final Result result = new Result();
    final BasicOperator makeBooleanResultOrResult;
    if (obj.getConclusion() != null && obj.getConclusion().getVariables().isEmpty()) {
      makeBooleanResultOrResult = new MakeBooleanResult();
      makeBooleanResultOrResult.addSucceedingOperator(result);
      result.addPrecedingOperator(makeBooleanResultOrResult);
    } else {
      makeBooleanResultOrResult = result;
    }

    for(final BasicOperator finalResult: finalResults){
      // DEBUG
      //finalResult.addSucceedingOperator(finalResult = new Distinct());
      finalResult.addSucceedingOperator(makeBooleanResultOrResult);
      makeBooleanResultOrResult.addPrecedingOperator(finalResult);
    }
    return result;
  }

  private BasicOperator[] patternFromConclusion(final IExpression conclusion) {
    BasicOperator[] result;
    if(conclusion instanceof Conjunction){
      final Conjunction conjunction = (Conjunction) conclusion;
      result = new BasicOperator[conjunction.exprs.size()];
      int i = 0;
      for(final IExpression elem: conjunction.exprs) {
        result[i] = this.onePatternFromConclusion(elem);
        i++;
      }
    } else {
      result = new BasicOperator[1];
      result[0] = this.onePatternFromConclusion(conclusion);
    }
    return result;
  }

  private BasicOperator onePatternFromConclusion(final IExpression conclusion){
    // Annahme, Conclusion ist Praedikat
    final RulePredicate pred = (RulePredicate) conclusion;
    if (pred.isTriple()) {
      return this.unitermToTriplePattern(pred);
    } else {
      final URILiteral predName = (URILiteral) pred.termName.accept(this,
          null);
      final List<Item> predItems = new ArrayList<Item>();
      for (final IExpression expr : pred.termParams) {
        final Item item = (Item) expr.accept(this, null);
        predItems.add(item);
      }
      return new PredicatePattern(predName, predItems.toArray(new Item[] {}));
    }
  }

  @Override
  public Object visit(final Rule obj, final Object arg) throws RIFException {
    // Besimmen, ob Triple, Predicates oder Equalities erstellt werden sollen
    final List<BasicOperator> resultOps = new ArrayList<BasicOperator>();
    final List<Equality> equalities = new ArrayList<Equality>();
    boolean generateTriples = false;
    boolean generatePredicates = false;
    boolean generateEqualities = false;
    for (final IExpression expr : obj.getHeadExpressions()) {
      if (expr instanceof RulePredicate) {
        if (((RulePredicate) expr).isTriple()) {
          generateTriples = true;
        } else {
          generatePredicates = true;
        }
      } else if (expr instanceof Equality) {
        generateEqualities = true;
        equalities.add((Equality) expr);
      }
    }

    // 1. Unteroperatorgraph berechnen
    BasicOperator subOperator = (BasicOperator) obj.getBody().accept(this, null);
    for(final OperatorIDTuple opID: subOperator.getSucceedingOperators()){
      opID.getOperator().removePrecedingOperator(subOperator);
    }
    subOperator.getSucceedingOperators().clear();
    // is one of the previous operators a join, which also eliminates duplicates?
    boolean foundJoin = false;
    BasicOperator current = subOperator;
    do {
      if(subOperator instanceof IndexJoinWithDuplicateElimination){
        foundJoin = true;
      } else {
        if(current.getPrecedingOperators().size()!=1){
          // Distinct is necessary!
          break;
        }
        current = current.getPrecedingOperators().get(0);
      }
    } while(!foundJoin);

    if(!foundJoin){
      // add DISTINCT operator in order to avoid infinity loops
      final Distinct distinct = new Distinct();
      subOperator.addSucceedingOperator(distinct);
      distinct.addPrecedingOperator(subOperator);
      subOperator = distinct;
    }

    // add nots under subOperator
    for(final IExpression not: obj.getNots()){
      // TODO check for recursion in not expression and throw error in that case
      // (In the current implementation, negation in rules retrieves reasonable results only for non-recursive rules)
      // first determine operator graph for the not expression:
      final BasicOperator notOperator = (BasicOperator) not.accept(this, null);
      for(final OperatorIDTuple opID: notOperator.getSucceedingOperators()){
        opID.getOperator().removePrecedingOperator(notOperator);
      }
      notOperator.getSucceedingOperators().clear();
      // now add a not operator below subOperator and notOperator!
      // Luckily, the semantics of the not operator is exactly the same as of the minus operator!
      final Minus minus = new Minus(false);
      subOperator.addSucceedingOperator(minus, 0);
      notOperator.addSucceedingOperator(minus, 1);
      minus.addPrecedingOperator(subOperator);
      minus.addPrecedingOperator(notOperator);
      subOperator = minus;
    }

    // 1. Construct erstellen
    if (generateTriples) {
      final Construct construct = new Construct();
      subOperator.addSucceedingOperator(construct);
      construct.addPrecedingOperator(subOperator);
      final List<TriplePattern> patterns = new ArrayList<TriplePattern>();
      for (final Uniterm term : obj.getHead().getPredicates()) {
        if (((RulePredicate) term).isTriple()) {
          final TriplePattern pattern = this.unitermToTriplePattern(term);
          patterns.add(pattern);
        }
      }
      construct.setTemplates(patterns);
      // Fuer jedes Triplepattern in Construct ein Generate fuer Inferenz erstellen
      // wird, falls es keinen Consumer gibt, spaeter wieder entfernt
      for (final TriplePattern pattern : construct.getTemplates()) {
        final Generate generateTriplesOp = new Generate(pattern.getItems());
        generateTriplesOp.addPrecedingOperator(subOperator);
        subOperator.addSucceedingOperator(new OperatorIDTuple(generateTriplesOp, subOperator.getSucceedingOperators().size()));
        generateTriplesOp.addPrecedingOperator(subOperator);
        // TripleProduzenten registrieren
        this.add(this.tripleProducer, new KeyTriplePattern(pattern), generateTriplesOp);
      }
      resultOps.add(construct);
    }

    // 2. ConstructPredicate erstellen
    if (generatePredicates) {
      final ConstructPredicate generate = new ConstructPredicate();
      subOperator.addSucceedingOperator(generate);
      generate.addPrecedingOperator(subOperator);
      for (final Uniterm term : obj.getHead().getPredicates()) {
        if (!((RulePredicate) term).isTriple()) {
          final URILiteral name = (URILiteral) term.termName.accept(this, arg);
          final List<Item> params = new ArrayList<Item>();
          for (final IExpression expr : term.termParams) {
            final Item item = (Item) expr.accept(this, arg);
            params.add(item);
          }
          final Item[] paramsArray = params.toArray(new Item[] {});
          generate.addPattern(name, paramsArray);
          // Produzenten registrieren
          this.add(this.tripleProducer, new KeyPredicatePattern(new PredicatePattern(name, paramsArray)), generate);
        }
      }
      resultOps.add(generate);
    }

    // 3. ConstructEquality erstellen
    if (generateEqualities) {
      final ConstructEquality constructEq = new ConstructEquality(
          this.equalityMap, equalities.toArray(new Equality[] {}));
      subOperator.addSucceedingOperator(constructEq);
      constructEq.addPrecedingOperator(subOperator);
      resultOps.add(constructEq);
      this.add(this.tripleProducer, BuildOperatorGraphRuleVisitor.keyEquality, constructEq);
    }
    if (resultOps.size() == 1) {
      return resultOps.iterator().next();
    } else {
      final Union union = new Union();
      for (final BasicOperator op : resultOps){
        union.addSucceedingOperator(op);
        op.addPrecedingOperator(union);
      }
      return union;
    }
  }

  private void add(final Map<KEY, Set<BasicOperator>> map, final KEY key, final BasicOperator toAdd){
    Set<BasicOperator> set = map.get(key);
    if(set==null){
      set = new LinkedHashSet<BasicOperator>();
      map.put(key, set);
    }
    set.add(toAdd);
  }

  @Override
  public Object visit(final Conjunction obj, final Object arg) throws RIFException {
    // Vorgehensweise: erstmal alle Sub-Operatoren sammeln -> Danach:
    final Set<BasicOperator> operands = new LinkedHashSet<BasicOperator>();
    final Set<BasicIndexScan> indexes = new LinkedHashSet<BasicIndexScan>();
    final List<RuleFilter> predicates = new ArrayList<RuleFilter>();

    for (final IExpression expr : obj.exprs) {
      final BasicOperator op = (BasicOperator) expr.accept(this, arg);
      if (op instanceof RuleFilter) {
        predicates.add((RuleFilter) op);
        continue;
      }
      operands.add(op);
      if (op instanceof BasicIndexScan && !(op instanceof IteratorIndexScan)) {
        indexes.add((BasicIndexScan) op);
      }
    }
    // 1. Mergen von Indexen
    BasicIndexScan mainIndex = null;

    if (indexes.size() > 1) {
      final Iterator<BasicIndexScan> it = indexes.iterator();
      mainIndex = it.next();
      while (it.hasNext()) {
        final BasicIndexScan mergeIndex = it.next();
        mainIndex.getTriplePattern().addAll(mergeIndex.getTriplePattern());
        for(final OperatorIDTuple opID: mergeIndex.getSucceedingOperators()){
          opID.getOperator().removePrecedingOperator(mergeIndex);
        }
        mergeIndex.getSucceedingOperators().clear();
        mergeIndex.removeFromOperatorGraph();
        operands.remove(mergeIndex);
      }
      if(arg!=null){
        mainIndex.setSucceedingOperator((OperatorIDTuple) arg);
        ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(mainIndex);
      }
    }
    // 2. Joins erstellen
    BasicOperator headOperator = null;
    final Iterator<BasicOperator> opIt = operands.iterator();
    if (operands.size() > 1) {
      Join bottomJoin = new IndexJoinWithDuplicateElimination();
      final BasicOperator op1 = opIt.next();
      for(final OperatorIDTuple opID: op1.getSucceedingOperators()){
        opID.getOperator().removePrecedingOperator(op1);
      }
      op1.getSucceedingOperators().clear();
      op1.addSucceedingOperator(new OperatorIDTuple(bottomJoin, 0));
      bottomJoin.addPrecedingOperator(op1);
      final BasicOperator op2 = opIt.next();
      for(final OperatorIDTuple opID: op2.getSucceedingOperators()){
        opID.getOperator().removePrecedingOperator(op2);
      }
      op2.getSucceedingOperators().clear();
      op2.addSucceedingOperator(new OperatorIDTuple(bottomJoin, 1));
      bottomJoin.addPrecedingOperator(op2);
      if(arg!=null){
        bottomJoin.setSucceedingOperator((OperatorIDTuple) arg);
        ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(bottomJoin);
      }
      while (opIt.hasNext()) {
        final Join tempJoin = new IndexJoinWithDuplicateElimination();
        final BasicOperator operand = opIt.next();
        for(final OperatorIDTuple opID: operand.getSucceedingOperators()){
          opID.getOperator().removePrecedingOperator(operand);
        }
        operand.getSucceedingOperators().clear();
        operand.setSucceedingOperator(new OperatorIDTuple(tempJoin, 0));
        tempJoin.addPrecedingOperator(operand);
        for(final OperatorIDTuple opID: bottomJoin.getSucceedingOperators()){
          opID.getOperator().removePrecedingOperator(bottomJoin);
        }
        bottomJoin.getSucceedingOperators().clear();
        bottomJoin.setSucceedingOperator(new OperatorIDTuple(tempJoin, 1));
        tempJoin.addPrecedingOperator(bottomJoin);
        if(arg!=null){
          tempJoin.setSucceedingOperator((OperatorIDTuple) arg);
          ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(tempJoin);
        }
        bottomJoin = tempJoin;
      }
      headOperator = bottomJoin;
    } else if (operands.size() == 1) {
      headOperator = opIt.next();
    }

    // 3. Predicates davorschalten
    if (!predicates.isEmpty()) {
      // 3.1 Predicates sortieren, alle m�glichen Assignments nach vorn
      if (predicates.size() > 1) {
        int i = 0;
        final Set<RuleFilter> visited = new LinkedHashSet<RuleFilter>();
        while (i < predicates.size()) {
          if (!predicates.get(i).getExpression()
              .isPossibleAssignment()
              && !visited.contains(predicates.get(i))) {
            final RuleFilter temp = predicates.get(i);
            predicates.remove(temp);
            predicates.add(temp);
            visited.add(temp);
            continue;
          }
          i++;
        }
      }
      // 3.2 Predicates in Baum einordnen
      // Sonderfall: Kein HeadOperator sondern nur RuleFilter
      if (headOperator == null) {
        headOperator = this.booleanIndex;
        for (final RuleFilter filter : predicates){
          headOperator.removeSucceedingOperator(filter);
          filter.removePrecedingOperator(headOperator);
        }
      } else {
        for(final OperatorIDTuple opID: headOperator.getSucceedingOperators()){
          opID.getOperator().removePrecedingOperator(headOperator);
        }
        headOperator.getSucceedingOperators().clear();
      }
      // Filter einbauen
      for (final BasicOperator pred : predicates) {
        headOperator.setSucceedingOperator(new OperatorIDTuple(pred, 0));
        pred.addPrecedingOperator(headOperator);
        if (headOperator != this.booleanIndex){
          this.booleanIndex.removeSucceedingOperator(pred);
          pred.removePrecedingOperator(this.booleanIndex);
        }
        headOperator = pred;
      }
      if(arg!=null){
        headOperator.setSucceedingOperator((OperatorIDTuple) arg);
        ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(headOperator);
      }
    }
    return headOperator;
  }

  @Override
  public Object visit(final Disjunction obj, final Object arg) throws RIFException {
    // Einf�hrung eines Union Operators, der alle Untergeordneten
    // Operatoren
    // zusammenf�hrt
    final Union union = new Union();
//    final Distinct distinct = new Distinct();
//    distinct.setSucceedingOperator((OperatorIDTuple) arg);
//    union.addSucceedingOperator(distinct);
    for (final IExpression expr : obj.exprs){
      expr.accept(this, new OperatorIDTuple(union, union.getSucceedingOperators().size()));
    }

//    return distinct;
    return union;
  }

  @Override
  public Object visit(final ExistExpression obj, final Object arg) throws RIFException {
    if (obj.getVariables().isEmpty()) {
      // keine Variablen zum joinen bzw. vereinen -> BooleanResult
      final MakeBooleanResult mbr = new MakeBooleanResult();
      if(arg!=null){
        mbr.setSucceedingOperator((OperatorIDTuple) arg);
        ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(mbr);
      }
      obj.expr.accept(this, new OperatorIDTuple(mbr, 0));
      return mbr;
    } else {
      // Variablen zum joinen vorhanden
      return obj.expr.accept(this, arg);
    }
  }

  public BasicOperator generatePattern(final RulePredicate obj, final Object arg){
    // Unterscheidung:
    // Wenn Pr�dikat, also kein Tripel
    if (!obj.isTriple()) {
      // PredicatePattern erstellen
      final URILiteral predName = (URILiteral) obj.termName.accept(this, arg);
      final List<Item> predItems = new ArrayList<Item>();
      for (final IExpression expr : obj.termParams) {
        final Item item = (Item) expr.accept(this, arg);
        predItems.add(item);
      }
      return new PredicatePattern(predName, predItems.toArray(new Item[] {}));
    }
    return this.unitermToTriplePattern(obj);
  }

  @Override
  public Object visit(final RulePredicate obj, final Object arg) throws RIFException {
    final BasicOperator zpattern = this.generatePattern(obj, arg);
    if (zpattern instanceof PredicatePattern) {
      final PredicatePattern predPat = (PredicatePattern) zpattern;
      if (this.predicateIndex == null) {
        this.predicateIndex = new PredicateIndexScan();
        this.indexScanCreator.getRoot().addSucceedingOperator(new OperatorIDTuple(this.predicateIndex, this.indexScanCreator.getRoot().getSucceedingOperators().size()));
        this.predicateIndex.addPrecedingOperator(this.indexScanCreator.getRoot());
      }
      this.predicateIndex.addSucceedingOperator(new OperatorIDTuple(predPat, this.predicateIndex.getSucceedingOperators().size()));
      predPat.addPrecedingOperator(this.predicateIndex);
      if(arg!=null){
        predPat.setSucceedingOperator((OperatorIDTuple) arg);
        ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(predPat);
      }
      // Pr�dikatkonsumenten anmelden
      this.add(this.tripleConsumer, new KeyPredicatePattern(predPat), predPat);
      return predPat;
    }
    // Normale TripleBearbeitung
    // 1. TriplePattern erstellen
    final TriplePattern pattern = (TriplePattern) zpattern;

    // 2. Index erstellen, noch ohne succeding operator
    final BasicOperator index = this.indexScanCreator.createIndexScanAndConnectWithRoot(null, new ArrayList<TriplePattern>(Arrays.asList(pattern)), null);
    index.setPrecedingOperator(this.indexScanCreator.getRoot());

    // 3. Pr�fen ob Triple-Pr�dikat an anderer Stelle erzeugt wird
    final KeyTriplePattern keyPattern = new KeyTriplePattern(pattern);
    boolean flag = false;

    final HashSet<KeyTriplePattern> possibleMatchingKeysOfProducers = new LinkedHashSet<KeyTriplePattern>();

    for(final KEY mainkey: this.tripleProducer.keySet()){
      if(mainkey instanceof KeyTriplePattern){
        final KeyTriplePattern mainkeyTP = (KeyTriplePattern) mainkey;
        if(keyPattern.mayConsume(mainkeyTP)){
          possibleMatchingKeysOfProducers.add(mainkeyTP);
          flag = true;
        }
      }
    }
    if (flag) {
      // index -> (union -> distinct) <- triplepattern : return union
//      Distinct distinct = new Distinct();
      final Union union = new Union();
//      union.setSucceedingOperator(new OperatorIDTuple(distinct, 0));
      index.setSucceedingOperator(new OperatorIDTuple(union, 0));
      union.addPrecedingOperator(index);
//      distinct.setSucceedingOperator((OperatorIDTuple) arg);
      pattern.setSucceedingOperator(new OperatorIDTuple(union, 1));
      union.addPrecedingOperator(pattern);
      for(final KeyTriplePattern keyTP: possibleMatchingKeysOfProducers){
        this.add(this.tripleConsumer, keyTP, pattern);
      }
//      return distinct;
      if(arg!=null){
        union.setSucceedingOperator((OperatorIDTuple) arg);
        ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(union);
      }
      return union;
    } else {
      if(arg!=null){
        index.setSucceedingOperator((OperatorIDTuple) arg);
        ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(index);
      }
      return index;
    }
  }

  @Override
  public Object visit(final External obj, final Object arg) throws RIFException {
    // Wenn iterierbar, dann Index erstellen
    // TODO: pr�fen ob Variable nicht anderweitig gebunden, in Validate,
    // dann irgendwie verf�gbar machen das genau hier
    // ein IteratorIndex notwendig ist;
    final URILiteral name = (URILiteral) ((Constant) obj.termName)
        .getLiteral();
    if (RIFBuiltinFactory.isIterable(name)) {
      final BasicOperator root = this.indexScanCreator.getRoot();
      final IteratorIndexScan index = new IteratorIndexScan((root instanceof Root)? (Root) root : null, obj);
      this.indexScanCreator.getRoot().addSucceedingOperator(index);
      index.addPrecedingOperator(this.indexScanCreator.getRoot());
      return index;
    } else {
      return this.buildRuleFilter(obj, arg);
    }
  }

  @Override
  protected RuleFilter buildRuleFilter(final IExpression expr, final Object arg) {
    if (this.booleanIndex == null) {
      final BasicOperator root = this.indexScanCreator.getRoot();
      this.booleanIndex = new BooleanIndexScan((root instanceof Root)? (Root) root : null);
      this.indexScanCreator.getRoot().addSucceedingOperator(new OperatorIDTuple(this.booleanIndex, 0));
      this.booleanIndex.addPrecedingOperator(this.indexScanCreator.getRoot());
    }
    RuleFilter filter = null;
    if (!this.usesEqualities || !(expr instanceof Equality)) {
      filter = new RuleFilter(expr, this.equalityMap);
    } else {
      filter = new EqualityFilter(expr, this.equalityMap);
      this.add(this.tripleConsumer, BuildOperatorGraphRuleVisitor.keyEquality, filter);
    }
    this.booleanIndex.addSucceedingOperator(filter);
    filter.addPrecedingOperator(this.booleanIndex);
    if(arg!=null){
      filter.setSucceedingOperator((OperatorIDTuple) arg);
      ((OperatorIDTuple) arg).getOperator().addPrecedingOperator(filter);
    }
    return filter;
  }
}
TOP

Related Classes of lupos.rif.visitor.BuildOperatorGraphRuleVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.