Package lupos.engine.operators

Source Code of lupos.engine.operators.BasicOperator

/**
* 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.engine.operators;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lupos.datastructures.items.Variable;
import lupos.datastructures.queryresult.QueryResult;
import lupos.datastructures.queryresult.QueryResultDebug;
import lupos.engine.operators.messages.BindingsFactoryMessage;
import lupos.engine.operators.messages.BoundVariablesMessage;
import lupos.engine.operators.messages.ComputeIntermediateResultMessage;
import lupos.engine.operators.messages.EndOfEvaluationMessage;
import lupos.engine.operators.messages.Message;
import lupos.engine.operators.messages.StartOfEvaluationMessage;
import lupos.misc.debug.DebugStep;

/**
* All operators are derived from this class. This class provides fundamental
* methods for all operators.
*/
public class BasicOperator implements Cloneable, Serializable {

  /**
   *
   */
  private static final long serialVersionUID = 1437787386470746802L;

  /**
   * succeedingOperators contains all succeeding operators (together with an
   * operand number) of this operator. Let us assume that the current operator
   * is C, another operator is A and a succeeding operator is B = A OPTIONAL
   * C. Note that it is important to know that A is the left operand and C is
   * the right operand of B. Then a succeeding operator of C is B with operand
   * number 1 (A has a succeeding operator B with operand number 0).
   */
  protected List<OperatorIDTuple> succeedingOperators = new LinkedList<OperatorIDTuple>();

  /**
   * The preceding operators are stored in this variable.
   */
  protected List<BasicOperator> precedingOperators = new LinkedList<BasicOperator>();

  /**
   * There exists a message system, which allows to send messages in the
   * operator graph. A message is only sent to the succeeding operators after
   * all operands (except those, where this operator is the loop head of) of
   * this operator have sent a message with the same message id. Before
   * sending a message to the succeeding operators, the messages with the same
   * message id are merged and processed by the current operator. After
   * sending the message, a message is received from the succeeding operators,
   * which is post-processed.
   */
  protected Map<String, Map<BasicOperator, Message>> messages = new HashMap<String, Map<BasicOperator, Message>>();

  /**
   * This variable contains all preceding operators, which have this operator
   * as ancestor, i.e. this operator is the head of a loop.
   *
   * @seealso messages
   */
  protected HashSet<BasicOperator> cycleOperands = new HashSet<BasicOperator>();

  /**
   * This variable contains the intersection of the used variables of its
   * preceding operators (and maybe newly bound variables).
   */
  protected Collection<Variable> intersectionVariables;

  /**
   * This variable contains the union of all used variables of the preceding
   * operators (and maybe newly bound variables).
   */
  protected Collection<Variable> unionVariables;

  /**
   * The standard constructor
   */
  public BasicOperator() {
    // no initializations...
  }

  /**
   * The constructor, which sets the succeeding operators
   *
   * @param succeedingOperators
   *            the succeeding operators
   */
  public BasicOperator(final List<OperatorIDTuple> succeedingOperators) {
    this.succeedingOperators = succeedingOperators;
  }

  /**
   * This constructor can be used to set only one succeeding operator
   *
   * @param succeedingOperator
   *            the one and only succeeding operator
   */
  public BasicOperator(final OperatorIDTuple succeedingOperator) {
    this.succeedingOperators = new LinkedList<OperatorIDTuple>();
    if (succeedingOperator != null) {
      this.succeedingOperators.add(succeedingOperator);
    }
  }

  /**
   * Replaces this operator with a replacement operator
   *
   * @param replacement
   *            The replacement operator
   */
  public void replaceWith(final BasicOperator replacement) {
    for (final BasicOperator preOp : this.precedingOperators) {
      for (final OperatorIDTuple opid : preOp.succeedingOperators) {
        if (opid.getOperator().equals(this)) {
          opid.setOperator(replacement);
        }
      }
    }
    for (final OperatorIDTuple sucOp : this.succeedingOperators) {
      for (int i = 0; i < sucOp.getOperator().precedingOperators.size(); i++) {
        if (sucOp.getOperator().precedingOperators.get(i).equals(this)) {
          sucOp.getOperator().precedingOperators.set(i, replacement);
        }
      }
    }
  }

