<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2009 Edgewall Software
# Copyright (C) 2005 <NAME> <<EMAIL>>
# Copyright (C) 2005-2007 <NAME> <<EMAIL>>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: <NAME> <<EMAIL>>
#         <NAME> <<EMAIL>>

import posixpath

from genshi.builder import tag

from trac.core import *
from trac.versioncontrol import NoSuchNode
from trac.versioncontrol.svn_fs import _path_within_scope
from trac.versioncontrol.web_ui.browser import IPropertyRenderer
from trac.versioncontrol.web_ui.changeset import IPropertyDiffRenderer
from trac.util import Ranges, to_ranges
from trac.util.translation import _, tag_


class SubversionPropertyRenderer(Component):
    implements(IPropertyRenderer)

    def __init__(self):
        self._externals_map = {}

    # IPropertyRenderer methods

    def match_property(self, name, mode):
        if name in ('svn:externals', 'svn:needs-lock'):
            return 4
        return name in ('svn:mergeinfo', 'svnmerge-blocked',
                        'svnmerge-integrated') and 2 or 0
    
    def render_property(self, name, mode, context, props):
        if name == 'svn:externals':
            return self._render_externals(props[name])
        elif name == 'svn:needs-lock':
            return self._render_needslock(context)
        elif name == 'svn:mergeinfo' or name.startswith('svnmerge-'):
            return self._render_mergeinfo(name, mode, context, props)

    def _render_externals(self, prop):
        if not self._externals_map:
            for dummykey, value in self.config.options('svn:externals'):
                value = value.split()
                if len(value) != 2:
                    self.log.warn("svn:externals entry %s doesn't contain "
                            "a space-separated key value pair, skipping.", 
                            label)
                    continue
                key, value = value
                self._externals_map[key] = value.replace('%', '%%') \
                                           .replace('$path', '%(path)s') \
                                           .replace('$rev', '%(rev)s')
        externals = []
        for external in prop.splitlines():
            elements = external.split()
            if not elements:
                continue
            localpath, rev, url = elements[0], '', elements[-1]
            if localpath.startswith('#'):
                externals.append((external, None, None, None, None))
                continue
            if len(elements) == 3:
                rev = elements[1]
                rev = rev.replace('-r', '')
            # retrieve a matching entry in the externals map
            prefix = []
            base_url = url
            while base_url:
                if base_url in self._externals_map or base_url==u'/':
                    break
                base_url, pref = posixpath.split(base_url)
                prefix.append(pref)
            href = self._externals_map.get(base_url)
            revstr = rev and ' at revision '+rev or ''
            if not href and (url.startswith('http://') or 
                             url.startswith('https://')):
                href = url.replace('%', '%%')
            if href:
                remotepath = ''
                if prefix:
                    remotepath = posixpath.join(*reversed(prefix))
                externals.append((localpath, revstr, base_url, remotepath,
                                  href % {'path': remotepath, 'rev': rev}))
            else:
                externals.append((localpath, revstr, url, None, None))
        externals_data = []
        for localpath, rev, url, remotepath, href in externals:
            label = localpath
            if url is None:
                title = ''
            elif href:
                if url:
                    url = ' in ' + url
                label += rev + url
                title = ''.join((remotepath, rev, url))
            else:
                title = _('No svn:externals configured in trac.ini')
            externals_data.append((label, href, title))
        return tag.ul([tag.li(tag.a(label, href=href, title=title))
                       for label, href, title in externals_data])

    def _render_needslock(self, context):
        return tag.img(src=context.href.chrome('common/lock-locked.png'),
                       alt="needs lock", title="needs lock")

    def _render_mergeinfo(self, name, mode, context, props):
        rows = []
        for row in props[name].splitlines():
            try:
                (path, revs) = row.rsplit(':', 1)
                rows.append([tag.td(path),
                             tag.td(revs.replace(',', u',\u200b'))])
            except ValueError:
                rows.append(tag.td(row, colspan=2))
        return tag.table(tag.tbody([tag.tr(row) for row in rows]),
                         class_='props')


