APPOSITION = "appos"   # the textual value of an appositio n node
 def isApposition(node):
    """
    check if this node is an apposition instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is an apposition instance
    """
    return isinstance(node,AppositionNode)
 class CopularNode(Node):
    """
    A class representing a copular head "BE" node.
    """
    @classmethod
    def init(cls,index,features,valid):
        if "Lemma" in features:
            del(features["Lemma"])
        return cls(isPredicate=True,
                   text=[Word(index,COPULA)],
                   features=features,
                   valid=valid)
 def addSymmetricPropogation(node1,node2):
    """
    Add two nodes onto each other's propogation
    
    @type  node1: Node
    @param node: The node onto which to propogate node2
    
    @type  node1: Node2
    @param node: The node onto which to propogate node1
    """
    
    node1.addPropogation(node2)
    node2.addPropogation(node1)
 def isConjunction(node):
    """
    check if this node is a conjunction instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is conjunction node instance
    """
    #TODO: efficiency
    return isinstance(node,ConjunctionNode)
 def isProp(node):
    """
    check if this node is a prop instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is prop node instance
    """
    #TODO: efficiency
    return isinstance(node,PropNode)
 def isCondition(node):
    """
    check if this node is a Cond instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is condition node instance
    """
    #TODO: efficiency
    return isinstance(node,CondNode)
 def isPreposition(node):
    """
    check if this node is a preposition instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is preposition node instance
    """
    #TODO: efficiency
    return isinstance(node,PrepNode)
 def isAdverb(node):
    """
    check if this node is a adverb instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is adverb node instance
    """
    #TODO: efficiency
    return isinstance(node,advNode)
 def isPossessive(node):
    """
    check if this node is a Possessive instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is possessive node instance
    """
    #TODO: efficiency
    return isinstance(node,PossessiveNode)
 EXISTENSIAL = "EXISTS" # the textual value of a conjunction node
 COND = "COND"          # the textual value of a conditional node
 def isTime(node):
    """
    check if this node is a time instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is time node instance
    """
    #TODO: efficiency
    return isinstance(node,TimeNode)
 class TimeNode(Node):
    """
    A class representing a time head node
    """
    @classmethod
    def init(cls,features):
        return cls(isPredicate=False,
                      text=[Word(NO_INDEX,TIME)],
                      features=features,
                      valid=True)
        cls.nodeShape = RECT_NODE_SHAPE
    
    def get_text(self,gr):
        neighbors = gr.neighbors(self)
        ret = []
        for n in neighbors:
            ret.extend(n.get_text(gr))
        return sorted(ret,key=lambda x:x.index)
 def isLocation(node):
    """
    check if this node is a location instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is location node instance
    """
    #TODO: efficiency
    return isinstance(node,LocationNode)
 class LocationNode(Node):
    """
    A class representing a location head node
    """
    @classmethod
    def init(cls,features):
        return cls(isPredicate=True,
                      text=[Word(NO_INDEX,LOCATION)],
                      features=features,
                      valid=True)
    
    def get_text(self,gr):
        neighbors = gr.neighbors(self)
        ret = []
        for n in neighbors:
            ret.extend(n.get_text(gr))
        return sorted(ret,key=lambda x:x.index)
 def join(node1,node2,gr):
    """
    Returns a node which is the concatenation of two nodes
    Raises in error in case they have contradicting features
    
    @type  node1: Node
    @param node1: first node to be joined
    
    @type  node2: Node
    @param node2: second node to be joined
    
    @rtype Node
    @return a node representing the union of both nodes
    """
    
    # make sure everything is ok
    if node1.isPredicate != node2.isPredicate:
        #raise Exception("Contradicting isPredicate value")
        print "Contradicting isPredicate value"
                
    if (not node1.valid) or (not node2.valid):
        raise Exception("Invalid node cannot be joined")
    
    
    # join all values
    isPredicate = (node1.isPredicate and node2.isPredicate)
    text = list(set(node1.get_text(gr)).union(node2.get_text(gr)))
    features = {}
    features.update(node1.features)
    features.update(node2.features)
    valid = node1.valid
    
    # remove contradicting features
    for k in set(node1.features).intersection(node2.features):
        if node1.features[k]!=node2.features[k]: 
                del(features[k])
                print("Contradicting features")
                
    
    # return new node
    return Node(isPredicate = isPredicate,
                text = text,
                features = features,
                valid = valid)
 def isCopular(node):
    """
    check if this node is an copular instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is an copular instance
    """
    return isinstance(node,CopularNode)
 def isRcmodProp(node):
    """
    check if this node is a prop instance
    
    @type  node: Node
    @param node: node to examine
    
    @rtype  bool
    @return True iff this node is prop node instance
    """
    #TODO: efficiency
    return isinstance(node,RCMODPropNode)
 def isDefinite(node):
    return node.features.get("Definite",False) == definite_label
 PREP = "PREP"          # the textual value of a preposition node
 def accessibility_wo_self(graph):