  /**
   * This method can be used to set only one succeeding operator
   *
   * @param succeedingOperator
   *            the one and only succeeding operator
   */
  public void setSucceedingOperator(final OperatorIDTuple succeedingOperator) {
    this.succeedingOperators = new LinkedList<OperatorIDTuple>();
    this.succeedingOperators.add(succeedingOperator);
  }

  /**
   * This method can be used to the succeeding operators
   *
   * @param succeedingOperators
   *            list of succeeding operators
   */
  public void setSucceedingOperators(
      final List<OperatorIDTuple> succeedingOperators) {
    this.succeedingOperators = succeedingOperators;
  }

  /**
   * This method adds one succeeding operator
   *
   * @param succeedingOperator
   *            the succeeding operator
   */
  public void addSucceedingOperator(final OperatorIDTuple succeedingOperator) {
    this.succeedingOperators.add(succeedingOperator);
  }

  public void addSucceedingOperator(final BasicOperator succeedingOperator) {
    this.addSucceedingOperator(new OperatorIDTuple(succeedingOperator, 0));
  }

  public void addSucceedingOperator(final BasicOperator succeedingOperator, final int operandID) {
    this.addSucceedingOperator(new OperatorIDTuple(succeedingOperator, operandID));
  }

  /**
   * This method adds all of a list of succeeding operators
   *
   * @param succeedingOperatorsParamter
   *            list of succeeding operators
   */
  public void addSucceedingOperators(final List<OperatorIDTuple> succeedingOperatorsParamter) {
    this.succeedingOperators.addAll(succeedingOperatorsParamter);
  }

  /**
   * This method returns the succeeding operators
   *
   * @return the succeeding operators
   */
  public List<OperatorIDTuple> getSucceedingOperators() {
    return this.succeedingOperators;
  }

  /**
   * This method adds one preceding operator
   *
   * @param precedingOperator
   *            the preceding operator
   */
  public void addPrecedingOperator(final BasicOperator precedingOperator) {
    if (precedingOperator != null) {
      this.precedingOperators.add(precedingOperator);
    }
  }

  /**
   * This method adds a collection of preceding operators
   *
   * @param precedingOperatorsParameter
   *            the preceding operators
   */
  public void addPrecedingOperators(final Collection<BasicOperator> precedingOperatorsParameter) {
    for (final BasicOperator precedingOperator : precedingOperatorsParameter) {
      this.precedingOperators.add(precedingOperator);
    }
  }

  /**
   * This method removes the given preceding operator
   *
   * @param precedingOperator
   *            the preceding operator to be removed
   */
  public void removePrecedingOperator(final BasicOperator precedingOperator) {
    if (precedingOperator != null) {
      this.precedingOperators.remove(precedingOperator);
    }
  }

  /**
   * This method removes the succeeding operator
   *
   * @param succeedingOperator
   *            the succeeding operator to be removed
   */
  public void removeSucceedingOperator(final BasicOperator succeedingOperator) {
    if (succeedingOperator != null) {
      boolean change = true;
      while (change) {
        change = false;
        for (final OperatorIDTuple oit : this.succeedingOperators) {
          if (oit.getOperator().equals(succeedingOperator)) {
            this.succeedingOperators.remove(oit);
            change = true;
            break;
          }
        }
      }
    }
  }

  public void removeSucceedingOperator(final OperatorIDTuple opIDT) {
    while(this.succeedingOperators.remove(opIDT)){
      // action plus break conditions is in the loop head!
    }
  }

  /**
   * This method sets the given preceding operator as the only preceding
   * operator
   *
   * @param precedingOperator
   *            the one and only preceding operator
   */
  public void setPrecedingOperator(final BasicOperator precedingOperator) {
    this.precedingOperators = new LinkedList<BasicOperator>();
    if (precedingOperator != null) {
      this.precedingOperators.add(precedingOperator);
    }
  }

  /**
   * This method sets the preceding operators
   *
   * @param precedingOperators
   *            the preceding operators
   */
  public void setPrecedingOperators(
      final List<BasicOperator> precedingOperators) {
    if (precedingOperators != null) {
      this.precedingOperators = precedingOperators;
    } else {
      this.precedingOperators = new LinkedList<BasicOperator>();
    }
  }