class SubversionMergePropertyRenderer(Component):
    implements(IPropertyRenderer)

    # IPropertyRenderer methods

    def match_property(self, name, mode):
        return name in ('svn:mergeinfo', 'svnmerge-blocked',
                        'svnmerge-integrated') and 4 or 0
    
    def render_property(self, name, mode, context, props):
        """Parse svn:mergeinfo and svnmerge-* properties, converting branch
        names to links and providing links to the revision log for merged
        and eligible revisions.
        """
        has_eligible = name in ('svnmerge-integrated', 'svn:mergeinfo')
        revs_label = (_('merged'), _('blocked'))[name.endswith('blocked')]
        revs_cols = has_eligible and 2 or None
        repos = self.env.get_repository()
        target_path = context.resource.id
        target_rev = context.resource.version
        if has_eligible:
            node = repos.get_node(target_path, target_rev)
            branch_starts = {}
            for path, rev in node.get_copy_ancestry(): 
                if path not in branch_starts:
                    branch_starts[path] = rev + 1
        rows = []
        for line in props[name].splitlines():
            path, revs = line.split(':', 1)
            spath = _path_within_scope(repos.scope, path)
            if spath is None:
                continue
            revs = revs.strip()
            deleted = False
            try:
                node = repos.get_node(spath, target_rev)
                if 'LOG_VIEW' in context.perm('source', spath):
                    row = [_get_source_link(spath, context),
                           _get_revs_link(revs_label, context, spath, revs)]
                    if has_eligible:
                        first_rev = branch_starts.get(spath)
                        eligible = set(repos._get_node_revs(spath, target_rev,
                                                            first_rev))
                        eligible -= set(Ranges(revs))
                        blocked = _get_blocked_revs(props, name, spath)
                        eligible -= set(Ranges(blocked))
                        eligible = to_ranges(eligible)
                        row.append(_get_revs_link(_('eligible'), context,
                                                  spath, eligible))
                    rows.append((False, spath, [tag.td(each) for each in row]))
                    continue
            except NoSuchNode:
                deleted = True
            revs = revs.replace(',', u',\u200b')
            rows.append((deleted, spath,
                         [tag.td('/' + spath),
                          tag.td(revs, colspan=revs_cols)]))
        if not rows:<commit_msg>0.12dev: ported svnmerge.py properties rendering fix from 0.11-stable<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2005-2009 Edgewall Software
# Copyright (C) 2005 <NAME> <<EMAIL>>
# Copyright (C) 2005-2007 <NAME> <<EMAIL>.fr>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: <NAME> <<EMAIL>>
#         <NAME> <<EMAIL>>

import posixpath

from genshi.builder import tag

from trac.core import *
from trac.versioncontrol import NoSuchNode
from trac.versioncontrol.svn_fs import _path_within_scope
from trac.versioncontrol.web_ui.browser import IPropertyRenderer
from trac.versioncontrol.web_ui.changeset import IPropertyDiffRenderer
from trac.util import Ranges, to_ranges
from trac.util.translation import _, tag_


