def Key(context, qname, keyList):
    """
    Implementation of key().

    The first argument specifies the name of the key. When the second
    argument to the key function is of type node-set, then the result
    is the union of the result of applying the key function to the
    string value of each of the nodes in the argument node-set.
    When the second argument to key is of any other type, the argument
    is converted to a string as if by a call to the string function; it
    returns a node-set containing the nodes in the same document as the
    context node that have a value for the named key equal to this string.
    """
    qname = Conversions.StringValue(qname)
    if not qname:
        raise XsltRuntimeException(Error.INVALID_QNAME_ARGUMENT,
                                   context.currentInstruction, qname)
    split_name = context.expandQName(Conversions.StringValue(qname))
    doc = context.node.rootNode
    try:
        keys_for_context_doc = context.processor.keys[doc]
        requested_key = keys_for_context_doc[split_name]
    except KeyError:
        sheet = context.processor.stylesheet
        sheet.updateKey(doc, split_name, context.processor)
        keys_for_context_doc = context.processor.keys[doc]
        requested_key = keys_for_context_doc[split_name]

    result = []
    if not isinstance(keyList, NodesetType):
        keyList = [keyList]
    for key in keyList:
        key = Conversions.StringValue(key)
        result.extend(requested_key.get(key, []))
    return result
Beispiel #2
0
def Tokenize(context, string, delimiters='\t\n\r '):
    """
    The str:tokenize function splits up a string and returns a node set of
    'token' elements, each containing one token from the string.

    The first argument is the string to be tokenized. The second argument
    is a string consisting of a number of characters. Each character in
    this string is taken as a delimiting character. The string given by the
    first argument is split at any occurrence of any of these characters.
    """
    string = Conversions.StringValue(string)
    if delimiters:
        delimiters = Conversions.StringValue(delimiters)
        tokens = re.split('[%s]' % delimiters, string)
    else:
        tokens = string

    processor = context.processor
    processor.pushResultTree(context.currentInstruction.baseUri)
    try:
        for token in tokens:
            processor.writer.startElement(u'token')
            processor.writer.text(token)
            processor.writer.endElement(u'token')
    finally:
        rtf = processor.popResult()
    return rtf.childNodes
def Replace(context, source, pattern, flags, repl):
    """
    The regexp:replace function replaces the parts of a string that match
    a regular expression with another string.

    The first argument is the string to be matched and replaced. The second
    argument is a regular expression that follows the Javascript regular
    expression syntax. The fourth argument is the string to replace the
    matched parts of the string.

    The third argument is a string consisting of character flags to be used
    by the match. If a character is present then that flag is true. The flags
    are:
      g: global replace - all occurrences of the regular expression in the
                          string are replaced. If this character is not
                          present, then only the first occurrence of the
                          regular expression is replaced.
      i: case insensitive - the regular expression is treated as case
                            insensitive. If this character is not present,
                            then the regular expression is case sensitive.
    """
    source = Conversions.StringValue(source)
    pattern = Conversions.StringValue(pattern)
    flags = Conversions.StringValue(flags)
    repl = Conversions.StringValue(repl)

    regexp = re.compile(pattern, 'i' in flags and re.IGNORECASE or 0)
    # a count of zero means replace all in RE.sub()
    return regexp.sub(repl, source, 'g' not in flags)
Beispiel #4
0
def DecodeUri(context, uri, encoding=u'UTF-8'):
    """
    The str:decode-uri function decodes a percent-encoded string, such as
    one would find in a URI.
    """
    uri = Conversions.StringValue(uri)
    encoding = Conversions.StringValue(encoding)
    try:
        decoder = codecs.lookup(encoding)[1]
    except LookupError:
        # Unsupported encoding
        return u''

    def repl(match, decoder=decoder):
        # Remove the leading '%'
        sequence = match.group()[1:]
        # There may be multiple encoded characters that are required
        # to produce a single Unicode character.
        ordinals = sequence.split('%')
        characters = [chr(int(ordinal, 16)) for ordinal in ordinals]

        # Ignore any invalid sequences in this encoding
        return decoder(''.join(characters), 'ignore')[0]

    return re.sub('(?:%[0-9a-fA-F]{2})+', repl, uri)