  /**
   * This method returns the preceding operators
   *
   * @return the preceding operators
   */
  public List<BasicOperator> getPrecedingOperators() {
    return this.precedingOperators;
  }

  /**
   * This method returns the first found preceding operator, which is connected to this operator with the given id
   * @param id the id of the OperatorIDTuple
   * @return the first found preceding operator, which is connected to this operator with the given id, otherwise null
   */
  public BasicOperator getPrecedingOperatorWithID(final int id){
    for(final BasicOperator prec: this.getPrecedingOperators()){
      if(prec.getOperatorIDTuple(this).getId() == id){
        return prec;
      }
    }
    return null;
  }

  /**
   * This method returns the first OperatorIDTuple in the list of succeeding
   * operators, which contains the given operator
   *
   * @param op
   *            the operator to besearched for in the list of succeeding
   *            operators.
   * @return the first OperatorIDTuple, which contains op
   */
  public OperatorIDTuple getOperatorIDTuple(final BasicOperator op) {
    for (final OperatorIDTuple opid : this.succeedingOperators) {
      if (opid.getOperator().equals(op)) {
        return opid;
      }
    }
    return null;
  }

  /**
   * This method replaces one OperatorIDTuple with another in the list of
   * succeeding operators.
   *
   * @param oit1
   *            the OperatorIDTuple to be replaced
   * @param oit2
   *            The OperatorIDTuple to be added
   */
  public void replaceOperatorIDTuple(final OperatorIDTuple oit1,
      final OperatorIDTuple oit2) {
    this.succeedingOperators.remove(oit1);
    this.succeedingOperators.add(oit2);
  }

  /**
   * All preceding operators are recursively (i.e. in the whole operator
   * graph) deleted.
   */
  public void deleteParents() {
    final SimpleOperatorGraphVisitor sogv = new SimpleOperatorGraphVisitor() {
      /**
       *
       */
      private static final long serialVersionUID = 6919722882143749999L;

      @Override
      public Object visit(final BasicOperator basicOperator) {
        basicOperator.precedingOperators = new LinkedList<BasicOperator>();
        return null;
      }
    };
    this.visit(sogv);
  }

  /**
   * This method sets the preceding operators recursively in the whole
   * operator graph: If A has the succeeding operator B, then A is added to
   * the list of preceding operators in B.
   */
  public void setParents() {
    final SimpleOperatorGraphVisitor sogv = new SimpleOperatorGraphVisitor() {
      /**
       *
       */
      private static final long serialVersionUID = -3649188246478511485L;

      @Override
      public Object visit(final BasicOperator basicOperator) {
        for (final OperatorIDTuple opid : basicOperator.succeedingOperators) {
          final BasicOperator op = opid.getOperator();
          if (!op.precedingOperators.contains(basicOperator)) {
            op.precedingOperators.add(basicOperator);
          }
        }
        return null;
      }
    };
    this.visit(sogv);
  }

  /**
   * This method is applied whenever messages are received by this operator.
   *
   * @param msg
   *            the message itself
   * @param from
   *            the operator from which the message has been sent
   * @return the post-processed message after processing and forwarding the
   *         merged message of all operands, which has been received from the
   *         succeeding operators
   */
  public Message receive(final Message message, final BasicOperator from) {
    Message msg = message;
    if (from != null) {
      Map<BasicOperator, Message> received = this.messages.get(msg.getId());
      if (received == null) {
        received = new HashMap<BasicOperator, Message>();
        this.messages.put(msg.getId(), received);
      }
      received.put(from, msg);

      final HashSet<BasicOperator> operatorsWithoutCycles = new HashSet<BasicOperator>();
      operatorsWithoutCycles.addAll(this.precedingOperators);
      operatorsWithoutCycles.removeAll(this.cycleOperands);

      if (!received.keySet().containsAll(operatorsWithoutCycles)) {
        return null;
      }

      if (received.keySet().containsAll(this.precedingOperators)) {
        this.messages.remove(msg.getId());
      }
      msg = msg.merge(received.values(), this);
    }
    msg = msg.preProcess(this);
    msg = this.forwardMessage(msg);
    if (msg == null) {
      return null;
    } else {
      return msg.postProcess(this);
    }
  }