class SubversionPropertyRenderer(Component):
    implements(IPropertyRenderer)

    def __init__(self):
        self._externals_map = {}

    # IPropertyRenderer methods

    def match_property(self, name, mode):
        if name in ('svn:externals', 'svn:needs-lock'):
            return 4
        return name in ('svn:mergeinfo', 'svnmerge-blocked',
                        'svnmerge-integrated') and 2 or 0
    
    def render_property(self, name, mode, context, props):
        if name == 'svn:externals':
            return self._render_externals(props[name])
        elif name == 'svn:needs-lock':
            return self._render_needslock(context)
        elif name == 'svn:mergeinfo' or name.startswith('svnmerge-'):
            return self._render_mergeinfo(name, mode, context, props)

    def _render_externals(self, prop):
        if not self._externals_map:
            for dummykey, value in self.config.options('svn:externals'):
                value = value.split()
                if len(value) != 2:
                    self.log.warn("svn:externals entry %s doesn't contain "
                            "a space-separated key value pair, skipping.", 
                            label)
                    continue
                key, value = value
                self._externals_map[key] = value.replace('%', '%%') \
                                           .replace('$path', '%(path)s') \
                                           .replace('$rev', '%(rev)s')
        externals = []
        for external in prop.splitlines():
            elements = external.split()
            if not elements:
                continue
            localpath, rev, url = elements[0], '', elements[-1]
            if localpath.startswith('#'):
                externals.append((external, None, None, None, None))
                continue
            if len(elements) == 3:
                rev = elements[1]
                rev = rev.replace('-r', '')
            # retrieve a matching entry in the externals map
            prefix = []
            base_url = url
            while base_url:
                if base_url in self._externals_map or base_url==u'/':
                    break
                base_url, pref = posixpath.split(base_url)
                prefix.append(pref)
            href = self._externals_map.get(base_url)
            revstr = rev and ' at revision '+rev or ''
            if not href and (url.startswith('http://') or 
                             url.startswith('https://')):
                href = url.replace('%', '%%')
            if href:
                remotepath = ''
                if prefix:
                    remotepath = posixpath.join(*reversed(prefix))
                externals.append((localpath, revstr, base_url, remotepath,
                                  href % {'path': remotepath, 'rev': rev}))
            else:
                externals.append((localpath, revstr, url, None, None))
        externals_data = []
        for localpath, rev, url, remotepath, href in externals:
            label = localpath
            if url is None:
                title = ''
            elif href:
                if url:
                    url = ' in ' + url
                label += rev + url
                title = ''.join((remotepath, rev, url))
            else:
                title = _('No svn:externals configured in trac.ini')
            externals_data.append((label, href, title))
        return tag.ul([tag.li(tag.a(label, href=href, title=title))
                       for label, href, title in externals_data])

    def _render_needslock(self, context):
        return tag.img(src=context.href.chrome('common/lock-locked.png'),
                       alt="needs lock", title="needs lock")

    def _render_mergeinfo(self, name, mode, context, props):
        rows = []
        for row in props[name].splitlines():
            try:
                (path, revs) = row.rsplit(':', 1)
                rows.append([tag.td(path),
                             tag.td(revs.replace(',', u',\u200b'))])
            except ValueError:
                rows.append(tag.td(row, colspan=2))
        return tag.table(tag.tbody([tag.tr(row) for row in rows]),
                         class_='props')


class SubversionMergePropertyRenderer(Component):
    implements(IPropertyRenderer)

    # IPropertyRenderer methods

    def match_property(self, name, mode):
        return name in ('svn:mergeinfo', 'svnmerge-blocked',
                        'svnmerge-integrated') and 4 or 0
    
    def render_property(self, name, mode, context, props):
        """Parse svn:mergeinfo and svnmerge-* properties, converting branch
        names to links and providing links to the revision log for merged
        and eligible revisions.
        """
        has_eligible = name in ('svnmerge-integrated', 'svn:mergeinfo')
        revs_label = (_('merged'), _('blocked'))[name.endswith('blocked')]
        revs_cols = has_eligible and 2 or None
        repos = self.env.get_repository()
        target_path = context.resource.id
        target_rev = context.resource.version
        if has_eligible:
            node = repos.get_node(target_path, target_rev)
            branch_starts = {}
            for path, rev in node.get_copy_ancestry(): 
                if path not in branch_starts:
                    branch_starts[path] = rev + 1
        rows = []
        if name.startswith('svnmerge-'):
            sources = props[name].split()
        else:
            sources = props[name].splitlines()
        for line in sources:
            path, revs = line.split(':', 1)
            spath = _path_within_scope(repos.scope, path)
            if spath is None:
                continue
            revs = revs.strip()
            deleted = False
            try:
                node = repos.get_node(spath, target_rev)
                if 'LOG_VIEW' in context.perm('source', spath):
                    row = [_get_source_link(spath, context),
                           _get_revs_link(revs_label, context, spath, revs)]
                    if has_eligible:
                        first_rev = branch_starts.get(spath)
                        eligible = set(repos._get_node_revs(spath, target_rev,
                                                            first_rev))
                        eligible -= set(Ranges(revs))
                        blocked = _get_blocked_revs(props, name, spath)
                        eligible -= set(Ranges(blocked))
                        eligible = to_ranges(eligible)
                        row.append(_get_revs_link(_('eligible'), context,
                                                  spath, eligible))
                    rows.append((False, spath, [tag.td(each) for each in row]))
                    continue
            except NoSuchNode:
                deleted = True
            revs = revs.replace(',', u',\u200b')
            rows.append((deleted, spath,
                         [tag.td('/' + spath),
                          tag.td(revs, colspan=revs_cols)]))
        if not rows:
{'fraction_non_alphanumeric': 0.10593534057255677, 'fraction_numerical': 0.005552813425468904, 'mean_word_length': 1.691183795450772, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1056376', 'n_tokens_mistral': 4536, 'n_tokens_neox': 4248, 'n_words': 1127}