Created on Jul 10, 2011
Represents a blocking plan, where a process is created only
in the leaves of the tree (to contact the sources). The
intermediate results are represented as lists.
@author: Maribel Acosta Deibe
import string
from multiprocessing import Process, Queue
from SPARQLWrapper import SPARQLWrapper, JSON
from ANAPSID.Catalog.Catalog import Catalog
def contactSource(server, query, queue):
    Contacts the datasource (i.e. endpoint).
    The hole answer is represented as a list that is stored in a queue.
    # Build the query and contact the source.
    sparql = SPARQLWrapper(server)
    res = sparql.query().convert()
    for x in res['results']['bindings']:
        for key, props in x.iteritems():
            x[key] = props['value']
    reslist = res['results']['bindings']
#    # Every tuple is appended in a list.
#    reslist = res.split('\n')
#    resint = []
#    for elem in reslist:
#        #TODO: queue.put(eval(elem.rstrip()))
#        resint.append(eval(elem.rstrip()))
    # The list is added to the queue.
class IndependentOperator(object):
    Implements an operator that can be resolved independently.
    It receives as input the url of the server to be contacted, and the
    filename that contains the query.
    The execute() method gets the list of results from a queue and returns it.
    def __init__(self, server, filename):
        self.server = server
        self.filename = filename
        self.q = None
        self.q = Queue()
        self.query = open(filename).read()
        self.p = Process(target=contactSource,
                         args=(self.server, self.query, self.q,))
    def execute(self):
        # Evaluate the independent operator.
        res = self.q.get()
        #res = []
        #elem = self.q.get()
        #while (elem != "EOF"):
        #    res.append(eval(elem.rstrip()))
        #    elem = self.q.get()
        return res
class DependentOperator(object):
    Implements an operator that must be resolved with an instance.
    It receives as input the url of the server to be contacted,
    and the filename that contains the query.
    The execute() method performs a semantic check. If the instance
    can be derreferenced from the source, it will contact the source.
    def __init__(self, server, filename):
        self.server = server
        self.filename = filename
        self.q = None
        self.q = Queue()
        self.atts = self.getQueryAttributes()
        self.catalog = Catalog("../Catalog/endpoints.desc")
    def execute(self, variables, instances):
        res = []
        self.query = open(self.filename).read()
        # Replace in the query, the instance that is derreferenced.
        for i in range(len(variables)):
            self.query = string.replace(self.query, "?" + variables[i], "", 1)
            self.query = string.replace(self.query, "?" + variables[i], "<" + instances[i] + ">")
        # If the instance has no ?query. Example: DESCRIBE ---
        if (instances[0].find("sparql?query") == -1):
            pos = instances[0].find("/resource")
            pre = instances[0][0:pos]
            # Semantic check!.
            for server in self.server:
                prefixes = self.catalog.data[server]
                    # Contact the source.
                    pos = prefixes.index(pre)
                    self.p = Process(target=contactSource,
                              args=(server, self.query, self.q,))
                    res = self.q.get()
                except ValueError:
                    # The source shouldn't be contacted.
        return res
    def getQueryAttributes(self):
        # Read the query from file and apply lower case.
        query = open(self.filename).read()
        query2 = string.lower(query)
        # Extract the variables, separated by commas.
        # TODO: it supposes that there's no from clause.
        begin = string.find(query2, "select")
        begin = begin + len("select")
        end = string.find(query2, "where")
        listatts = query[begin:end]
        listatts = string.split(listatts, " ")
        # Iterate over the list of attributes, and delete "?".
        outlist = []
        for att in listatts:
            if ((len(att) > 0) and (att[0] == '?')):
                if ((att[len(att)-1] == ',') or (att[len(att)-1] == '\n')):
                    outlist = outlist + [att[1:len(att)-1]]
                    outlist = outlist + [att[1:len(att)]]
        return outlist
class TreePlan(object):
    Represents a plan to be executed by the engine.
    It is composed by a left node, a right node, and an operator node.
    The left and right nodes can be leaves to contact sources, or subtrees.
    The operator node is a physical operator, provided by the engine.
    The execute() method evaluates the plan.
    The operator is evaluated when left and right are done.
    If the right node is an independent operator or a subtree, it is evaluated.
    def __init__(self, operator, left=None, right=None):
        self.operator = operator
        self.left = left
        self.right = right
    def execute(self):
        # Evaluates the execution plan.
        if self.left and self.right:
            # The left node is always evaluated.
            resleft = self.left.execute()
            # Check the right node to determine if evaluate it or not.
            if ((self.right.__class__.__name__ == "IndependentOperator") or
                (self.right.__class__.__name__ == "TreePlan")):
                resright = self.right.execute()
                resright = self.right
        # Execute the operator.
        return self.operator.execute(resleft, resright)