  /**
   * This method forwards the message to the succeeding operators.
   *
   * @param msg
   *            the message to be forwarded
   * @return a message received from the succeeding operators
   */
  protected Message forwardMessage(final Message msg) {
    msg.setVisited(this);
    Message result = msg;
    for (final OperatorIDTuple opid : this.succeedingOperators) {
      if (!msg.hasVisited(opid.getOperator())) {
        final Message msg2 = msg.clone();
        result = opid.getOperator().receive(msg2, this);
      }
    }
    return result;
  }

  /**
   * This method merges several received messages
   *
   * @param messagesParameter
   *            the received messages
   * @param msg
   *            the last received message
   * @return the merged message
   */
  public Message mergeMessages(final Collection<Message> messagesParameter, final Message msg) {
    return msg;
  }

  /**
   * This method pre-processes a merged message
   *
   * @param msg
   *            the message to be pre-processed
   * @return the pre-processed message
   */
  public Message preProcessMessage(final Message msg) {
    return msg;
  }

  /**
   * This method post-processes a message
   *
   * @param msg
   *            the message to be post-processed
   * @return the post-processed message
   */
  public Message postProcessMessage(final Message msg) {
    return msg;
  }

  @SuppressWarnings("unused")
  public Message preProcessMessageDebug(final Message msg, final DebugStep debugstep) {
    return this.preProcessMessage(msg);
  }

  @SuppressWarnings("unused")
  public Message preProcessMessageDebug(final StartOfEvaluationMessage msg, final DebugStep debugstep) {
    return this.preProcessMessage(msg);
  }

  @SuppressWarnings("unused")
  public Message preProcessMessageDebug(final EndOfEvaluationMessage msg, final DebugStep debugstep) {
    return this.preProcessMessage(msg);
  }

  @SuppressWarnings("unused")
  public Message preProcessMessageDebug(final BoundVariablesMessage msg, final DebugStep debugstep) {
    return this.preProcessMessage(msg);
  }

  @SuppressWarnings("unused")
  public Message postProcessMessageDebug(final Message msg, final DebugStep debugstep) {
    return this.postProcessMessage(msg);
  }

  @SuppressWarnings("unused")
  public Message postProcessMessageDebug(final StartOfEvaluationMessage msg, final DebugStep debugstep) {
    return this.postProcessMessage(msg);
  }

  @SuppressWarnings("unused")
  public Message postProcessMessageDebug(final EndOfEvaluationMessage msg, final DebugStep debugstep) {
    return this.postProcessMessage(msg);
  }

  @SuppressWarnings("unused")
  public Message postProcessMessageDebug(final BoundVariablesMessage msg, final DebugStep debugstep) {
    return this.postProcessMessage(msg);
  }

  /**
   * This method is for merging the BindingsFactoryMessage
   *
   * @param messagesParameter
   *            the messages received from the operands of this operator
   * @param msg
   *            the last received message
   * @return the merged message
   */
  public Message mergeMessages(final Collection<Message> messagesParameter, final BindingsFactoryMessage msg) {
    return msg;
  }

  /**
   * This method pre-processes the BindingsFactoryMessage
   * Some operates override this method just to save the BindingsFactory in a local variable for later usage.
   *
   * @param msg
   *            the message to be pre-processed
   * @return the pre-processed message
   */
  public Message preProcessMessage(final BindingsFactoryMessage msg) {
    return msg;
  }

  /**
   * This method post-processes the BindingsFactoryMessage
   *
   * @param msg
   *            the message to be post-processed
   * @return the post-processed message
   */
  public Message postProcessMessage(final BindingsFactoryMessage msg) {
    return msg;
  }

  /**
   * This method computes the intersection and union of used variables. Note
   * that some operators override this method.
   *
   * @param messagesParameter
   *            the messages received from the operands of this operator
   * @param msg
   *            the last received message
   * @return the merged message
   */
  public Message mergeMessages(final Collection<Message> messagesParameter, final BoundVariablesMessage msg) {
    this.intersectionVariables = new HashSet<Variable>();
    this.intersectionVariables.addAll(msg.getVariables());
    for (final Message m : messagesParameter) {
      this.intersectionVariables.retainAll(((BoundVariablesMessage) m)
          .getVariables());
    }
    this.unionVariables = new HashSet<Variable>();
    for (final Message m : messagesParameter) {
      this.unionVariables.addAll(((BoundVariablesMessage) m).getVariables());
    }
    final BoundVariablesMessage msg2 = new BoundVariablesMessage(msg);
    msg2.setVariables(this.unionVariables);
    return msg2;
  }