def duplicate_node(graph, node, connectToNeighbours):
def get_node_dic(graph, node):
def duplicateEdge(graph, orig, new, newLabel=""):
def findChain(graph, func_ls):
    def inner(nodes, func_ls):
def delete_component(graph, node):
def component_to_string(graph, node):
def duplicate_component(graph, node):
def find_nodes(graph, filterFunc):
def find_edges(graph, filterFunc):
def join(graph, nodeLs):
def generate_possessive_top_node(graph, nodeLs):
def sort_nodes_topologically(graph, nodeLs):
def get_min_max_span(graph, node):
def sister_nodes(graph, node):
def is_following(graph, node1, node2):
def immediate_sister(graph, node1, node2):
def reattch(graph, node, new_father, label=""):
def deref(graph, node, rel):
def find_node_by_index_range(graph, start, end):
def to_undirected(graph):
def shortest_distance(graph, node1, node2):
def find_top_of_component(graph, source_node):
def reverse_graph_edges(graph):
def merge_nodes(gr, node1, node2):
def subgraph_to_string(graph,node,exclude=[]):
def multi_get(d, ls):
 POSSESSOR_LABEL = "possessor"
 APPENDIX_EXISTENSIALS = "Existensials"
 APPENDIX_RANGE = "Ranges"
 class appendix_types:
    def __init__(self):
        self.d = {}
    def add(self,obj):
        self._update(obj, add=+1)
    def getSet(self):
        return set([k for k in self.d.keys() if self.d[k]>0])
    def union(self,other):
        for k in other.d:
            self._update(obj=k, add=other.d[k])
            
    def remove(self,obj):
        self._update(obj, add=-1)
    def _update(self,obj,add):
        if obj not in self.d:
            self.d[obj] = 0
        self.d[obj]+=add APPENDIX_LOCATION  = "Locations"
 FIRST_ENTITY_LABEL = "entity"#"first_entity"
 SECOND_ENTITY_LABEL = "entity"#"second_entity"
 NO_INDEX = -1 # index used in cases where there's no such word in the sentence
class Word:
    def __init__(self,index,word):
    def to_conll_like(self):
    def __str__(self):
    def __eq__(self,other_word):
    def __hash__(self):
def strip_punctuations(ls):
 COPULA = "SAME AS"          # the textual value of a copula node
PROP = "PROP"          # the textual value of a property node
RCMOD_PROP = "PROP"    # the textual value of a property for rcmod node
POSSESSIVE = "POSSESS" # the textual value of a possessive node
APPOSITION = "appos"   # the textual value of an appositio n node
PREP = "PREP"          # the textual value of a preposition node
PREP_TYPE = "TYPE"     # the textual value of a preposition node's type
COND = "COND"          # the textual value of a conditional node
TIME = "TIME"          # the textual value of a time node
LOCATION = "LOCATION"  # the textual value of a location node
CONJUNCTION = "CONJ -" # the textual value of a conjunction node
ADVERB = "ADV"         # the textual value of a conjunction node
EXISTENSIAL = "EXISTS" # the textual value of a conjunction node
COND_TYPE= PREP_TYPE   # the textual value of a conditional node's type
RECT_NODE_SHAPE = "rect"
DEFAULT_NODE_SHAPE = "ellipse"
PRINT_FEATURES = [("Tense",lambda t:t),
                  ("Determiner",lambda t:"det: "+t["Value"]),
                  ("Time Value",lambda t:"date: "+t),
                  ("Negation", lambda t:"negated"),
                  ("Passive Voice", lambda t:"passive"),
                  ("Modal",lambda t:"modal: "+ " ".join(t["Value"])),
                  ("Definite",lambda t:t),
                  ("Modifier",lambda t:"modifer: "+t)]
class Node:
class CopularNode(Node):
class PossessiveNode(Node):
class PropNode(Node):
class RCMODPropNode(Node):
class TimeNode(Node):
class LocationNode(Node):
class PrepNode(Node):
class CondNode(Node):
class AppositionNode(Node):
class ConjunctionNode(Node):
class advNode(Node):
    def __init__(self,isPredicate,text,features,valid,orderText = True):
    def get_text(self,gr):
    def copy(self):
    def addPropogation(self,node):
    def minIndex(self):
    def maxIndex(self):
    def __str__(self):
    def __hash__(self):
    def init(cls,index,features,valid):
    def init(cls,index,features,valid):
    def init(cls,features,valid,index,parent_relation):
    def copy(self):
    def is_relative(self):
    def is_prenominal(self):
    def get_text(self,gr):
    def init(cls,features,valid):
    def is_prenominal(self):
    def init(cls,features):
    def get_text(self,gr):
    def init(cls,features):
    def get_text(self,gr):
    def init(cls,index,prepType,features,valid):
    def copy(self):
    def get_text(self,gr):
    def is_time_prep(self):
    def init(cls,index,condType,features,valid):
    def get_text(self,gr):
    def copy(self):
    def init(cls,index,features):
    def init(cls,text,features):
    def copy(self):
    def get_text(self,gr):
    def init(cls,features):