Beispiel #5
0
def Align(context, target, padding, alignment=''):
    """
    The str:align function aligns a string within another string.

    See http://exslt.org/str/functions/align/str.align.html for further
    explanation.
    """
    target = Conversions.StringValue(target)
    padding = Conversions.StringValue(padding)
    alignment = alignment and Conversions.StringValue(alignment)

    # If the target string is longer than the padding string, then it is
    # truncated to be the same length as the padding string and returned.
    if len(target) > len(padding):
        return target[:len(padding)]

    # If no third argument is given or if it is not one of 'left', 'right'
    # or 'center', then it defaults to left alignment.
    if alignment == 'right':
        result = padding[:-len(target)] + target
    elif alignment == 'center':
        # With center alignment, the range of characters replaced by the target
        # string is in the middle of the padding string, such that either the
        # number of unreplaced characters on either side of the range is the
        # same or there is one less on the left than there is on the right.
        left = (len(padding) - len(target)) / 2
        right = left + len(target)
        result = padding[:left] + target + padding[right:]
    else:
        result = target + padding[len(target):]
    return result
Beispiel #6
0
def Split(context, string, pattern=u' '):
    """
    The str:split function splits up a string and returns a node set of
    token elements, each containing one token from the string.

    The first argument is the string to be split. The second argument is a
    pattern string (default=' '). The string given by the first argument is
    split at any occurrence of this pattern. An empty string pattern will
    result in a split on every character in the string.
    """
    string = Conversions.StringValue(string)
    pattern = Conversions.StringValue(pattern)
    processor = context.processor
    processor.pushResultTree(context.currentInstruction.baseUri)
    try:
        if pattern:
            for token in string.split(pattern):
                processor.writer.startElement(u'token')
                processor.writer.text(token)
                processor.writer.endElement(u'token')
        else:
            for ch in string:
                processor.writer.startElement(u'token')
                processor.writer.text(ch)
                processor.writer.endElement(u'token')
    finally:
        rtf = processor.popResult()
    return rtf.childNodes
Beispiel #7
0
def pipp_thumbnail(context, src, width, height):
    ctx = context.processor.extensionParams[(NAMESPACE, 'context')]
    image_name = ctx.abs_in_path(Conversions.StringValue(src))
    ctx.add_depends(image_name[len(ctx.in_root):])
    thumb_name = re.sub('(\.\w+)$', '_thumb\g<1>',
                        Conversions.StringValue(src))

    if width:
        width = int(Conversions.NumberValue(width))
    if height:
        height = int(Conversions.NumberValue(height))

    img = Image.open(image_name)
    w, h = img.size

    if height and not width:
        width = int(w * height / h)
    if width and not height:
        height = int(h * width / w)

    img = img.resize((width, height))
    img.save(ctx.abs_out_path(ctx.abs_in_path(thumb_name)))

    #--
    # Add image to cache using fake inroot name, so width/height functions work
    #--
    images[ctx.abs_in_path(thumb_name)] = img

    return thumb_name
Beispiel #8
0
def pipp_code(context, src, code, lexer, docss):
    ctx = context.processor.extensionParams[(NAMESPACE, 'context')]

    src = Conversions.StringValue(src)
    if src:
        abs_src = ctx.abs_in_path(src)
        ctx.add_depends(abs_src[len(ctx.in_root):])
        fname = os.path.basename(src)
        code = open(abs_src).read()
    else:
        fname = 'inline-code'
        code = Conversions.StringValue(code)

    lexer = Conversions.StringValue(lexer)
    if lexer:
        lexer = get_lexer_by_name(lexer)
    elif src:
        lexer = get_lexer_for_filename(fname)
    else:
        raise Exception(
            'The lexer must be explicitly specified for inline code blocks')

    formatter = HtmlFormatter(cssclass="source")
    result = highlight(code, lexer, formatter)
    if Conversions.StringValue(docss) == '1':
        result = '<link rel="stylesheet" href="%s.css"/>' % fname + result
        css = open(ctx.abs_out_path(ctx.abs_in_path(fname + '.css')), 'w')
        css.write(formatter.get_style_defs())
        css.close()

    return result
Beispiel #9
0
def pipp_export(context, name, value):
    ctx = context.processor.extensionParams[(NAMESPACE, 'context')]
    new_node = ctx.state_doc.createElementNS(EMPTY_NAMESPACE,
                                             Conversions.StringValue(name))
    new_node.appendChild(
        ctx.state_doc.createTextNode(Conversions.StringValue(value)))
    ctx.exports_node.appendChild(new_node)