  /**
   * This method pre-processes the BoundVariablesMessage
   *
   * @param msg
   *            the message to be pre-processed
   * @return the pre-processed message
   */
  public Message preProcessMessage(final BoundVariablesMessage msg) {
    return msg;
  }

  /**
   * This method post-processes the BoundVariablesMessage
   *
   * @param msg
   *            the message to be post-processed
   * @return the post-processed message
   */
  public Message postProcessMessage(final BoundVariablesMessage msg) {
    return msg;
  }

  /**
   * This method pre-processes the EndOfStreamMessage
   *
   * @param msg
   *            the message to be pre-processed
   * @return the pre-processed message
   */
  public Message preProcessMessage(final EndOfEvaluationMessage msg) {
    return msg;
  }

  /**
   * This method merges several received messages
   *
   * @param messages
   *            the received messages
   * @param msg
   *            the last received message
   * @return the merged message
   */
  @SuppressWarnings("unused")
  public Message mergeMessages(final Collection<Message> messagesParameter, final EndOfEvaluationMessage msg) {
    return msg;
  }

  /**
   * This method pre-processes the StartOfStreamMessage
   *
   * @param msg
   *            the message to be pre-processed
   * @return the pre-processed message
   */
  public Message preProcessMessage(final StartOfEvaluationMessage msg) {
    return msg;
  }

  /**
   * This method post-processes the StartOfStreamMessage
   *
   * @param msg
   *            the message to be post-processed
   * @return the post-processed message
   */
  public Message postProcessMessage(final StartOfEvaluationMessage msg) {
    return msg;
  }

  /**
   * This method merges several received messages
   *
   * @param messages
   *            the received messages
   * @param msg
   *            the last received message
   * @return the merged message
   */
  @SuppressWarnings("unused")
  public Message mergeMessages(final Collection<Message> messagesParameter, final StartOfEvaluationMessage msg) {
    return msg;
  }

  public Message preProcessMessage(final ComputeIntermediateResultMessage msg) {
    return msg;
  }

  /**
   * All implementation classes that replace a super class which already holds
   * relevant state, have to override this method to copy that state from the
   * superclass. Make sure to call super so that children and parents don't
   * get lost
   *
   * @param op
   *            The BasicOperator to copy state from
   */
  public void cloneFrom(final BasicOperator op) {
    if (op.succeedingOperators != null) {
      this.succeedingOperators = new LinkedList<OperatorIDTuple>();
      this.succeedingOperators.addAll(op.succeedingOperators);
    }
    if (op.precedingOperators != null) {
      this.precedingOperators = new LinkedList<BasicOperator>();
      this.precedingOperators.addAll(op.precedingOperators);
    }
    this.messages = op.messages;
    if (op.intersectionVariables != null) {
      this.intersectionVariables = new LinkedList<Variable>();
      this.intersectionVariables.addAll(op.intersectionVariables);
    }
    if (op.unionVariables != null) {
      this.unionVariables = new LinkedList<Variable>();
      this.unionVariables.addAll(op.unionVariables);
    }
  }

  /**
   * The clone method to clone the current operator
   *
   * @return the cloned operator
   */
  @Override
  public BasicOperator clone() {
    BasicOperator result = null;
    try {
      result = (BasicOperator) super.clone();
      result.cloneFrom(this);
    } catch (final CloneNotSupportedException ex) {
      // just return null;
    }
    return result;
  }