def isCopular(node):
def isApposition(node):
def isProp(node):
def isRcmodProp(node):
def isConjunction(node):
def isPreposition(node):
def isTime(node):
def isLocation(node):
def isAdverb(node):
def isCondition(node):
def isDefinite(node):
def isNominal(node,gr):
def isPossessive(node):
def join(node1,node2,gr):
def addSymmetricPropogation(node1,node2):
 class Node:
    """
    node class
    
    represents a single node in the representation graph.
    @type isPredicate: bool
    @var  isPredicate: denotes if this node is a predicate
    
    @type text: list of Word object
    @var  text: the text contained within this node
    
    @type features: dict
    @var  features: syntactic features of this node (e.g., definiteness)
    
    @type propagateTo: list
    @var  propagateTo: list of Node objects onto which the properties of this node should propogate to
    
    @type span: list
    @var  span: list of indices in the original sentence which this node spans
    @todo think if this is needed, or consumed by Word
    
    @type valid: bool
    @var  valid: debug variable, indicates if this node should be converted

    @type uid: int
    @var  uid: unique id for this node, to be able to distinguish nodes with identical features
    """
    def __init__(self,isPredicate,text,features,valid,orderText = True):
        """
        initialize a node object
        
        @type  orderText: boolean
        @param orderText: defines if text elements should be sorted by indices upon printing in the __str__ function 
        """  
        self.isPredicate = isPredicate
        self.text = text
        self.features = features
        self.valid = valid
        global nodeCounter
        self.uid  = nodeCounter
        nodeCounter +=1
        self.propagateTo = []
        self.orderText = orderText
        self.nodeShape = DEFAULT_NODE_SHAPE
        self.__str__() # calculate variables in str
        
    def get_text(self,gr):
        return self.text
        
    def copy(self):
        """ 
        'copy constructor' 
        """
        
        # get proper type and new uid
        ret = self.__class__(isPredicate = self.isPredicate,
                             text = self.text,
                             features = self.features,
                             valid = self.valid)
        
        # copy propagations
        for curNode in self.propagateTo:
            addSymmetricPropogation(ret, curNode)
            
        return ret
    
    def addPropogation(self,node):
        """
        Add a node onto which this node's properties should propogate.
        
        @type  node: Node
        @param node: The node onto which to propogate
        """
        if node not in self.propagateTo:
            self.propagateTo.append(node)
            
    def minIndex(self):
        """ 
        Minimum index covered by this node
        
        @rtype: int
        """
        if not self.text:
            return NO_INDEX # TODO: why is this happenning?
        return min([w.index for w in self.text])
    
    def maxIndex(self):
        """ 
        Minimum index covered by this node
        
        @rtype: int
        """
        if not self.text:
            return NO_INDEX # TODO: why is this happenning?
        return max([w.index for w in self.text])
        
    def __str__(self):
        ret = '<TABLE BORDER="0" CELLSPACING="0"><TR><TD>'
        filtered_spans = []
        for feat,_ in PRINT_FEATURES:
            if (feat in self.features) and (isinstance(self.features[feat], dict)) and ("Span" in self.features[feat]):
                filtered_spans.extend(self.features[feat]["Span"])
        
        
        if 'Lemma' in self.features and len(self.text)==1:
            self.str = [Word(index = self.text[0].index,word=self.features['Lemma'])]
        else:
            ls = self.text
            if self.orderText:
                ls = sorted(self.text,key=lambda word:word.index)
            # self.str stores the words as displayed in the node
            self.str = [w for w in ls if w.index not in filtered_spans] 
            
        self.str = strip_punctuations(self.str)
            
        ret+= "  ".join([str(x) for x in self.str])
        
        
        ret+="</TD></TR>"
        for feat, printFunc in PRINT_FEATURES:
            if feat in self.features:
                if self.isPredicate and feat =="Definite":
                    continue
                ret += "<TR><TD>" 
                ret+= '<FONT POINT-SIZE="10">{0}</FONT>'.format(cgi.escape(str(printFunc(self.features[feat]))))
                ret+="</TD></TR>"
            
        ret +="</TABLE>" 
        return ret
    
    def __hash__(self):
        return self.__str__().__hash__()
 class Word:
    """
    word container class, to add the index of the word in addition to the word 
    
    @type index: int
    @var  index: the index of the word within the sentence
    
    @type text: string
    @var  text: the text contained within this word
    """
    
    def __init__(self,index,word):
        """
        initialize a word container object
        """
        self.index = index
        self.word = word
    
    def to_conll_like(self):
        return ",".join([str(self.index),self.word])
    
    def __str__(self):
        ret = cgi.escape(self.word)
        if self.index != NO_INDEX:
            ret += '<FONT POINT-SIZE="7">[{0}]</FONT>'.format(self.index)
        return ret
    
    def __eq__(self,other_word):
        return (self.index == other_word.index) and (self.word == other_word.word)
    
    def __hash__(self):
        return self.__str__().__hash__()
 def double_filter(f,ls):
    sucess=[]
    fail = []
    for x in ls:
        if f(x):
            sucess.append(x)
        else:
            fail.append(x)
    return (sucess,fail)
 ADVERBIALS = ["ADV", "VOC", "BNF", "DIR", "EXT", "LOC", "MNR", "TMP", "CLR","PRP"]