Beispiel #10
0
    def evaluate(self, context):
        """Returns a boolean"""
        left = self._left.evaluate(context)
        right = self._right.evaluate(context)

        if isinstance(left, Types.NodesetType) or \
           isinstance(right, Types.NodesetType):
            return _nodeset_compare(self._cmp, left, right)

        # From XPath 1.0 Section 3.4:
        # order for equality expressions when neither is a node-set
        # 1. either is boolean, both are converted as if by boolean()
        # 2. either is number, both are converted as if by number()
        # otherwise, both are converted as if by string()
        if isinstance(left, Types.BooleanType):
            right = Conversions.BooleanValue(right)
        elif isinstance(right, Types.BooleanType):
            left = Conversions.BooleanValue(left)
        elif isinstance(left, Types.NumberType):
            right = Conversions.NumberValue(right)
        elif isinstance(right, Types.NumberType):
            left = Conversions.NumberValue(left)
        else:
            left = Conversions.StringValue(left)
            right = Conversions.StringValue(right)
        return self._cmp(left, right) and boolean.true or boolean.false
Beispiel #11
0
def pipp_link(context, link):
    """Record a link"""
    ctx = context.processor.extensionParams[(NAMESPACE, 'context')]
    new_node = ctx.state_doc.createElementNS(EMPTY_NAMESPACE,
                                             Conversions.StringValue('link'))
    new_node.appendChild(
        ctx.state_doc.createTextNode(Conversions.StringValue(link)))
    ctx.links_node.appendChild(new_node)
Beispiel #12
0
def EndsWith(context, outer, inner):
    """
    Returns true if the string given in the first argument ends with
    the substring given in the second argument.
    """
    outer = Conversions.StringValue(outer)
    inner = Conversions.StringValue(inner)
    return outer.endswith(inner) and boolean.true or boolean.false
def Match(context, source, pattern, flags=''):
    """
    The regexp:match function lets you get hold of the substrings of the
    string passed as the first argument that match the captured parts of
    the regular expression passed as the second argument.

    The second argument is a regular expression that follows the Javascript
    regular expression syntax.

    The third argument is a string consisting of character flags to be used
    by the match. If a character is present then that flag is true. The
    flags are:
      g: global match - the submatches from all the matches in the string
                        are returned. If this character is not present, then
                        only the submatches from the first match in the
                        string are returned.
      i: case insensitive - the regular expression is treated as case
                            insensitive. If this character is not present,
                            then the regular expression is case sensitive.

    The regexp:match function returns a node set of 'match' elements, each of
    whose string value is equal to a portion of the first argument string
    that was captured by the regular expression. If the match is not global,
    the first match element has a value equal to the portion of the string
    matched by the entire regular expression.
    """
    source = Conversions.StringValue(source)
    pattern = Conversions.StringValue(pattern)
    flags = flags and Conversions.StringValue(flags)

    regexp = re.compile(pattern, 'i' in flags and re.IGNORECASE or 0)

    match = regexp.search(source)
    if match is None:
        return []
    processor = context.processor
    processor.pushResultTree(context.currentInstruction.baseUri)
    try:
        if 'g' in flags:
            # find all matches in the source
            while match:
                processor.writer.startElement(u'match')
                # return everything that matched the pattern
                processor.writer.text(match.group())
                processor.writer.endElement(u'match')
                match = regexp.search(source, match.end())
        else:
            # the first 'match' element contains entire matched text
            all = [match.group()]
            groups = match.groups()
            groups and all.extend(list(groups))
            for match in all:
                processor.writer.startElement(u'match')
                match and processor.writer.text(match)
                processor.writer.endElement(u'match')
    finally:
        rtf = processor.popResult()
    return rtf.childNodes
Beispiel #14
0
def ResolvePath(context, base, rel):
    """
    Resolves a Posix-style path, such as the path portion of a URL,
    against a base. Similar to f:resolve-url, but allows the base to be
    just a path, not necessarily a full URL.
    """
    base = Conversions.StringValue(base)
    rel = Conversions.StringValue(rel)
    return Uri.BaseJoin(base, rel)
Beispiel #15
0
def Contains(context, outer, inner):
    """Function: <string> contains(<string>, <string>)"""
    if not isinstance(outer, XPathStringType):
        outer = Conversions.StringValue(outer)
    if not isinstance(inner, XPathStringType):
        inner = Conversions.StringValue(inner)
    if not inner:
        return boolean.true
    return outer.find(inner) >= 0 and boolean.true or boolean.false
Beispiel #16
0
def StartsWith(context, outer, inner):
    """Function: <string> starts-with(<string>, <string>)"""
    if not isinstance(outer, XPathStringType):
        outer = Conversions.StringValue(outer)
    if not isinstance(inner, XPathStringType):
        inner = Conversions.StringValue(inner)
    if not inner:
        return boolean.true
    return outer[:len(inner)] == inner and boolean.true or boolean.false