  /**
   * This method clones not only the current operator, but all of its
   * succeeding operators recursively.
   *
   * @return the cloned operator
   */
  public BasicOperator deepClone() {
    final BasicOperator op = (BasicOperator) this.visit(new SimpleOperatorGraphVisitor() {
      /**
       *
       */
      private static final long serialVersionUID = -2374279115052843835L;

      final Map<BasicOperator, BasicOperator> clones = new HashMap<BasicOperator, BasicOperator>();

      @Override
      public Object visit(final BasicOperator basicOperator) {
        BasicOperator cloneCurrent;
        if (this.clones.containsKey(basicOperator)) {
          cloneCurrent = this.clones.get(basicOperator);
        } else {
          cloneCurrent = basicOperator.clone();
          this.clones.put(basicOperator, cloneCurrent);
        }

        final LinkedList<OperatorIDTuple> newSucc = new LinkedList<OperatorIDTuple>();

        for (final OperatorIDTuple opid : basicOperator.succeedingOperators) {
          BasicOperator clone = null;
          if (this.clones.containsKey(opid.getOperator())) {
            clone = this.clones.get(opid.getOperator());
          } else {
            clone = opid.getOperator().clone();
            this.clones.put(opid.getOperator(), clone);
          }
          newSucc.add(new OperatorIDTuple(clone, opid.getId()));
        }

        cloneCurrent.setSucceedingOperators(newSucc);
        return cloneCurrent;
      }

    });

    op.deleteParents();
    op.setParents();

    return op;
  }

  /**
   * This method sets the union of used variables
   *
   * @param unionVariables
   *            the union of used variables
   */
  public void setUnionVariables(final Collection<Variable> unionVariables) {
    this.unionVariables = unionVariables;
  }

  /**
   * This method returns the union of used variables
   *
   * @return the union of used variables
   */
  public Collection<Variable> getUnionVariables() {
    return this.unionVariables;
  }

  /**
   * This method sets the intersection of used variables.
   *
   * @param sortCriterium
   *            the intersection of used variables
   */
  public void setIntersectionVariables(final Collection<Variable> sortCriterium) {
    this.intersectionVariables = sortCriterium;
  }

  /**
   * This method returns the intersection of used variables.
   *
   * @return the intersection of used variables
   */
  public Collection<Variable> getIntersectionVariables() {
    return this.intersectionVariables;
  }

  /**
   * This method sends a message.
   *
   * @param msg
   *            the message to be sent
   * @return the received (and post-processed) message from the succeeding
   *         operators
   */
  public Message sendMessage(final Message msg) {
    final Message msgResult = this.forwardMessage(msg.preProcess(this));
    // postprocess anyway one message, does not matter if the resultant msg is null or not to allow processing right after initialization
    if(msgResult == null) {
      return msg.postProcess(this);
    } else {
      return msgResult.postProcess(this);
    }
  }

  /**
   * This method starts processing a simple visitor in the whole operator
   * graph. Depth-first visit is applied.
   *
   * @param visitor
   *            The visitor to be applied to each node in the operator graph
   * @return The object retrieved from processing the visitor on this
   *         operator.
   */
  public Object visit(final SimpleOperatorGraphVisitor visitor) {
    return this.visit(visitor, new HashSet<BasicOperator>());
  }

  /**
   * This is a helper method of the method Object
   * visit(SimpleOperatorGraphVisitor visitor)
   *
   * @param visitor
   *            The visitor to be applied to each node
   * @param hs
   *            the already visited operators
   * @return The object retrieved from processing the visitor on this
   *         operator.
   */
  private Object visit(final SimpleOperatorGraphVisitor visitor,
      final HashSet<BasicOperator> hs) {
    if (hs.contains(this)) {
      return null;
    }
    hs.add(this);
    final Object result = visitor.visit(this);
    for (final OperatorIDTuple opid : this.succeedingOperators) {
      opid.getOperator().visit(visitor, hs);
    }
    return result;
  }

  /**
   * This method starts processing a simple visitor in the whole operator
   * graph. Depth-first visit is applied.
   * This visitor stops when the first result is not null.
   *
   * @param visitor
   *            The visitor to be applied to each node in the operator graph
   * @return The object retrieved from processing the visitor on this
   *         operator.
   */
  public Object visitAndStop(final SimpleOperatorGraphVisitor visitor) {
    return this.visitAndStop(visitor, new HashSet<BasicOperator>());
  }

  /**
   * This is a helper method of the method Object
   * visit(SimpleOperatorGraphVisitor visitor)
   *
   * @param visitor
   *            The visitor to be applied to each node
   * @param hs
   *            the already visited operators
   * @return The object retrieved from processing the visitor on this
   *         operator.
   */
  private Object visitAndStop(final SimpleOperatorGraphVisitor visitor,
      final HashSet<BasicOperator> hs) {
    if (hs.contains(this)) {
      return null;
    }

    hs.add(this);

    Object result = visitor.visit(this);

    if(result != null) {
      return result;
    }

    for (final OperatorIDTuple opid : this.succeedingOperators) {
      result = opid.getOperator().visitAndStop(visitor, hs);

      if(result != null) {
        return result;
      }
    }

    return result;
  }