from props.graph_representation.node import APPOSITION, isApposition, CopularNode,\
    addSymmetricPropogation, isConjunction, isProp, isCondition, isPreposition,\
    isAdverb, isPossessive, EXISTENSIAL, COND, isTime, TimeNode, isLocation,\
    LocationNode, join, isCopular, isRcmodProp, isDefinite, PREP
from props.graph_representation.graph_utils import find_nodes, duplicate_node, sort_nodes_topologically,\
    sister_nodes, is_following, reattch, duplicate_component, deref,\
    fixPossessor, find_edges, findChain, duplicateEdge, delete_component,\
    isRCmod, get_min_max_span
from props.graph_representation.parse_graph import POSSESSOR_LABEL,\
    APPENDIX_EXISTENSIALS, APPENDIX_RANGE, appendix_types, APPENDIX_LOCATION,\
    FIRST_ENTITY_LABEL, SECOND_ENTITY_LABEL
from dependency_tree.definitions import EXPL_LABEL, SUBJ_LABEL,\
    subject_dependencies, SOURCE_LABEL, contractions, definite_label,\
    clausal_complements, object_dependencies
from props.graph_representation import word, node
from props.graph_representation.node import Node
from props.graph_representation.word import Word
from props.dependency_tree.tree import double_filter
from props.constituency_tree.my_definitions import any_in


class FixGraph:
    """
    class to bunch together all function of conversion from "almost graph" to a valid graph representation
    Mainly in order to store the graph as a member which all these functions can edit.   
    """
    
    def __init__(self,graph):
        # maintain an appendix for easier browsing
        self.types = appendix_types()
        self.gr = graph
        self.applyGeneralTransformations()
        self.fixExistensials()
        self.fixPossesive()
        self.fixApposition()
        self.fixProps()
    
    
    def applyGeneralTransformations(self):
        """
        Apply general transformations on the graph in clousre mode:
        1. prop followed by cond -> cond becomes son of prop
        2. prop followed by prep -> prep becomes son of prop
        3. adv followed by prop -> adv becomes son of prop
        4. collapse cond-that
        5. collapse conjunctions (when conjunction is headed by another conjunction
        6. able -> xcomp -> predicate ===> predicate(able)
        7. time -> time ===> time
        8. time preposition -> (time or location) ===> time / location
        9. prop-> conj -> elms  ---> prop-> elms
        10. detect ranges (location, times)
        11. reduce locations with more than one child
        12. reduce locations which are headed by prop
        13. reduce prop in cases of prop -> predicate
        14. reduce prop in cases of be->prop 
        15. (acomp) reduce a predicate who is modified by a feature and has a single, sujbect, negigbour 
        16. reduce node <-> prop
        17. reduce cases of sources pointing to contractions
        """
        
        def inner():
            change = False
            # 1,2
            nodes = find_nodes(self.gr, isCondition)
            nodes.extend(find_nodes(self.gr, isPreposition))
            for curNode in nodes:
                sisterNodes = sister_nodes(graph=self.gr, node=curNode)
                for sisterNode in sisterNodes:
                    if isProp(sisterNode) and is_following(graph=self.gr,
                                                           node1=sisterNode,
                                                           node2=curNode):
                        reattch(graph=self.gr, 
                                node=curNode, 
                                new_father=sisterNode)
                        return True
                        break
            # 3
            nodes = find_nodes(self.gr, isAdverb)
            for curNode in nodes:
                sisterNodes = sister_nodes(graph=self.gr, node=curNode)
                for sisterNode in sisterNodes:

                    if isProp(sisterNode) and is_following(graph=self.gr,