def Lookup(context, name, key):
    """
    f:lookup() queries an index as defined by f:create-index.
    """
    name = Conversions.StringValue(name)
    key = Conversions.StringValue(key)
    processor = context.processor
    indices = processor.extensionParams.get((FT_EXT_NAMESPACE, 'indices'), {})
    index = indices.get(name, {})
    value = index.get(key, [])
    return value
Beispiel #18
0
def ResolveUrl(context, base, rel):
    """
    Returns the relative URL ref given in the second argument
    resolved against the base given in the first argument.
    In case of URI processing error an empty string is returned
    """
    base = Conversions.StringValue(base)
    rel = Conversions.StringValue(rel)
    try:
        return Uri.Absolutize(rel, base)
    except Uri.UriException:
        return u''
Beispiel #19
0
def SubstringAfter(context, outer, inner):
    """Function: <string> substring-after(<string>, <string>)"""
    if not isinstance(outer, XPathStringType):
        outer = Conversions.StringValue(outer)
    if not isinstance(inner, XPathStringType):
        inner = Conversions.StringValue(inner)
    if not inner:
        return u''
    index = outer.find(inner)
    if index == -1:
        return u''
    return outer[index + len(inner):]
Beispiel #20
0
def pipp_import_join(context, name, join_str):
    ctx = context.processor.extensionParams[(NAMESPACE, 'context')]
    name = Conversions.StringValue(name)
    cur_doc = ctx.state_node
    values = []
    while cur_doc:
        ctx.add_edepends(cur_doc.getAttributeNS(EMPTY_NAMESPACE, 'src'), name)
        nodes = cur_doc.xpath("exports/*[name()='%s']" % name.replace("'", ""))
        if nodes:
            values.insert(0, get_text(nodes[0]))
        cur_doc = cur_doc.parentNode.parentNode
    return Conversions.StringValue(join_str).join(values)
Beispiel #21
0
def ParseDate(context, date, format=None):
    """
    This function is similar to EXSLT's date:parse-date()
    except that it uses Python rather than Java conventions
    for the date formatting.
    """
    import time
    date = Conversions.StringValue(date)
    format = Conversions.StringValue(format)
    time_tuple = time.strptime(format)
    #perhaps add some variants for missing time tuple values?
    str_time = time.strftime("%Y-%m-%dT%H:%M:%S", time_tuple)
    return unicode(str_time, 'us-ascii', errors='replace')
Beispiel #22
0
def Replace(context, old, new, arg=None):
    """
    Returns the third argument string, which defaults to the
    string-value of the context node, with occurrences of the substring
    given in the first argument replaced by the string given in the
    second argument.
    See also: EXSLT's str:replace()
    """
    if not arg:
        arg = context.node
    arg = Conversions.StringValue(arg)
    old = Conversions.StringValue(old)
    new = Conversions.StringValue(new)
    return arg.replace(old, new)
Beispiel #23
0
def StrFTime(context, format, date=None):
    """
    Returns the given ISO 8601 UTC date-time formatted according to
    the given format string as would be used by Python's
    time.strftime(). If no date-time string is given, the current
    time is used.
    """
    format = Conversions.StringValue(format)
    if date is not None:
        date = Conversions.StringValue(date)
        time_str = time.strftime(format, time.strptime(date, '%Y-%m-%dT%H:%M:%SZ'))
    else:
        time_str = time.strftime(format)
    return unicode(time_str, 'us-ascii', errors='replace')
Beispiel #24
0
def Seconds(context, string=None):
    """
    The date:seconds function returns the number of seconds specified by the
    argument string. If no argument is given, then the current local
    date/time, as returned by date:date-time is used as a default argument.

    Implements version 1.
    """
    if string is None:
        string = str(_DateTime.now())
    else:
        string = Conversions.StringValue(string)

    try:
        if 'P' in string:
            # its a duration
            duration = _Duration.parse(string)
        else:
            # its a dateTime
            dateTime = _DateTime.parse(
                string, ('dateTime', 'date', 'gYearMonth', 'gYear'))
            duration = _difference(_EPOCH, dateTime)
    except ValueError:
        return number.nan

    # The number of years and months must both be equal to zero
    if duration.years or duration.months:
        return number.nan

    # Convert the duration to just seconds
    seconds = (duration.days * 86400 + duration.hours * 3600 +
               duration.minutes * 60 + duration.seconds)
    if duration.negative:
        seconds *= -1
    return seconds