  /**
   * This method starts processing a visitor in the whole operator graph.
   * Depth-first visit is applied. In comparison to visit(final
   * SimpleOperatorGraphVisitor visitor), the used visitor allows to have an
   * additional parameter for data for the visit-method.
   *
   * @param visitor
   *            The visitor to be applied to each node in the operator graph
   * @return The object retrieved from processing the visitor on this
   *         operator.
   */
  public<T> Object visit(final OperatorGraphVisitor<T> visitor, final T data) {
    return this.visit(visitor, data, new HashSet<BasicOperator>());
  }

  /**
   * This is a helper method of the method Object
   * visit(SimpleOperatorGraphVisitor visitor)
   *
   * @param visitor
   *            The visitor to be applied to each node
   * @param hs
   *            the already visited operators
   * @return The object retrieved from processing the visitor on this
   *         operator.
   */
  private<T> Object visit(final OperatorGraphVisitor<T> visitor, final T data,
      final HashSet<BasicOperator> hs) {
    if (hs.contains(this)) {
      return null;
    }
    hs.add(this);
    final T result = visitor.visit(this, data);
    for (final OperatorIDTuple opid : this.succeedingOperators) {
      opid.getOperator().visit(visitor, result, hs);
    }
    return result;
  }

  /**
   * This method detects cycles and stores the last operator of a cycle in the
   * loop head.
   */
  public void detectCycles() {
    final SimpleOperatorGraphVisitor deleteOldDetectedCycles = new SimpleOperatorGraphVisitor() {
      /**
       *
       */
      private static final long serialVersionUID = 5010278675133006511L;

      @Override
      public Object visit(final BasicOperator basicOperator) {
        basicOperator.cycleOperands.clear();
        return null;
      }
    };
    this.visit(deleteOldDetectedCycles);
    this.detectCycles(new LinkedList<BasicOperator>(),
        new HashSet<BasicOperator>());
  }

  /**
   * This method is a helper method of detectCycles.
   *
   * @seealso detectCycles
   */
  private void detectCycles(final List<BasicOperator> hs,
      final Set<BasicOperator> visited) {
    // System.out.println(this);
    if (visited.contains(this)) {
      return;
    }
    hs.add(this);
    visited.add(this);
    for (final OperatorIDTuple opid : this.succeedingOperators) {
      final int i = hs.indexOf(opid.getOperator());
      if (i >= 0) {
        opid.getOperator().cycleOperands.add(this);
        // cycleOperands.add(opid.getOperator());
        for (int j = i + 1; j < hs.size(); j++) {
          hs.get(j).cycleOperands.add(hs.get(j - 1));
        }
      }
    }
    for (final OperatorIDTuple opid : this.succeedingOperators) {
      opid.getOperator().detectCycles(hs, visited);
    }
    hs.remove(this);
  }

  public HashSet<BasicOperator> getCycleOperands() {
    return this.cycleOperands;
  }

  public void setCycleOperands(final HashSet<BasicOperator> cycleOperands) {
    this.cycleOperands = cycleOperands;
  }

  @Override
  public String toString() {
    return this.getClass().getSimpleName();
  }

  @SuppressWarnings("unused")
  public String toString(final lupos.rdf.Prefix prefixInstance) {
    return this.toString();
  }


  public void dump(final String prefix, final HashSet<BasicOperator> visited) {
    if (visited.contains(this)) {
      return;
    }

    visited.add(this);

    System.out.println(prefix + this.toString());

    for (final OperatorIDTuple childIDT : this.succeedingOperators) {
      final BasicOperator child = childIDT.getOperator();

      child.dump(prefix + "-", visited);
    }
  }

  public void removeFromOperatorGraph() {
    for (final OperatorIDTuple oidtuple : this.getSucceedingOperators()) {
      oidtuple.getOperator().removePrecedingOperator(this);
      oidtuple.getOperator().addPrecedingOperators(
          this.getPrecedingOperators());
    }
    for (final BasicOperator prec : this.getPrecedingOperators()) {
      prec.removeSucceedingOperator(this);
      prec.addSucceedingOperators(this.getSucceedingOperators());
    }
  }

