def SubstringBefore(context, outer, inner): """Function: <string> substring-before(<string>, <string>)""" outer = Conversions.StringValue(outer) inner = Conversions.StringValue(inner) index = string.find(outer, inner) if index == -1: return '' return outer[:index]
def SubstringAfter(context, outer, inner): """Function: <string> substring-after(<string>, <string>)""" outer = Conversions.StringValue(outer) inner = Conversions.StringValue(inner) index = string.find(outer, inner) if index == -1: return '' return outer[index + len(inner):]
def Replace(context, old, new, arg=None): """Do a global search and replace of the string contents""" if not arg: arg = context.node arg = Conversions.StringValue(arg) old = Conversions.StringValue(old) new = Conversions.StringValue(new) return string.replace(arg, old, new)
def Contains(context, outer, inner): """Function: <string> contains(<string>, <string>)""" outer = Conversions.StringValue(outer) inner = Conversions.StringValue(inner) if len(inner) == 1: return inner in outer and boolean.true or boolean.false else: return string.find(outer, inner) != -1 and boolean.true or boolean.false
def range(context, lo, hi): doc = context.node while doc.parentNode: doc = doc.parentNode lo = Conversions.NumberValue(lo) hi = Conversions.NumberValue(hi) nodeset = [] for number in xrange(lo, hi): nodeset.append(doc.createTextNode(str(number))) return nodeset
def evaluate(self, context): '''returns a number''' if self._leftLit: lrt = self._left else: lrt = self._left.evaluate(context) lrt = Conversions.NumberValue(lrt) if self._rightLit: rrt = self._right else: rrt = self._right.evaluate(context) rrt = Conversions.NumberValue(rrt) return lrt + (rrt * self._sign)
def evaluate(self, context): if self._op == '=': true = boolean.true false = boolean.false else: true = boolean.false false = boolean.true lrt = self._left.evaluate(context) rrt = self._right.evaluate(context) lType = type(lrt) rType = type(rrt) if lType == types.ListType == rType: #Node set to node set for right_curr in rrt: right_curr = Conversions.StringValue(right_curr) for left_curr in lrt: if right_curr == Conversions.StringValue(left_curr): return true return false elif lType == types.ListType or rType == types.ListType: func = None if lType == types.ListType: set = lrt val = rrt else: set = rrt val = lrt if type(val) in NumberTypes: func = Conversions.NumberValue elif boolean.IsBooleanType(val): func = Conversions.BooleanValue elif type(val) == types.StringType: func = Conversions.StringValue else: #Deal with e.g. RTFs val = Conversions.StringValue(val) func = Conversions.StringValue for n in set: if func(n) == val: return true return false if boolean.IsBooleanType(lrt) or boolean.IsBooleanType(rrt): rt = Conversions.BooleanValue(lrt) == Conversions.BooleanValue(rrt) elif lType in NumberTypes or rType in NumberTypes: rt = Conversions.NumberValue(lrt) == Conversions.NumberValue(rrt) else: rt = Conversions.StringValue(lrt) == Conversions.StringValue(rrt) if rt: # Due to the swapping of true/false, true might evaluate to 0 # We cannot compact this to 'rt and true or false' return true return false
def __init__(self, sign, left, right): self._sign = sign self._leftLit = 0 self._rightLit = 0 if isinstance(left, ParsedLiteralExpr): self._leftLit = 1 self._left = Conversions.NumberValue(left.evaluate(None)) else: self._left = left if isinstance(right, ParsedLiteralExpr): self._rightLit = 1 self._right = Conversions.NumberValue(right.evaluate(None)) else: self._right = right return
def Translate(context, source, fromChars, toChars): """Function: <string> translate(<string>, <string>, <string>)""" source = Conversions.StringValue(source) fromChars = Conversions.StringValue(fromChars) toChars = Conversions.StringValue(toChars) # string.maketrans/translate do not handle unicode translate = {} for from_char, to_char in map(None, fromChars, toChars): translate[ord(from_char)] = to_char result = reduce(lambda a, b, t=translate: a + (t.get(ord(b), b) or ''), source, '') return result
def Match(context, pattern, arg=None): """Do a regular expression match against the argument""" if not arg: arg = context.node arg = Conversions.StringValue(arg) bool = re.match(pattern, arg) and boolean.true or boolean.false return bool
def Map(context, funcname, *nodesets): """ Apply the function serially over the given node sets. In iteration i, the function is passed N parameters where N is the number of argument node sets. Each parameter is a node set of size 1, whose node is the ith node of the corresponding argument node set. The return value is a node set consisting of a series of result-tree nodes, each of which is a text node whose value is the string value of the result of the ith function invocation. Warning: this function uses the implied ordering of the node set Based on its implementation as a Python list. But in reality There is no reliable ordering of XPath node sets. In other words, this function is voodoo. """ (prefix, local) = ExpandQName(funcname, namespaces=context.processorNss) func = (g_extFunctions.get(expanded) or CoreFunctions.CoreFunctions.get(expanded, None)) if not func: raise Exception('Dynamically invoked function %s not found.' % funcname) flist = [f] * len(nodesets) lf = lambda x, f, *args: apply(f, args) retlist = apply(map, (lf, flist) + nodesets) proc = context.processor result_nodeset = [] for ret in retlist: proc.pushResult() proc.writers[-1].text(Conversions.StringValue(ret)) frag = proc.popResult() context.rtfs.append(frag) result_nodeset.append(frag.childNodes[0]) return result_nodeset
def String(context, object=None): """Function: <string> string(<object>?)""" if type(object) in g_stringTypes: return object if object is None: object = [context.node] return Conversions.StringValue(object)
def __init__(self, opcode, left, right): self._op = opcode if isinstance(left, ParsedLiteralExpr): self._left = Conversions.NumberValue(left.evaluate(None)) self._leftLit = 1 else: self._left = left self._leftLit = 0 if isinstance(right, ParsedLiteralExpr): self._right = Conversions.NumberValue(right.evaluate(None)) self._rightLit = 1 else: self._right = right self._rightLit = 0
def SearchRePy20(context, pattern, arg=None): """Do a regular expression search against the argument (i.e. get all matches)""" if not arg: arg = context.node arg = Conversions.StringValue(arg) proc = context.processor matches_nodeset = [] _re = re.compile(pattern) _match = _re.search(arg) while _match: proc.pushResult() proc.writers[-1].startElement('Match', EMPTY_NAMESPACE) _groups = _match.groups() # .groups() return empty tuple when the pattern did not do grouping if not _groups: _groups = tuple(_match.group()) for group in _groups: proc.writers[-1].startElement('Group', EMPTY_NAMESPACE) # MatchObject groups return None if unmatched # unlike .findall() returning empty strings proc.writers[-1].text(group or '') proc.writers[-1].endElement('Group') proc.writers[-1].endElement('Match') frag = proc.popResult() context.rtfs.append(frag) matches_nodeset.append(frag.childNodes[0]) _match = _re.search(arg, _match.end()) return matches_nodeset
def Substring(context, st, start, end=None): """Function: <string> substring(<string>, <number>, <number>?)""" st = Conversions.StringValue(st) start = Conversions.NumberValue(start) if start is NaN: return '' start = int(round(start)) start = start > 1 and start - 1 or 0 if end is None: return st[start:] end = Conversions.NumberValue(end) if start is NaN: return st[start:] end = int(round(end)) return st[start:start + end]
def Concat(context, *args): """Function: <string> concat(<string>, <string>, ...)""" if len(args) < 1: raise RuntimeException(RuntimeException.WRONG_ARGUMENTS, 'concat', _("at least 2 arguments expected")) return reduce(lambda a, b, c=context: a + Conversions.StringValue(b), args, '')
def distinct(context, nodeset): if type(nodeset) != type([]): raise Exception("'distinct' parameter must be of type node-set!") nodes = {} for node in nodeset: nodes[Conversions.StringValue(node)] = node return nodes.values()
def split(context, arg, delim=None): doc = context.node while doc.parentNode: doc = doc.parentNode nodeset = [] for token in string.split(Conversions.StringValue(arg), delim): nodeset.append(doc.createTextNode(token)) return nodeset
def evaluate(self, context): if self._leftLit: lrt = self._left else: lrt = Conversions.NumberValue(self._left.evaluate(context)) if self._rightLit: rrt = self._right else: rrt = Conversions.NumberValue(self._right.evaluate(context)) if self._op == 0: rt = (lrt < rrt) elif self._op == 1: rt = (lrt <= rrt) elif self._op == 2: rt = (lrt > rrt) elif self._op == 3: rt = (lrt >= rrt) return rt and boolean.true or boolean.false
def evaluate(self, context): '''returns a number''' lrt = self._left.evaluate(context) lrt = Conversions.NumberValue(lrt) rrt = self._right.evaluate(context) rrt = Conversions.NumberValue(rrt) res = 0 if self._op == 0: res = lrt * rrt elif self._op == 1: if rrt == 0: res = NaN else: res = lrt / rrt elif self._op == 2: if rrt == 0: res = NaN else: res = lrt % rrt return res
def Id(context, object): """Function: <node-set> id(<object>)""" id_list = [] if type(object) != type([]): st = Conversions.StringValue(object) id_list = string.split(st) else: for n in object: id_list.append(Conversions.StringValue(n)) rt = [] for id in id_list: doc = context.node.ownerDocument or context.node elements = Util.ElementsById(doc.documentElement, id) if len(elements) > 1: raise RuntimeException(RuntimeException.WRONG_ARGUMENTS, 'id', _("argument not unique")) elif elements: # Must be 1 rt.append(elements[0]) return rt
def Floor(context, number): """Function: <number> floor(<number>)""" number = Conversions.NumberValue(number) #if type(number) in g_stringTypes: # number = string.atof(number) if int(number) == number: return number elif number < 0: return int(number) - 1 else: return int(number)
def Ceiling(context, number): """Function: <number> ceiling(<number>)""" number = Conversions.NumberValue(number) #if type(number) in g_stringTypes: # number = string.atof(number) if int(number) == number: return number elif number > 0: return int(number) + 1 else: return int(number)
def Lang(context, lang): """Function: <boolean> lang(<string>)""" lang = string.upper(Conversions.StringValue(lang)) node = context.node while node: lang_attr = filter(lambda x: x.name == 'xml:lang' and x.value, node.attributes.values()) value = lang_attr and lang_attr[0].nodeValue or None if value: # See if there is a suffix index = string.find(value, '-') if index != -1: value = value[:index] value = string.upper(value) return value == lang and boolean.true or boolean.false node = node.nodeType == Node.ATTRIBUTE_NODE and node.ownerElement or node.parentNode return boolean.false
def SearchRe(context, pattern, arg=None): """Do a regular expression search against the argument (i.e. get all matches)""" if not arg: arg = context.node arg = Conversions.StringValue(arg) matches = re.findall(pattern, arg) proc = context.processor matches_nodeset = [] for groups in matches: proc.pushResult() proc.writers[-1].startElement('Match', EMPTY_NAMESPACE) if type(groups) != type(()): groups = (groups, ) for group in groups: proc.writers[-1].startElement('Group', EMPTY_NAMESPACE) proc.writers[-1].text(group) proc.writers[-1].endElement('Group') proc.writers[-1].endElement('Match') frag = proc.popResult() context.rtfs.append(frag) matches_nodeset.append(frag.childNodes[0]) return matches_nodeset
def filter(self, nodeList, context, reverse): if self._length: state = context.copyNodePosSize() for pred in self._predicates: size = len(nodeList) ctr = 0 current = nodeList nodeList = [] for node in current: position = (reverse and size - ctr) or (ctr + 1) context.setNodePosSize((node, position, size)) res = pred.evaluate(context) if type(res) in NumberTypes: # This must be separate to prevent falling into # the boolean check. if res == position: nodeList.append(node) elif Conversions.BooleanValue(res): nodeList.append(node) ctr = ctr + 1 context.setNodePosSize(state) return nodeList
def Round(context, number): """Function: <number> round(<number>)""" number = Conversions.NumberValue(number) return round(number, 0)
def Sum(context, nodeSet): """Function: <number> sum(<node-set>)""" nns = map(lambda x: Conversions.NumberValue(x), nodeSet) return reduce(lambda x, y: x + y, nns, 0)
def Number(context, object=None): """Function: <number> number(<object>?)""" if object is None: object = [context.node] return Conversions.NumberValue(object)
def Not(context, object): """Function: <boolean> not(<boolean>)""" return (not Conversions.BooleanValue(object) and boolean.true) or boolean.false