Beispiel #25
0
def EscapeXml(context, text):
    """
    Returns the given string with XML markup characters "&", "<" and
    ">" escaped as "&amp;", "&lt;" and "&gt;", respectively.
    """
    from xml.sax.saxutils import escape
    return escape(Conversions.StringValue(text))
Beispiel #26
0
def Lang(context, lang):
    """Function: <boolean> lang(<string>)"""
    lang = Conversions.StringValue(lang).lower()
    node = context.node
    while node.parentNode:
        for attr in node.attributes.values():
            # Search for xml:lang attribute
            if (attr.localName == 'lang'
                    and attr.namespaceURI == XML_NAMESPACE):
                value = attr.nodeValue.lower()
                # Exact match (PrimaryPart and possible SubPart)
                if value == lang:
                    return boolean.true

                # Just PrimaryPart (ignore '-' SubPart)
                index = value.find('-')
                if index != -1 and value[:index] == lang:
                    return boolean.true

                # Language doesn't match
                return boolean.false

        # Continue to next ancestor
        node = node.parentNode

    # No xml:lang declarations found
    return boolean.false
Beispiel #27
0
def Id(context, object_):
    """Function: <node-set> id(<object>)"""
    if not isinstance(object_, NodesetType):
        st = Conversions.StringValue(object_)
        id_list = st.split()
    else:
        id_list = [Conversions.StringValue(n) for n in object_]

    id_list = Set.Unique(id_list)
    doc = context.node.rootNode
    nodeset = []
    for id in id_list:
        element = doc.getElementById(id)
        if element:
            nodeset.append(element)
    return nodeset
Beispiel #28
0
def pipp_child(context, file_name):
    ctx = context.processor.extensionParams[(NAMESPACE, 'context')]
    file_name = ctx.abs_in_path(Conversions.StringValue(file_name)) \
                                [len(ctx.in_root):]
    new_node = ctx.state_doc.createElementNS(EMPTY_NAMESPACE, 'page')
    new_node.setAttributeNS(EMPTY_NAMESPACE, 'src', file_name)
    ctx.children_node.appendChild(new_node)
Beispiel #29
0
 def instantiate(self, context, processor):
     doc = context.node.rootNode
     if self._raw.evaluate(context):
         processor.xslMessage(repr(context.varBindings))
     else:
         from Ft.Xml.XPath.XPathTypes import g_xpathPrimitiveTypes
         from Ft.Xml.Xslt.CopyOfElement import CopyNode
         writer = processor.writer
         writer.startElement(u'zz:VarDump', RESERVED_NAMESPACE)
         for k, v in context.varBindings.items():
             writer.startElement(u'zz:Var', RESERVED_NAMESPACE)
             #FIXME: should try to join back prefix to var name
             writer.attribute(u'name', k[1], EMPTY_NAMESPACE)
             if isinstance(v, list):
                 # NOTE - this must be before the primitive check due to
                 #  the fact that a node-set is a primitive type
                 for node in v:
                     if node.nodeType == Node.ATTRIBUTE_NODE:
                         processor.writer.comment(
                             u"Attribute: %s=%s" %
                             (node.nodeName, node.value))
                     else:
                         CopyNode(processor, node)
             elif type(v) in g_xpathPrimitiveTypes:
                 writer.text(Conversions.StringValue(v))
             elif hasattr(v, 'nodeType'):
                 CopyNode(processor, v)
             writer.endElement(u'zz:Var', RESERVED_NAMESPACE)
         writer.endElement(u'zz:VarDump', RESERVED_NAMESPACE)
     return
Beispiel #30
0
def pipp_map_view(context, xslt_file):
    ctx = context.processor.extensionParams[(NAMESPACE, 'context')]

    #--
    # Create the XSLT processor object. For efficiency there is a cache of these.
    #--
    xslt_file = ctx.abs_in_path(Conversions.StringValue(xslt_file))
    ctx.add_depends(xslt_file[len(ctx.in_root):])
    processor = processors.get(xslt_file)
    if not processor:
        processor = Processor.Processor()
        processor.registerExtensionModules(['pipp_xslt'])
        processor.appendStylesheet(
            InputSource.DefaultFactory.fromString(
                open(xslt_file).read(), xslt_file))
    processor.extensionParams[(NAMESPACE, 'context')] = ctx

    #--
    # Run the processor against state.xml and return the output.
    # If successful, store the processor object in a cache
    #--
    input = InputSource.DefaultFactory.fromUri(OsPathToUri(ctx.state_xml))
    output = processor.run(input)
    processors[xslt_file] = processor
    return output