  public void removeFromOperatorGraphWithoutConnectingPrecedingWithSucceedingOperators() {
    for (final OperatorIDTuple oidtuple : this.getSucceedingOperators()) {
      oidtuple.getOperator().removePrecedingOperator(this);
    }
    this.succeedingOperators.clear();
    for (final BasicOperator prec : this.getPrecedingOperators()) {
      prec.removeSucceedingOperator(this);
    }
    this.precedingOperators.clear();
  }

  protected Message forwardMessageDebug(final Message msg,
      final DebugStep debugstep) {
    msg.setVisited(this);
    Message result = msg;
    for (final OperatorIDTuple opid : this.succeedingOperators) {
      if (!msg.hasVisited(opid.getOperator())) {
        final Message msg2 = msg.clone();
        result = opid.getOperator().receiveDebug(msg2, this, debugstep);
      }
    }
    return result;
  }

  @SuppressWarnings("unused")
  public Message preProcessMessageDebug(final ComputeIntermediateResultMessage msg, final DebugStep debugstep) {
    return msg;
  }

  public Message receiveDebug(final Message message, final BasicOperator from, final DebugStep debugstep) {
    Message msg = message;
    if (from != null) {
      debugstep.stepMessage(from, this, msg);
      Map<BasicOperator, Message> received = this.messages.get(msg.getId());
      if (received == null) {
        received = new HashMap<BasicOperator, Message>();
        this.messages.put(msg.getId(), received);
      }
      received.put(from, msg);

      final HashSet<BasicOperator> operatorsWithoutCycles = new HashSet<BasicOperator>();
      operatorsWithoutCycles.addAll(this.precedingOperators);
      operatorsWithoutCycles.removeAll(this.cycleOperands);

      if (!received.keySet().containsAll(operatorsWithoutCycles)) {
        return null;
      }

      if (received.keySet().containsAll(this.precedingOperators)) {
        this.messages.remove(msg.getId());
      }
      msg = msg.merge(received.values(), this);
    }
    msg = msg.preProcessDebug(this, debugstep);
    msg = this.forwardMessageDebug(msg, debugstep);
    if (msg == null) {
      return null;
    } else {
      return msg.postProcessDebug(this, debugstep);
    }
  }

  public Message sendMessageDebug(final Message msg, final DebugStep debugstep) {
    final Message msgResult = this.forwardMessageDebug(msg.preProcessDebug(this, debugstep), debugstep);
    // postprocess anyway one message, does not matter if the resultant msg is null or not to allow processing right after initialization
    if(msgResult == null) {
      return msg.postProcessDebug(this, debugstep);
    } else {
      return msgResult.postProcessDebug(this, debugstep);
    }
  }

  /**
   * Processes the given queryresult at all of its succeeding operators...
   *
   * @param qr the queryresult ro be processed at the succeeding operators
   */
  public void processAtSucceedingOperators(final QueryResult qr){
    for (final OperatorIDTuple succOperator : this.succeedingOperators) {
      ((Operator) succOperator.getOperator()).processAll(qr, succOperator.getId());
    }
  }

  /**
   * Processes the given queryresult at all of its succeeding operators logging debug steps...
   *
   * @param qr the queryresult ro be processed at the succeeding operators
   * @param debugstep the object for logging debug steps
   */
  public void processAtSucceedingOperatorsDebug(final QueryResult qr, final DebugStep debugstep){
    for (final OperatorIDTuple succOperator : this.succeedingOperators) {
      ((Operator) succOperator.getOperator()).processAll(new QueryResultDebug(qr, debugstep, this, succOperator.getOperator(), true), succOperator.getId());
    }
  }

  @SuppressWarnings("unused")
  public boolean remainsSortedData(final Collection<Variable> sortCriterium){
    return false;
  }

  public Collection<Variable> transformSortCriterium(final Collection<Variable> sortCriterium){
    return sortCriterium;
  }

  /**
   * just to enable access to the root node in a SubgraphContainer in module distributed for BasicOperatorByteArray...
   *
   * @return the root node of the contained graph
   */
  public BasicOperator getContainedGraph(){
    return null;
  }
}
TOP

Related Classes of lupos.engine.operators.BasicOperator

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.