[Zope3-checkins] CVS: zopeproducts/bugtracker/browser/StructuredText - ClassicDocumentClass.py:1.1 ClassicStructuredText.py:1.1 DocBookClass.py:1.1 DocumentClass.py:1.1 DocumentWithImages.py:1.1 HTMLClass.py:1.1 HTMLWithImages.py:1.1 ST.py:1.1 STDOM.py:1.1 STNG.txt:1.1 STletters.py:1.1 StructuredText.py:1.1 __init__.py:1.1 ts_regex.py:1.1

Stephan Richter srichter@cosmos.phy.tufts.edu
Thu, 24 Jul 2003 14:08:30 -0400


Update of /cvs-repository/zopeproducts/bugtracker/browser/StructuredText
In directory cvs.zope.org:/tmp/cvs-serv302/browser/StructuredText

Added Files:
	ClassicDocumentClass.py ClassicStructuredText.py 
	DocBookClass.py DocumentClass.py DocumentWithImages.py 
	HTMLClass.py HTMLWithImages.py ST.py STDOM.py STNG.txt 
	STletters.py StructuredText.py __init__.py ts_regex.py 
Log Message:
First Checkin of the Bug Tracker. A list of features is the README.txt file
and a to-do list is in TODO.txt.

The code features the use of vocabularies and vocabulary fields.

There is still a bit of work to do, but I am pretty close to make it usable
for us.


=== Added File zopeproducts/bugtracker/browser/StructuredText/ClassicDocumentClass.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

import re, ST, STDOM
from STletters import letters

from types import StringType, UnicodeType, ListType
StringTypes = (StringType, UnicodeType)

class StructuredTextExample(ST.StructuredTextParagraph):
    """Represents a section of document with literal text, as for examples"""

    def __init__(self, subs, **kw):
        t=[]; a=t.append
        for s in subs: a(s.getNodeValue())
        ST.StructuredTextParagraph.__init__(self, '\n\n'.join(t), (), **kw)

    def getColorizableTexts(self): return ()
    def setColorizableTexts(self, src): pass # never color examples

class StructuredTextBullet(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextNumbered(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextDescription(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

    def __init__(self, title, src, subs, **kw):
        ST.StructuredTextParagraph.__init__(self, src, subs, **kw)
        self._title=title

    def getColorizableTexts(self): return self._title, self._src
    def setColorizableTexts(self, src): self._title, self._src = src

    def getChildren(self):
        return (StructuredTextDescriptionTitle(self._title),
                StructuredTextDescriptionBody(self._src, self._subs))

class StructuredTextSectionTitle(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextSection(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""
    def __init__(self, src, subs=None, **kw):
        ST.StructuredTextParagraph.__init__(
            self, StructuredTextSectionTitle(src), subs, **kw)

    def getColorizableTexts(self):
        return self._src.getColorizableTexts()

    def setColorizableTexts(self,src):
        self._src.setColorizableTexts(src)

# a StructuredTextTable holds StructuredTextRows
class StructuredTextTable(ST.StructuredTextDocument):
    """
    rows is a list of lists containing tuples, which
    represent the columns/cells in each rows.
    EX
    rows = [[('row 1:column1',1)],[('row2:column1',1)]]
    """

    def __init__(self, rows, src, subs, **kw):
        ST.StructuredTextDocument.__init__(self, subs, **kw)
        self._rows = []
        for row in rows:
            if row:
                self._rows.append(StructuredTextRow(row,kw))

    def getRows(self):
        return [self._rows]

    def _getRows(self):
        return self.getRows()

    def getColorizableTexts(self):
        """
        return a tuple where each item is a column/cell's
        contents. The tuple, result, will be of this format.
        ("r1 col1", "r1=col2", "r2 col1", "r2 col2")
        """

        #result = ()
        result = []
        for row in self._rows:
            for column in row.getColumns()[0]:
                #result = result[:] + (column.getColorizableTexts(),)
                result.append(column.getColorizableTexts()[0])
        return result

    def setColorizableTexts(self,texts):
        """
        texts is going to a tuple where each item is the
        result of being mapped to the colortext function.
        Need to insert the results appropriately into the
        individual columns/cells
        """
        for row_index in range(len(self._rows)):
            for column_index in range(len(self._rows[row_index]._columns)):
                self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
                texts = texts[1:]

    def _getColorizableTexts(self):
        return self.getColorizableTexts()

    def _setColorizableTexts(self):
        return self.setColorizableTexts()

# StructuredTextRow holds StructuredTextColumns
class StructuredTextRow(ST.StructuredTextDocument):

    def __init__(self,row,kw):
        """
        row is a list of tuples, where each tuple is
        the raw text for a cell/column and the span
        of that cell/column.
        EX
        [('this is column one',1), ('this is column two',1)]
        """
        ST.StructuredTextDocument.__init__(self, [], **kw)
        self._columns = []
        for column in row:
            self._columns.append(StructuredTextColumn(column[0],column[1],kw))
    def getColumns(self):
        return [self._columns]

    def _getColumns(self):
        return [self._columns]

# this holds the raw text of a table cell
class StructuredTextColumn(ST.StructuredTextParagraph):
    """
    StructuredTextColumn is a cell/column in a table.
    This contains the actual text of a column and is
    thus a StructuredTextParagraph. A StructuredTextColumn
    also holds the span of its column
    """

    def __init__(self,text,span,kw):
        ST.StructuredTextParagraph.__init__(self, text, [], **kw)
        self._span = span

    def getSpan(self):
        return self._span

    def _getSpan(self):
        return self._span

class StructuredTextMarkup(STDOM.Element):

    def __init__(self, v, **kw):
        self._value=v
        self._attributes=kw.keys()
        for k, v in kw.items(): setattr(self, k, v)

    def getChildren(self, type=type, lt=type([])):
        v=self._value
        if type(v) is not lt: v=[v]
        return v

    def getColorizableTexts(self): return self._value,
    def setColorizableTexts(self, v): self._value=v[0]

    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, `self._value`)

class StructuredTextLiteral(StructuredTextMarkup):
    def getColorizableTexts(self): return ()
    def setColorizableTexts(self, v): pass

class StructuredTextEmphasis(StructuredTextMarkup): pass

class StructuredTextStrong(StructuredTextMarkup): pass

class StructuredTextInnerLink(StructuredTextMarkup): pass

class StructuredTextNamedLink(StructuredTextMarkup): pass

class StructuredTextUnderline(StructuredTextMarkup): pass

class StructuredTextLink(StructuredTextMarkup):
    "A simple hyperlink"

class DocumentClass:
    """
    Class instance calls [ex.=> x()] require a structured text
    structure. Doc will then parse each paragraph in the structure
    and will find the special structures within each paragraph.
    Each special structure will be stored as an instance. Special
    structures within another special structure are stored within
    the 'top' structure
    EX : '-underline this-' => would be turned into an underline
    instance. '-underline **this**' would be stored as an underline
    instance with a strong instance stored in its string
    """

    paragraph_types  = [
        'doc_bullet',
        'doc_numbered',
        'doc_description',
        'doc_header',
        'doc_table',
        ]

    text_types = [
        'doc_href1',
        'doc_href2',
        'doc_strong',
        'doc_emphasize',
        'doc_literal',
        'doc_inner_link',
        'doc_named_link',
        'doc_underline',
        ]

    def __call__(self, doc):
        if type(doc) in StringTypes:
            doc=ST.StructuredText(doc)
            doc.setSubparagraphs(self.color_paragraphs(
               doc.getSubparagraphs()))
        else:
            doc=ST.StructuredTextDocument(self.color_paragraphs(
               doc.getSubparagraphs()))
        return doc

    def parse(self, raw_string, text_type,
              type=type, sts=StringTypes, lt=type([])):

        """
        Parse accepts a raw_string, an expr to test the raw_string,
        and the raw_string's subparagraphs.

        Parse will continue to search through raw_string until
        all instances of expr in raw_string are found.

        If no instances of expr are found, raw_string is returned.
        Otherwise a list of substrings and instances is returned
        """

        tmp = []    # the list to be returned if raw_string is split
        append=tmp.append

        if type(text_type) in sts: text_type=getattr(self, text_type)

        while 1:
            t = text_type(raw_string)
            if not t: break
            #an instance of expr was found
            t, start, end    = t

            if start: append(raw_string[0:start])

            tt=type(t)
            if tt in sts:
                # if we get a string back, add it to text to be parsed
                raw_string = t+raw_string[end:len(raw_string)]
            else:
                if tt is lt:
                # is we get a list, append it's elements
                    tmp[len(tmp):]=t
                else:
                    # normal case, an object
                    append(t)
                raw_string = raw_string[end:len(raw_string)]

        if not tmp: return raw_string # nothing found

        if raw_string: append(raw_string)
        elif len(tmp)==1: return tmp[0]

        return tmp


    def color_text(self, str, types=None):
        """Search the paragraph for each special structure
        """
        if types is None: types=self.text_types

        for text_type in types:

            if type(str) in StringTypes:
                str = self.parse(str, text_type)
            elif type(str) is ListType:
                r=[]; a=r.append
                for s in str:
                    if type(s) in StringTypes:
                        s=self.parse(s, text_type)
                        if type(s) is ListType: r[len(r):]=s
                        else: a(s)
                    else:
                        s.setColorizableTexts(
                           map(self.color_text,
                               s.getColorizableTexts()
                               ))
                        a(s)
                str=r
            else:
                r=[]; a=r.append; color=self.color_text
                for s in str.getColorizableTexts():
                    color(s, (text_type,))
                    a(s)

                str.setColorizableTexts(r)

        return str

    def color_paragraphs(self, raw_paragraphs,
                           type=type, sequence_types=(type([]), type(())),
                           sts=StringTypes):
        result=[]
        for paragraph in raw_paragraphs:

            if paragraph.getNodeName() != 'StructuredTextParagraph':
                result.append(paragraph)
                continue

            for pt in self.paragraph_types:
                if type(pt) in sts:
                    # grab the corresponding function
                    pt=getattr(self, pt)
                # evaluate the paragraph
                r=pt(paragraph)
                if r:
                    if type(r) not in sequence_types:
                        r=r,
                    new_paragraphs=r
                    for paragraph in new_paragraphs:
                        paragraph.setSubparagraphs(self.color_paragraphs(paragraph.getSubparagraphs()))
                    break
            else:
                new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
                                                             self.color_paragraphs(paragraph.getSubparagraphs()),
                                                             indent=paragraph.indent),
            # color the inline StructuredText types
            # for each StructuredTextParagraph
            for paragraph in new_paragraphs:
                paragraph.setColorizableTexts(
                   map(self.color_text,
                       paragraph.getColorizableTexts()
                       ))
                result.append(paragraph)

        return result

    def doc_table(self,paragraph, expr = re.compile('(\s*)([||]+)').match):
        #print "paragraph=>", type(paragraph), paragraph, paragraph._src
        text    = paragraph.getColorizableTexts()[0]
        m       = expr(text)

        if not (m):
            return None
        rows = []

        # initial split
        for row in text.split("\n"):
            rows.append(row)

        # clean up the rows
        for index in range(len(rows)):
            tmp = []
            rows[index] = rows[index].strip()
            l = len(rows[index])-2
            result = rows[index][:l].split("||")
            for text in result:
                if text:
                    tmp.append(text)
                    tmp.append('')
                else:
                    tmp.append(text)
            rows[index] = tmp
        # remove trailing '''s
        for index in range(len(rows)):
            l = len(rows[index])-1
            rows[index] = rows[index][:l]

        result = []
        for row in rows:
            cspan   = 0
            tmp     = []
            for item in row:
                if item:
                    tmp.append((item,cspan))
                    cspan = 0
                else:
                    cspan = cspan + 1
            result.append(tmp)

        subs = paragraph.getSubparagraphs()
#        indent=paragraph.indent
        return StructuredTextTable(result,text,subs,indent=paragraph.indent)

    def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match):
        top=paragraph.getColorizableTexts()[0]
        m=expr(top)

        if not m:
            return None

        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[StructuredTextExample(subs)]
            top=top[:-1]
        return StructuredTextBullet(top[m.span()[1]:], subs,
                                     indent=paragraph.indent,
                                     bullet=top[:m.span()[1]]
                                     )

    def doc_numbered(
        self, paragraph,
        expr = re.compile('(\s*[%s]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)' % letters).match):

        # This is the old expression. It had a nasty habit
        # of grabbing paragraphs that began with a single
        # letter word even if there was no following period.

        #expr = re.compile('\s*'
        #                   '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
        #                   '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
        #                   '\s+').match):

        top=paragraph.getColorizableTexts()[0]
        m=expr(top)
        if not m: return None
        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[StructuredTextExample(subs)]
            top=top[:-1]
        return StructuredTextNumbered(top[m.span()[1]:], subs,
                                        indent=paragraph.indent,
                                        number=top[:m.span()[1]])

    def doc_description(
        self, paragraph,
        delim = re.compile('\s+--\s+').search,
        nb=re.compile(r'[^\000- ]').search,
        ):

        top=paragraph.getColorizableTexts()[0]
        d=delim(top)
        if not d: return None
        start, end = d.span()
        title=top[:start]
        if title.find('\n') >= 0: return None
        if not nb(title): return None
        d=top[start:end]
        top=top[end:]

        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[StructuredTextExample(subs)]
            top=top[:-1]

        return StructuredTextDescription(
           title, top, subs,
           indent=paragraph.indent,
           delim=d)

    def doc_header(self, paragraph,
                    expr    = re.compile('[ %s0-9.:/,-_*<>\?\'\"]+' % letters).match
                    ):
        subs=paragraph.getSubparagraphs()
        if not subs: return None
        top=paragraph.getColorizableTexts()[0]
        if not top.strip(): return None
        if top[-2:]=='::':
            subs=StructuredTextExample(subs)
            if top.strip()=='::': return subs
            return ST.StructuredTextParagraph(top[:-1],
                                              [subs],
                                              indent=paragraph.indent,
                                              level=paragraph.level)

        if top.find('\n') >= 0: return None
        return StructuredTextSection(top, subs, indent=paragraph.indent, level=paragraph.level)

    def doc_literal(
        self, s,
        expr=re.compile(
          "(?:\s|^)'"                                               # open
          "([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
          "'(?:\s|[,.;:!?]|$)"                                      # close
          ).search):

        r=expr(s)
        if r:
            start, end = r.span(1)
            return (StructuredTextLiteral(s[start:end]), start-1, end+1)
        else:
            return None

    def doc_emphasize(
        self, s,
        expr = re.compile('\s*\*([ \n%s0-9.:/;,\'\"\?\=\-\>\<\(\)]+)\*(?!\*|-)' % letters).search
        ):

        r=expr(s)
        if r:
            start, end = r.span(1)
            return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
        else:
            return None

    def doc_inner_link(self,
                       s,
                       expr1 = re.compile("\.\.\s*").search,
                       expr2 = re.compile("\[[%s0-9]+\]" % letters).search):

        # make sure we dont grab a named link
        if expr2(s) and expr1(s):
            start1,end1 = expr1(s).span()
            start2,end2 = expr2(s).span()
            if end1 == start2:
                # uh-oh, looks like a named link
                return None
            else:
                # the .. is somewhere else, ignore it
                return (StructuredTextInnerLink(s[start2+1,end2-1]),start2,end2)
            return None
        elif expr2(s) and not expr1(s):
            start,end = expr2(s).span()
            return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
        return None

    def doc_named_link(self,
                       s,
                       expr=re.compile(r"(\.\.\s)(\[[%s0-9]+\])" % letters).search):

        result = expr(s)
        if result:
            start,end   = result.span(2)
            str = s[start+1:end-1]
            st,en       = result.span()
            return (StructuredTextNamedLink(str),st,en)
        return None

    def doc_underline(self,
                      s,
                      expr=re.compile("_([%s0-9\s\.,\?\/]+)_" % letters).search):


        result = expr(s)
        if result:
            start,end = result.span(1)
            st,e = result.span()
            return (StructuredTextUnderline(s[start:end]),st,e)
        else:
            return None

    def doc_strong(self,
                   s,
        expr = re.compile('\s*\*\*([ \n%s0-9.:/;\-,!\?\'\"]+)\*\*' % letters).search
        ):

        r=expr(s)
        if r:
            start, end = r.span(1)
            return (StructuredTextStrong(s[start:end]), start-2, end+2)
        else:
            return None



    def doc_href1(self, s,
        expr=re.compile("(\"[ %s0-9\n\-\.\,\;\(\)\/\:\/\*\']+\")(:)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)([,]*\s*)" % letters).search
        ):
        return self.doc_href(s, expr)

    def  doc_href2(self, s,
        expr=re.compile('(\"[ %s0-9\n\-\.\:\;\(\)\/\*\']+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)(\s*)' % letters).search
        ):
        return self.doc_href(s, expr)

    def doc_href(self, s, expr, punctuation = re.compile("[\,\.\?\!\;]+").match):

        r=expr(s)

        if r:
            # need to grab the href part and the
            # beginning part

            start,e = r.span(1)
            name    = s[start:e]
            name    = name.replace('"','',2)
            #start   = start + 1
            st,end   = r.span(3)
            if punctuation(s[end-1:end]):
                end = end -1
            link    = s[st:end]
            #end     = end - 1

            # name is the href title, link is the target
            # of the href
            return (StructuredTextLink(name, href=link),
                    start, end)

            #return (StructuredTextLink(s[start:end], href=s[start:end]),
            #        start, end)
        else:
            return None


=== Added File zopeproducts/bugtracker/browser/StructuredText/ClassicStructuredText.py ===
#! /usr/bin/env python -- # -*- python -*-
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Structured Text Manipulation

Parse a structured text string into a form that can be used with
structured formats, like html.

Structured text is text that uses indentation and simple
symbology to indicate the structure of a document.

A structured string consists of a sequence of paragraphs separated by
one or more blank lines.  Each paragraph has a level which is defined
as the minimum indentation of the paragraph.  A paragraph is a
sub-paragraph of another paragraph if the other paragraph is the last
preceding paragraph that has a lower level.

Special symbology is used to indicate special constructs:

- A single-line paragraph whose immediately succeeding paragraphs are lower
  level is treated as a header.

- A paragraph that begins with a '-', '*', or 'o' is treated as an
  unordered list (bullet) element.

- A paragraph that begins with a sequence of digits followed by a
  white-space character is treated as an ordered list element.

- A paragraph that begins with a sequence of sequences, where each
  sequence is a sequence of digits or a sequence of letters followed
  by a period, is treated as an ordered list element.

- A paragraph with a first line that contains some text, followed by
  some white-space and '--' is treated as
  a descriptive list element. The leading text is treated as the
  element title.

- Sub-paragraphs of a paragraph that ends in the word 'example' or the
  word 'examples', or '::' is treated as example code and is output as is.

- Text enclosed single quotes (with white-space to the left of the
  first quote and whitespace or punctuation to the right of the second quote)
  is treated as example code.

- Text surrounded by '*' characters (with white-space to the left of the
  first '*' and whitespace or punctuation to the right of the second '*')
  is emphasized.

- Text surrounded by '**' characters (with white-space to the left of the
  first '**' and whitespace or punctuation to the right of the second '**')
  is made strong.

- Text surrounded by '_' underscore characters (with whitespace to the left
  and whitespace or punctuation to the right) is made underlined.

- Text encloded by double quotes followed by a colon, a URL, and concluded
  by punctuation plus white space, *or* just white space, is treated as a
  hyper link. For example:

    "Zope":http://www.zope.org/ is ...

  Is interpreted as '<a href="http://www.zope.org/">Zope</a> is ....'
  Note: This works for relative as well as absolute URLs.

- Text enclosed by double quotes followed by a comma, one or more spaces,
  an absolute URL and concluded by punctuation plus white space, or just
  white space, is treated as a hyper link. For example:

    "mail me", mailto:amos@digicool.com.

  Is interpreted as '<a href="mailto:amos@digicool.com">mail me</a>.'

- Text enclosed in brackets which consists only of letters, digits,
  underscores and dashes is treated as hyper links within the document.
  For example:

    As demonstrated by Smith [12] this technique is quite effective.

  Is interpreted as '... by Smith <a href="#12">[12]</a> this ...'. Together
  with the next rule this allows easy coding of references or end notes.

- Text enclosed in brackets which is preceded by the start of a line, two
  periods and a space is treated as a named link. For example:

    .. [12] "Effective Techniques" Smith, Joe ...

  Is interpreted as '<a name="12">[12]</a> "Effective Techniques" ...'.
  Together with the previous rule this allows easy coding of references or
  end notes.


- A paragraph that has blocks of text enclosed in '||' is treated as a
  table. The text blocks correspond to table cells and table rows are
  denoted by newlines. By default the cells are center aligned. A cell
  can span more than one column by preceding a block of text with an
  equivalent number of cell separators '||'. Newlines and '|' cannot
  be a part of the cell text. For example:

      |||| **Ingredients** ||
      || *Name* || *Amount* ||
      ||Spam||10||
      ||Eggs||3||

  is interpreted as::

    <TABLE BORDER=1 CELLPADDING=2>
     <TR>
      <TD ALIGN=CENTER COLSPAN=2> <strong>Ingredients</strong> </TD>
     </TR>
     <TR>
      <TD ALIGN=CENTER COLSPAN=1> <em>Name</em> </TD>
      <TD ALIGN=CENTER COLSPAN=1> <em>Amount</em> </TD>
     </TR>
     <TR>
      <TD ALIGN=CENTER COLSPAN=1>Spam</TD>
      <TD ALIGN=CENTER COLSPAN=1>10</TD>
     </TR>
     <TR>
      <TD ALIGN=CENTER COLSPAN=1>Eggs</TD>
      <TD ALIGN=CENTER COLSPAN=1>3</TD>
     </TR>
    </TABLE>

'''

import ts_regex
import string, re


def untabify(aString,
             indent_tab=ts_regex.compile('\(\n\|^\)\( *\)\t').search_group,
             ):
    '''\
    Convert indentation tabs to spaces.
    '''
    result=''
    rest=aString
    while 1:
        ts_results = indent_tab(rest, (1,2))
        if ts_results:
            start, grps = ts_results
            lnl=len(grps[0])
            indent=len(grps[1])
            result=result+rest[:start]
            rest="\n%s%s" % (' ' * ((indent/8+1)*8),
                             rest[start+indent+1+lnl:])
        else:
            return result+rest

def indent(aString, indent=2):
    """Indent a string the given number of spaces"""
    r=untabify(aString).split('\n')
    if not r: return ''
    if not r[-1]: del r[-1]
    tab=' '*indent
    return "%s%s\n" % (tab,('\n'+tab).join(r))

def reindent(aString, indent=2, already_untabified=0):
    "reindent a block of text, so that the minimum indent is as given"

    if not already_untabified: aString=untabify(aString)

    l=indent_level(aString)[0]
    if indent==l: return aString

    r=[]

    append=r.append

    if indent > l:
        tab=' ' * (indent-l)
        for s in aString.split('\n'): append(tab+s)
    else:
        l=l-indent
        for s in aString.split('\n'): append(s[l:])

    return '\n'.join(r)

def indent_level(aString,
                 indent_space=ts_regex.compile('\n\( *\)').search_group,
                 ):
    '''\
    Find the minimum indentation for a string, not counting blank lines.
    '''
    start=0
    text='\n'+aString
    indent=l=len(text)
    while 1:

        ts_results = indent_space(text, (1,2), start)
        if ts_results:
            start, grps = ts_results
            i=len(grps[0])
            start=start+i+1
            if start < l and text[start] != '\n':       # Skip blank lines
                if not i: return (0,aString)
                if i < indent: indent = i
        else:
            return (indent,aString)

def paragraphs(list,start):
    l=len(list)
    level=list[start][0]
    i=start+1
    while i < l and list[i][0] > level: i=i+1
    return i-1-start

def structure(list):
    if not list: return []
    i=0
    l=len(list)
    r=[]
    while i < l:
        sublen=paragraphs(list,i)
        i=i+1
        r.append((list[i-1][1],structure(list[i:i+sublen])))
        i=i+sublen
    return r


class Table:
    CELL='  <TD ALIGN=CENTER COLSPAN=%i>%s</TD>\n'
    ROW=' <TR>\n%s </TR>\n'
    TABLE='\n<TABLE BORDER=1 CELLPADDING=2>\n%s</TABLE>'
    def create(self,aPar,
        td_reg=re.compile(r'[ \t\n]*\|\|([^\0|]*)')
        ):
        '''parses a table and returns nested list representing the
        table'''
        self.table=[]
        text=filter(None,aPar.split('\n'))
        for line in text:
            row=[]
            while 1:
                mo =  td_reg.match(line)
                if not mo: return 0
                pos = mo.end(1)
                row.append(mo.group(1))
                if pos==len(line):break
                line=line[pos:]
            self.table.append(row)
        return 1

    def html(self):
        '''Creates an HTML representation of table'''
        htmltable=[]
        for row in self.table:
            htmlrow=[]
            colspan=1
            for cell in row:
                if cell=='':
                    colspan=colspan+1
                    continue
                else:
                    htmlrow.append(self.CELL%(colspan,cell))
                    colspan=1
            htmltable.append(self.ROW % ''.join(htmlrow))
        return self.TABLE % ''.join(htmltable)

table=Table()

class StructuredText:

    """Model text as structured collection of paragraphs.

    Structure is implied by the indentation level.

    This class is intended as a base classes that do actual text
    output formatting.
    """

    def __init__(self, aStructuredString, level=0,
                 paragraph_divider=ts_regex.compile('\(\r?\n *\)+\r?\n'),
                 ):
        '''Convert a structured text string into a structured text object.

        Aguments:

          aStructuredString -- The string to be parsed.
          level -- The level of top level headings to be created.
        '''


        pat = ' \"([%s0-9-_,./?=@~&]*)\":' % string.letters+ \
              '([-:%s0-9_,./?=@#~&]*?)' % string.letters + \
              '([.:?;] )'

        p_reg = re.compile(pat,re.M)

        aStructuredString = p_reg.sub(r'<a href="\2">\1</a>\3 ' , aStructuredString)

        pat = ' \"([%s0-9-_,./?=@~&]*)\", ' % string.letters+ \
              '([-:%s0-9_,./?=@#~&]*?)' % string.letters + \
              '([.:?;] )'

        p_reg = re.compile(pat,re.M)

        aStructuredString = p_reg.sub(r'<a href="\2">\1</a>\3 ' , aStructuredString)


        protoless = aStructuredString.find('<a href=":')
        if protoless != -1:
            aStructuredString = re.sub('<a href=":', '<a href="',
                                     aStructuredString)

        self.level=level
        paragraphs=ts_regex.split(untabify(aStructuredString),
                                  paragraph_divider)
        paragraphs=map(indent_level,paragraphs)

        self.structure=structure(paragraphs)


    def __str__(self):
        return str(self.structure)


ctag_prefix=r'([\x00- \\(]|^)'
ctag_suffix=r'([\x00- ,.:;!?\\)]|$)'
ctag_middle=r'[%s]([^\x00- %s][^%s]*[^\x00- %s]|[^%s])[%s]'
ctag_middl2=r'[%s][%s]([^\x00- %s][^%s]*[^\x00- %s]|[^%s])[%s][%s]'

def ctag(s,
         em=re.compile(
             ctag_prefix+(ctag_middle % (("*",)*6) )+ctag_suffix),
         strong=re.compile(
             ctag_prefix+(ctag_middl2 % (("*",)*8))+ctag_suffix),
         under=re.compile(
             ctag_prefix+(ctag_middle % (("_",)*6) )+ctag_suffix),
         code=re.compile(
             ctag_prefix+(ctag_middle % (("\'",)*6))+ctag_suffix),
         ):
    if s is None: s=''
    s=strong.sub(r'\1<strong>\2</strong>\3',s)
    s=under.sub( r'\1<u>\2</u>\3',s)
    s=code.sub(  r'\1<code>\2</code>\3',s)
    s=em.sub(    r'\1<em>\2</em>\3',s)
    return s

class HTML(StructuredText):

    '''\
    An HTML structured text formatter.
    '''\

    def __str__(self,
                extra_dl=re.compile("</dl>\n<dl>"),
                extra_ul=re.compile("</ul>\n<ul>"),
                extra_ol=re.compile("</ol>\n<ol>"),
                ):
        '''\
        Return an HTML string representation of the structured text data.

        '''
        s=self._str(self.structure,self.level)
        s=extra_dl.sub('\n',s)
        s=extra_ul.sub('\n',s)
        s=extra_ol.sub('\n',s)
        return s

    def ul(self, before, p, after):
        if p: p="<p>%s</p>" % ctag(p).strip()
        return ('%s<ul><li>%s\n%s\n</li></ul>\n'
                % (before,p,after))

    def ol(self, before, p, after):
        if p: p="<p>%s</p>" % ctag(p).strip()
        return ('%s<ol><li>%s\n%s\n</li></ol>\n'
                % (before,p,after))

    def dl(self, before, t, d, after):
        return ('%s<dl><dt>%s</dt><dd><p>%s</p>\n%s\n</dd></dl>\n'
                % (before,ctag(t),ctag(d),after))

    def head(self, before, t, level, d):
        if level > 0 and level < 6:
            return ('%s<h%d>%s</h%d>\n%s\n'
                    % (before,level,ctag(t).strip(),level,d))

        t="<p><strong>%s</strong></p>" % ctag(t).strip()
        return ('%s<dl><dt>%s\n</dt><dd>%s\n</dd></dl>\n'
                % (before,t,d))

    def normal(self,before,p,after):
        return '%s<p>%s</p>\n%s\n' % (before,ctag(p),after)

    def pre(self,structure,tagged=0):
        if not structure: return ''
        if tagged:
            r=''
        else:
            r='<PRE>\n'
        for s in structure:
            r="%s%s\n\n%s" % (r,html_quote(s[0]),self.pre(s[1],1))
        if not tagged: r=r+'</PRE>\n'
        return r

    def table(self,before,table,after):
        return '%s<p>%s</p>\n%s\n' % (before,ctag(table),after)

    def _str(self,structure,level,
             # Static
             bullet=ts_regex.compile('[ \t\n]*[o*-][ \t\n]+\([^\0]*\)'
                                     ).match_group,
             example=ts_regex.compile('[\0- ]examples?:[\0- ]*$'
                                      ).search,
             dl=ts_regex.compile('\([^\n]+\)[ \t]+--[ \t\n]+\([^\0]*\)'
                                 ).match_group,
             nl=ts_regex.compile('\n').search,
             ol=ts_regex.compile(
                 '[ \t]*\(\([0-9]+\|[%s]+\)[.)]\)+[ \t\n]+\([^\0]*\|$\)' % string.letters
                 ).match_group,
             olp=ts_regex.compile('[ \t]*([0-9]+)[ \t\n]+\([^\0]*\|$\)'
                                  ).match_group,
             ):
        r=''
        for s in structure:

            ts_results = bullet(s[0], (1,))
            if ts_results:
                p = ts_results[1]
                if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
                else: ps=self._str(s[1],level)
                r=self.ul(r,p,ps)
                continue
            ts_results = ol(s[0], (3,))
            if ts_results:
                p = ts_results[1]
                if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
                else: ps=self._str(s[1],level)
                r=self.ol(r,p,ps)
                continue
            ts_results = olp(s[0], (1,))
            if ts_results:
                p = ts_results[1]
                if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
                else: ps=self._str(s[1],level)
                r=self.ol(r,p,ps)
                continue
            ts_results = dl(s[0], (1,2))
            if ts_results:
                t,d = ts_results[1]
                r=self.dl(r,t,d,self._str(s[1],level))
                continue
            if example(s[0]) >= 0 and s[1]:
                # Introduce an example, using pre tags:
                r=self.normal(r,s[0],self.pre(s[1]))
                continue
            if s[0][-2:]=='::' and s[1]:
                # Introduce an example, using pre tags:
                r=self.normal(r,s[0][:-1],self.pre(s[1]))
                continue
            if table.create(s[0]):
                ## table support.
                r=self.table(r,table.html(),self._str(s[1],level))
                continue
            else:

                if nl(s[0]) < 0 and s[1] and s[0][-1:] != ':':
                    # Treat as a heading
                    t=s[0]
                    r=self.head(r,t,level,
                                self._str(s[1],level and level+1))
                else:
                    r=self.normal(r,s[0],self._str(s[1],level))
        return r


def html_quote(v,
               character_entities=(
                       (re.compile('&'), '&amp;'),
                       (re.compile("<"), '&lt;' ),
                       (re.compile(">"), '&gt;' ),
                       (re.compile('"'), '&quot;')
                       )): #"
    text=str(v)
    for re,name in character_entities:
        text=re.sub(name,text)
    return text

def html_with_references(text, level=1):
    text = re.sub(
        r'[\0\n]\.\. \[([0-9_%s-]+)\]' % string.letters,
        r'\n  <a name="\1">[\1]</a>',
        text)

    text = re.sub(
        r'([\x00- ,])\[(?P<ref>[0-9_%s-]+)\]([\x00- ,.:])'   % string.letters,
        r'\1<a href="#\2">[\2]</a>\3',
        text)

    text = re.sub(
        r'([\0- ,])\[([^]]+)\.html\]([\0- ,.:])',
        r'\1<a href="\2.html">[\2]</a>\3',
        text)

    return HTML(text,level=level)


def main():
    import sys, getopt

    opts,args=getopt.getopt(sys.argv[1:],'twl')

    if args:
        [infile]=args
        s=open(infile,'r').read()
    else:
        s=sys.stdin.read()

    if opts:

        if filter(lambda o: o[0]=='-w', opts):
            print 'Content-Type: text/html\n'

        if filter(lambda o: o[0]=='-l', opts):
            import locale
            locale.setlocale(locale.LC_ALL,"")

        if s[:2]=='#!':
            s=re.sub('^#![^\n]+','',s)

        mo = re.compile('([\0-\n]*\n)').match(s)
        if mo is not None:
            s = s[len(mo.group(0)) :]

        s=str(html_with_references(s))
        if s[:4]=='<h1>':
            t=s[4: s.find('</h1>')]
            s='''<html><head><title>%s</title>
            </head><body>
            %s
            </body></html>
            ''' % (t,s)
        print s
    else:
        print html_with_references(s)

if __name__=="__main__": main()


=== Added File zopeproducts/bugtracker/browser/StructuredText/DocBookClass.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################


class DocBookClass:

    element_types={
       '#text': '_text',
       'StructuredTextDocument': 'document',
       'StructuredTextParagraph': 'paragraph',
       'StructuredTextExample': 'example',
       'StructuredTextBullet': 'bullet',
       'StructuredTextNumbered': 'numbered',
       'StructuredTextDescription': 'description',
       'StructuredTextDescriptionTitle': 'descriptionTitle',
       'StructuredTextDescriptionBody': 'descriptionBody',
       'StructuredTextSection': 'section',
       'StructuredTextSectionTitle': 'sectionTitle',
       'StructuredTextLiteral': 'literal',
       'StructuredTextEmphasis': 'emphasis',
       'StructuredTextStrong': 'strong',
       'StructuredTextLink': 'link',
       'StructuredTextXref': 'xref',
       'StructuredTextSGML': 'sgml',
       }

    def dispatch(self, doc, level, output):
        getattr(self, self.element_types[doc.getNodeName()])(doc, level, output)

    def __call__(self, doc, level=1):
        r=[]
        self.dispatch(doc, level-1, r.append)
        return ''.join(r)

    def _text(self, doc, level, output):
        if doc.getNodeName() == 'StructuredTextLiteral':
            output(doc.getNodeValue())
        else:
            output(doc.getNodeValue().lstrip())

    def document(self, doc, level, output):
        output('<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">\n')
        output('<book>\n')
        children=doc.getChildNodes()
        if (children and
            children[0].getNodeName() == 'StructuredTextSection'):
            output('<title>%s</title>' % children[0].getChildNodes()[0].getNodeValue())
        for c in children:
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</book>\n')

    def section(self, doc, level, output):
        output('\n<section>\n')
        children=doc.getChildNodes()
        for c in children:
            getattr(self, self.element_types[c.getNodeName()])(c, level+1, output)
        output('\n</section>\n')

    def sectionTitle(self, doc, level, output):
        output('<title>')
        for c in doc.getChildNodes():
            try:
                getattr(self, self.element_types[c.getNodeName()])(c, level, output)
            except:
                print "failed", c.getNodeName(), c
        output('</title>\n')

    def description(self, doc, level, output):
        p=doc.getPreviousSibling()
        if p is None or  p.getNodeName() is not doc.getNodeName():
            output('<variablelist>\n')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        n=doc.getNextSibling()
        if n is None or n.getNodeName() is not doc.getNodeName():
            output('</variablelist>\n')

    def descriptionTitle(self, doc, level, output):
        output('<varlistentry><term>\n')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</term>\n')

    def descriptionBody(self, doc, level, output):
        output('<listitem><para>\n')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</para></listitem>\n')
        output('</varlistentry>\n')

    def bullet(self, doc, level, output):
        p=doc.getPreviousSibling()
        if p is None or p.getNodeName() is not doc.getNodeName():
            output('<itemizedlist>\n')
        output('<listitem><para>\n')

        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        n=doc.getNextSibling()
        output('</para></listitem>\n')
        if n is None or n.getNodeName() is not doc.getNodeName():
            output('</itemizedlist>\n')

    def numbered(self, doc, level, output):
        p=doc.getPreviousSibling()
        if p is None or p.getNodeName() is not doc.getNodeName():
            output('<orderedlist>\n')
        output('<listitem><para>\n')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        n=doc.getNextSibling()
        output('</para></listitem>\n')
        if n is None or n.getNodeName() is not doc.getNodeName():
            output('</orderedlist>\n')

    def example(self, doc, level, output):
        i=0
        for c in doc.getChildNodes():
            if i==0:
                output('<programlisting>\n<![CDATA[\n')
                ##
                ## eek.  A ']]>' in your body will break this...
                ##
                output(prestrip(c.getNodeValue()))
                output('\n]]></programlisting>\n')
            else:
                getattr(self, self.element_types[c.getNodeName()])(
                   c, level, output)

    def paragraph(self, doc, level, output):
        output('<para>\n\n')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(
               c, level, output)
        output('</para>\n\n')

    def link(self, doc, level, output):
        output('<ulink url="%s">' % doc.href)
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</ulink>')

    def emphasis(self, doc, level, output):
        output('<emphasis>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</emphasis> ')

    def literal(self, doc, level, output):
        output('<literal>')
        for c in doc.getChildNodes():
            output(c.getNodeValue())
        output('</literal>')

    def strong(self, doc, level, output):
        output('<emphasis>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</emphasis>')

    def xref(self, doc, level, output):
        output('<xref linkend="%s"/>' % doc.getNodeValue())

    def sgml(self, doc, level, output):
        output(doc.getNodeValue())


def prestrip(v):
    v=v.replace( '\r\n', '\n')
    v=v.replace( '\r', '\n')
    v=v.replace( '\t', '        ')
    lines=v.split('\n')
    indent=len(lines[0])
    for line in lines:
        if not len(line): continue
        i=len(line)-len(line.lstrip())
        if i < indent:
            indent=i
    nlines=[]
    for line in lines:
        nlines.append(line[indent:])
    return '\n'.join(nlines, '\n')


class DocBookChapter(DocBookClass):

    def document(self, doc, level, output):
        output('<chapter>\n')
        children=doc.getChildNodes()
        if (children and
            children[0].getNodeName() == 'StructuredTextSection'):
            output('<title>%s</title>' % children[0].getChildNodes()[0].getNodeValue())
        for c in children[0].getChildNodes()[1:]:
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</chapter>\n')

ets = DocBookClass.element_types
ets.update({'StructuredTextImage': 'image'})

class DocBookChapterWithFigures(DocBookChapter):

    element_types = ets

    def image(self, doc, level, output):
        if hasattr(doc, 'key'):
            output('<figure id="%s"><title>%s</title>\n' % (doc.key, doc.getNodeValue()) )
        else:
            output('<figure><title>%s</title>\n' % doc.getNodeValue())
##        for c in doc.getChildNodes():
##            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('<graphic fileref="%s"></graphic>\n</figure>\n' % doc.href)

class DocBookArticle(DocBookClass):

    def document(self, doc, level, output):
        output('<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN">\n')
        output('<article>\n')
        children=doc.getChildNodes()
        if (children and
            children[0].getNodeName() == 'StructuredTextSection'):
            output('<articleinfo>\n<title>%s</title>\n</articleinfo>\n' %
                   children[0].getChildNodes()[0].getNodeValue())
        for c in children:
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</article>\n')


class DocBookBook:

    def __init__(self, title=''):
        self.title = title
        self.chapters = []

    def addChapter(self, chapter):
        self.chapters.append(chapter)

    def read(self):
        out = '<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">\n<book>\n'
        out = out + '<title>%s</title>\n' % self.title
        for chapter in self.chapters:
            out = out + chapter + '\n</book>\n'

        return out

    def __str__(self):
        return self.read()


=== Added File zopeproducts/bugtracker/browser/StructuredText/DocumentClass.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

import re, ST, STDOM
from STletters import letters, digits, literal_punc, under_punc,\
     strongem_punc, phrase_delimiters,dbl_quoted_punc

from types import StringType, UnicodeType, ListType
StringTypes = (StringType, UnicodeType)

def flatten(obj, append):
    if obj.getNodeType()==STDOM.TEXT_NODE:
        append(obj.getNodeValue())
    else:
        for child in obj.getChildNodes():
            flatten(child, append)


class StructuredTextExample(ST.StructuredTextParagraph):
    """Represents a section of document with literal text, as for examples"""

    def __init__(self, subs, **kw):
        t=[]
        a=t.append
        for s in subs:
            flatten(s, a)
        ST.StructuredTextParagraph.__init__(self, '\n\n'.join(t), (), **kw)

    def getColorizableTexts(self): return ()
    def setColorizableTexts(self, src): pass # never color examples

class StructuredTextBullet(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextNumbered(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextDescription(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

    def __init__(self, title, src, subs, **kw):
        ST.StructuredTextParagraph.__init__(self, src, subs, **kw)
        self._title=title

    def getColorizableTexts(self): return self._title, self._src
    def setColorizableTexts(self, src): self._title, self._src = src

    def getChildren(self):
        return (StructuredTextDescriptionTitle(self._title),
                StructuredTextDescriptionBody(self._src, self._subs))

class StructuredTextSectionTitle(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""

class StructuredTextSection(ST.StructuredTextParagraph):
    """Represents a section of a document with a title and a body"""
    def __init__(self, src, subs=None, **kw):
        ST.StructuredTextParagraph.__init__(
            self, StructuredTextSectionTitle(src), subs, **kw)

    def getColorizableTexts(self):
        return self._src.getColorizableTexts()

    def setColorizableTexts(self,src):
        self._src.setColorizableTexts(src)

# a StructuredTextTable holds StructuredTextRows
class StructuredTextTable(ST.StructuredTextParagraph):
    """
    rows is a list of lists containing tuples, which
    represent the columns/cells in each rows.
    EX
    rows = [[('row 1:column1',1)],[('row2:column1',1)]]
    """

    def __init__(self, rows, src, subs, **kw):
        ST.StructuredTextParagraph.__init__(self, subs, **kw)
        self._rows = []
        for row in rows:
            if row:
                self._rows.append(StructuredTextRow(row,kw))

    def getRows(self):
        return [self._rows]

    def _getRows(self):
        return self.getRows()

    def getColumns(self):
        result = []
        for row in self._rows:
            result.append(row.getColumns())
        return result

    def _getColumns(self):
        return self.getColumns()

    def setColumns(self,columns):
        for index in range(len(self._rows)):
            self._rows[index].setColumns(columns[index])

    def _setColumns(self,columns):
        return self.setColumns(columns)

    def getColorizableTexts(self):
        """
        return a tuple where each item is a column/cell's
        contents. The tuple, result, will be of this format.
        ("r1 col1", "r1=col2", "r2 col1", "r2 col2")
        """

        result = []
        for row in self._rows:
            for column in row.getColumns()[0]:
                result.append(column.getColorizableTexts()[0])
        return result

    def setColorizableTexts(self,texts):
        """
        texts is going to a tuple where each item is the
        result of being mapped to the colortext function.
        Need to insert the results appropriately into the
        individual columns/cells
        """
        for row_index in range(len(self._rows)):
            for column_index in range(len(self._rows[row_index]._columns)):
                self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
                texts = texts[1:]

    def _getColorizableTexts(self):
        return self.getColorizableTexts()

    def _setColorizableTexts(self, texts):
        return self.setColorizableTexts(texts)

# StructuredTextRow holds StructuredTextColumns
class StructuredTextRow(ST.StructuredTextParagraph):

    def __init__(self,row,kw):
        """
        row is a list of tuples, where each tuple is
        the raw text for a cell/column and the span
        of that cell/column.
        EX
        [('this is column one',1), ('this is column two',1)]
        """

        ST.StructuredTextParagraph.__init__(self, [], **kw)

        self._columns = []
        for column in row:
            self._columns.append(StructuredTextColumn(column[0],
                                                      column[1],
                                                      column[2],
                                                      column[3],
                                                      column[4],
                                                      kw))

    def getColumns(self):
        return [self._columns]

    def _getColumns(self):
        return [self._columns]

    def setColumns(self,columns):
        self._columns = columns

    def _setColumns(self,columns):
        return self.setColumns(columns)

# this holds the text of a table cell
class StructuredTextColumn(ST.StructuredTextParagraph):
    """
    StructuredTextColumn is a cell/column in a table.
    A cell can hold multiple paragraphs. The cell
    is either classified as a StructuredTextTableHeader
    or StructuredTextTableData.
    """

    def __init__(self,text,span,align,valign,typ,kw):
        ST.StructuredTextParagraph.__init__(self, text, [], **kw)
        self._span = span
        self._align = align
        self._valign = valign
        self._type = typ

    def getSpan(self):
        return self._span

    def _getSpan(self):
        return self._span

    def getAlign(self):
        return self._align

    def _getAlign(self):
        return self.getAlign()

    def getValign(self):
        return self._valign

    def _getValign(self):
        return self.getValign()

    def getType(self):
        return self._type

    def _getType(self):
        return self.getType()

class StructuredTextTableHeader(ST.StructuredTextParagraph): pass

class StructuredTextTableData(ST.StructuredTextParagraph): pass

class StructuredTextMarkup(STDOM.Element):

    def __init__(self, v, **kw):
        self._value=v
        self._attributes=kw.keys()
        for k, v in kw.items(): setattr(self, k, v)

    def getChildren(self):
        v=self._value
        if not isinstance(v, ListType): v=[v]
        return v

    def getColorizableTexts(self): return self._value,
    def setColorizableTexts(self, v): self._value=v[0]

    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, `self._value`)

class StructuredTextLiteral(StructuredTextMarkup):
    def getColorizableTexts(self): return ()
    def setColorizableTexts(self, v): pass

class StructuredTextEmphasis(StructuredTextMarkup): pass

class StructuredTextStrong(StructuredTextMarkup): pass

class StructuredTextInnerLink(StructuredTextMarkup): pass

class StructuredTextNamedLink(StructuredTextMarkup): pass

class StructuredTextUnderline(StructuredTextMarkup): pass

class StructuredTextSGML(StructuredTextMarkup): pass

class StructuredTextLink(StructuredTextMarkup): pass

class StructuredTextXref(StructuredTextMarkup): pass

class DocumentClass:
    """
    Class instance calls [ex.=> x()] require a structured text
    structure. Doc will then parse each paragraph in the structure
    and will find the special structures within each paragraph.
    Each special structure will be stored as an instance. Special
    structures within another special structure are stored within
    the 'top' structure
    EX : '-underline this-' => would be turned into an underline
    instance. '-underline **this**' would be stored as an underline
    instance with a strong instance stored in its string
    """

    paragraph_types  = [
        'doc_bullet',
        'doc_numbered',
        'doc_description',
        'doc_header',
        'doc_table',
        ]

    #'doc_inner_link',
    #'doc_named_link',
    #'doc_underline'
    text_types = [
        'doc_literal',
        'doc_sgml',
        'doc_inner_link',
        'doc_named_link',
        'doc_href1',
        'doc_href2',
        'doc_strong',
        'doc_emphasize',
        'doc_underline',
        'doc_sgml',
        'doc_xref',
        ]

    def __call__(self, doc):
        if type(doc) in StringTypes:
            doc=ST.StructuredText(doc)
            doc.setSubparagraphs(self.color_paragraphs(
               doc.getSubparagraphs()))
        else:
            doc=ST.StructuredTextDocument(self.color_paragraphs(
               doc.getSubparagraphs()))
        return doc

    def parse(self, raw_string, text_type,
              type=type, sts=StringTypes, lt=type([])):

        """
        Parse accepts a raw_string, an expr to test the raw_string,
        and the raw_string's subparagraphs.

        Parse will continue to search through raw_string until
        all instances of expr in raw_string are found.

        If no instances of expr are found, raw_string is returned.
        Otherwise a list of substrings and instances is returned
        """

        tmp = []    # the list to be returned if raw_string is split
        append=tmp.append

        if type(text_type) in sts: text_type=getattr(self, text_type)

        while 1:
            t = text_type(raw_string)
            if not t: break
            #an instance of expr was found
            t, start, end    = t

            if start: append(raw_string[0:start])

            tt=type(t)
            if tt in sts:
                # if we get a string back, add it to text to be parsed
                raw_string = t+raw_string[end:len(raw_string)]
            else:
                if tt is lt:
                # is we get a list, append it's elements
                    tmp[len(tmp):]=t
                else:
                    # normal case, an object
                    append(t)
                raw_string = raw_string[end:len(raw_string)]

        if not tmp: return raw_string # nothing found

        if raw_string: append(raw_string)
        elif len(tmp)==1: return tmp[0]

        return tmp


    def color_text(self, str, types=None):
        """Search the paragraph for each special structure
        """
        if types is None: types=self.text_types

        for text_type in types:

            if type(str) in StringTypes:
                str = self.parse(str, text_type)
            elif type(str) is ListType:
                r=[]; a=r.append
                for s in str:
                    if type(s) in StringTypes:
                        s=self.parse(s, text_type)
                        if type(s) is ListType: r[len(r):]=s
                        else: a(s)
                    else:
                        s.setColorizableTexts(
                           map(self.color_text,
                               s.getColorizableTexts()
                               ))
                        a(s)
                str=r
            else:
                r=[]; a=r.append; color=self.color_text
                for s in str.getColorizableTexts():
                    color(s, (text_type,))
                    a(s)

                str.setColorizableTexts(r)

        return str

    def color_paragraphs(self, raw_paragraphs,
                           type=type, sequence_types=(type([]), type(())),
                           sts=StringTypes):
        result=[]
        for paragraph in raw_paragraphs:
            if paragraph.getNodeName() != 'StructuredTextParagraph':
                result.append(paragraph)
                continue

            for pt in self.paragraph_types:
                if type(pt) in sts:
                    # grab the corresponding function
                    pt=getattr(self, pt)
                # evaluate the paragraph
                r=pt(paragraph)
                if r:
                    if type(r) not in sequence_types:
                        r=r,
                    new_paragraphs=r
                    for paragraph in new_paragraphs:
                        subs = self.color_paragraphs(paragraph.getSubparagraphs())
                        paragraph.setSubparagraphs(subs)
                    break
            else:
                # copy, retain attributes
                kw = {}
                atts = getattr(paragraph, '_attributes', [])
                for att in atts: kw[att] = getattr(paragraph, att)
                subs = self.color_paragraphs(paragraph.getSubparagraphs())
                new_paragraphs=ST.StructuredTextParagraph(
                   paragraph. getColorizableTexts()[0], subs, **kw),

            # color the inline StructuredText types
            # for each StructuredTextParagraph
            for paragraph in new_paragraphs:

                if paragraph.getNodeName() is "StructuredTextTable":
#                cells = paragraph.getColumns()
                    text = paragraph.getColorizableTexts()
                    text = map(ST.StructuredText,text)
                    text = map(self.__call__,text)
                    for t in range(len(text)):
                        text[t] = text[t].getSubparagraphs()
                    paragraph.setColorizableTexts(text)

                paragraph.setColorizableTexts(
                   map(self.color_text,
                       paragraph.getColorizableTexts()
                       ))
                result.append(paragraph)

        return result

    def doc_table(self, paragraph, expr = re.compile(r'\s*\|[-]+\|').match):
        text    = paragraph.getColorizableTexts()[0]
        m       = expr(text)

        subs = paragraph.getSubparagraphs()

        if not (m):
            return None
        rows = []

        spans   = []
        ROWS    = []
        COLS    = []
        indexes = []
        ignore  = []

        TDdivider   = re.compile("[\-]+").match
        THdivider   = re.compile("[\=]+").match
        col         = re.compile('\|').search
        innertable  = re.compile('\|([-]+|[=]+)\|').search

        text = text.strip()
        rows = text.split('\n')
        foo  = ""

        for row in range(len(rows)):
            rows[row] = rows[row].strip()

        # have indexes store if a row is a divider
        # or a cell part
        for index in range(len(rows)):
            tmpstr = rows[index][1:len(rows[index])-1]
            if TDdivider(tmpstr):
                indexes.append("TDdivider")
            elif THdivider(tmpstr):
                indexes.append("THdivider")
            else:
                indexes.append("cell")

        for index in range(len(indexes)):
            if indexes[index] is "TDdivider" or indexes[index] is "THdivider":
                ignore = [] # reset ignore
                #continue    # skip dividers

            tmp     = rows[index].strip()    # clean the row up
            tmp     = tmp[1:len(tmp)-1]     # remove leading + trailing |
            offset  = 0

            # find the start and end of inner
            # tables. ignore everything between
            if innertable(tmp):
                tmpstr = tmp.strip()
                while innertable(tmpstr):
                    start,end   = innertable(tmpstr).span()
                    if not (start,end-1) in ignore:
                        ignore.append((start,end-1))
                    tmpstr = " " + tmpstr[end:]

            # find the location of column dividers
            # NOTE: |'s in inner tables do not count
            #   as column dividers
            if col(tmp):
                while col(tmp):
                    bar         = 1   # true if start is not in ignore
                    start,end   = col(tmp).span()

                    if not start+offset in spans:
                        for s,e in ignore:
                            if start+offset >= s or start+offset <= e:
                                bar = None
                                break
                        if bar:   # start is clean
                            spans.append(start+offset)
                    if not bar:
                        foo = foo + tmp[:end]
                        tmp = tmp[end:]
                        offset = offset + end
                    else:
                        COLS.append((foo + tmp[0:start],start+offset))
                        foo = ""
                        tmp = " " + tmp[end:]
                        offset = offset + start
            if not offset+len(tmp) in spans:
                spans.append(offset+len(tmp))
            COLS.append((foo + tmp,offset+len(tmp)))
            foo = ""
            ROWS.append(COLS)
            COLS = []

        spans.sort()
        ROWS = ROWS[1:len(ROWS)]

        # find each column span
        cols    = []
        tmp     = []

        for row in ROWS:
            for c in row:
                tmp.append(c[1])
            cols.append(tmp)
            tmp = []

        cur = 1
        tmp = []
        C   = []
        for col in cols:
            for span in spans:
                if not span in col:
                    cur = cur + 1
                else:
                    tmp.append(cur)
                    cur = 1
            C.append(tmp)
            tmp = []

        for index in range(len(C)):
            for i in range(len(C[index])):
                ROWS[index][i] = (ROWS[index][i][0],C[index][i])
        rows = ROWS

        # label things as either TableData or
        # Table header
        TD  = []
        TH  = []
        all = []
        for index in range(len(indexes)):
            if indexes[index] is "TDdivider":
                TD.append(index)
                all.append(index)
            if indexes[index] is "THdivider":
                TH.append(index)
                all.append(index)
        TD = TD[1:]
        dividers = all[1:]
        #print "TD  => ", TD
        #print "TH  => ", TH
        #print "all => ", all, "\n"

        for div in dividers:
            if div in TD:
                index = all.index(div)
                for rowindex in range(all[index-1],all[index]):
                    for i in range(len(rows[rowindex])):
                        rows[rowindex][i] = (rows[rowindex][i][0],
                                             rows[rowindex][i][1],
                                             "td")
            else:
                index = all.index(div)
                for rowindex in range(all[index-1],all[index]):
                    for i in range(len(rows[rowindex])):
                        rows[rowindex][i] = (rows[rowindex][i][0],
                                             rows[rowindex][i][1],
                                             "th")

        # now munge the multi-line cells together
        # as paragraphs
        ROWS    = []
        COLS    = []
        for row in rows:
            for index in range(len(row)):
                if not COLS:
                    COLS = range(len(row))
                    for i in range(len(COLS)):
                        COLS[i] = ["",1,""]
                if TDdivider(row[index][0]) or THdivider(row[index][0]):
                    ROWS.append(COLS)
                    COLS = []
                else:
                    COLS[index][0] = COLS[index][0] + (row[index][0]) + "\n"
                    COLS[index][1] = row[index][1]
                    COLS[index][2] = row[index][2]

        # now that each cell has been munged together,
        # determine the cell's alignment.
        # Default is to center. Also determine the cell's
        # vertical alignment, top, middle, bottom. Default is
        # to middle
        rows = []
        cols = []
        for row in ROWS:
            for index in range(len(row)):
                topindent       = 0
                bottomindent    = 0
                leftindent      = 0
                rightindent     = 0
                left            = []
                right           = []
                text            = row[index][0]
                text            = text.split('\n')
                text            = text[:len(text)-1]
                align           = ""
                valign          = ""
                for t in text:
                    t = t.strip()
                    if not t:
                        topindent = topindent + 1
                    else:
                        break
                text.reverse()
                for t in text:
                    t = t.strip()
                    if not t:
                        bottomindent = bottomindent + 1
                    else:
                        break
                text.reverse()
                tmp   = '\n'.join(text[topindent:len(text)-bottomindent])
                pars  = re.compile("\n\s*\n").split(tmp)
                for par in pars:
                    if index > 0:
                        par = par[1:]
                    par = par.split(' ')
                    for p in par:
                        if not p:
                            leftindent = leftindent+1
                        else:
                            break
                    left.append(leftindent)
                    leftindent = 0
                    par.reverse()
                    for p in par:
                        if not p:
                            rightindent = rightindent + 1
                        else:
                            break
                    right.append(rightindent)
                    rightindent = 0
                left.sort()
                right.sort()

                if topindent == bottomindent:
                    valign="middle"
                elif topindent < 1:
                    valign="top"
                elif bottomindent < 1:
                    valign="bottom"
                else:
                    valign="middle"

                if left[0] < 1:
                    align = "left"
                elif right[0] < 1:
                    align = "right"
                elif left[0] > 1 and right[0] > 1:
                    align="center"
                else:
                    align="left"

                cols.append((row[index][0],row[index][1],align,valign,row[index][2]))
            rows.append(cols)
            cols = []
        return StructuredTextTable(rows,text,subs,indent=paragraph.indent)

    def doc_bullet(self, paragraph, expr = re.compile(r'\s*[-*o]\s+').match):
        top=paragraph.getColorizableTexts()[0]
        m=expr(top)

        if not m:
            return None

        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[StructuredTextExample(subs)]
            top=top[:-1]
        return StructuredTextBullet(top[m.span()[1]:], subs,
                                     indent=paragraph.indent,
                                     bullet=top[:m.span()[1]]
                                     )

    def doc_numbered(
        self, paragraph,
        expr = re.compile(r'(\s*[%s]\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)' % letters).match):

        # This is the old expression. It had a nasty habit
        # of grabbing paragraphs that began with a single
        # letter word even if there was no following period.

        #expr = re.compile('\s*'
        #                   '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
        #                   '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
        #                   '\s+').match):

        top=paragraph.getColorizableTexts()[0]
        m=expr(top)
        if not m: return None
        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[StructuredTextExample(subs)]
            top=top[:-1]
        return StructuredTextNumbered(top[m.span()[1]:], subs,
                                        indent=paragraph.indent,
                                        number=top[:m.span()[1]])

    def doc_description(
        self, paragraph,
        delim = re.compile(r'\s+--\s+').search,
        nb=re.compile(r'[^\000- ]').search,
        ):

        top=paragraph.getColorizableTexts()[0]
        d=delim(top)
        if not d: return None
        start, end = d.span()
        title=top[:start]
        if title.find('\n') >= 0: return None
        if not nb(title): return None
        d=top[start:end]
        top=top[end:]

        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[StructuredTextExample(subs)]
            top=top[:-1]

        return StructuredTextDescription(
           title, top, subs,
           indent=paragraph.indent,
           delim=d)

    def doc_header(self, paragraph):
        subs=paragraph.getSubparagraphs()
        if not subs: return None
        top=paragraph.getColorizableTexts()[0]
        if not top.strip(): return None
        if top[-2:]=='::':
            subs=StructuredTextExample(subs)
            if top.strip()=='::': return subs
            # copy attrs when returning a paragraph
            kw = {}
            atts = getattr(paragraph, '_attributes', [])
            for att in atts: kw[att] = getattr(paragraph, att)
            return ST.StructuredTextParagraph(top[:-1], [subs], **kw)

        if top.find('\n') >= 0: return None
        return StructuredTextSection(top, subs, indent=paragraph.indent)

    def doc_literal(
        self, s,
        expr = re.compile(r"(\W+|^)'([%s%s%s\s]+)'([%s]+|$)" % (letters, digits, literal_punc, phrase_delimiters)).search,):

        # old expr... failed to cross newlines.
        #        expr=re.compile(
        #          r"(?:\s|^)'"           # open
        #          r"([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n\r']*[^ \t\n\r\f\v'])" # contents
        #          r"'(?:\s|[,.;:!?]|$)"  # close
        #          ).search):

        r=expr(s) #or expr2(s)
        if r:
            start, end = r.span(2)
            return (StructuredTextLiteral(s[start:end]), start-1, end+1)
        else:
            return None

    def doc_emphasize(
        self, s,
        expr = re.compile(r'\*([%s%s%s\s]+?)\*' % (letters, digits, strongem_punc)).search
        #expr = re.compile(r'\s*\*([ \n\r%s0-9.:/;,\'\"\?\-\_\/\=\-\>\<\(\)]+)\*(?!\*|-)' % letters).search       # old expr, inconsistent punctuation
        ):

        r=expr(s)
        if r:
            start, end = r.span(1)
            return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
        else:
            return None

    def doc_inner_link(self,
                       s,
                       expr1 = re.compile(r"\.\.\s*").search,
                       expr2 = re.compile(r"\[[%s%s]+\]" % (letters, digits) ).search):

        # make sure we dont grab a named link
        if expr2(s) and expr1(s):
            start1,end1 = expr1(s).span()
            start2,end2 = expr2(s).span()
            if end1 == start2:
                # uh-oh, looks like a named link
                return None
            else:
                # the .. is somewhere else, ignore it
                return (StructuredTextInnerLink(s[start2+1:end2-1]),start2,end2)
            return None
        elif expr2(s) and not expr1(s):
            start,end = expr2(s).span()
            return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
        return None

    def doc_named_link(self,
                       s,
                       expr=re.compile(r"(\.\.\s)(\[[%s0-9]+\])" % letters).search):

        result = expr(s)
        if result:
            start,end   = result.span(2)
            str = s[start+1:end-1]
            st,en       = result.span()
            return (StructuredTextNamedLink(str),st,en)
        return None

    def doc_underline(self,
                      s,
                      expr=re.compile(r'_([%s%s%s\s]+)_([\s%s]|$)' % (letters, digits, under_punc,phrase_delimiters)).search):

        result = expr(s)
        if result:
            if result.group(1)[:1] == '_':
                return None # no double unders
            start,end = result.span(1)
            st,e = result.span()
            return (StructuredTextUnderline(s[start:end]),st,e-len(result.group(2)))
        else:
            return None

    def doc_strong(self,
                   s,
                   expr = re.compile(r'\*\*([%s%s%s\s]+?)\*\*' % (letters, digits, strongem_punc)).search
                   #expr = re.compile(r'\s*\*\*([ \n\r%s0-9.:/;,\'\"\?\-\_\/\=\-\>\<\(\)]+)\*\*(?!\*|-)' % letters).search, # old expr, inconsistent punc, failed to cross newlines.
        ):

        r=expr(s)
        if r:
            start, end = r.span(1)
            return (StructuredTextStrong(s[start:end]), start-2, end+2)
        else:
            return None

    ## Some constants to make the doc_href() regex easier to read.
    _DQUOTEDTEXT = r'("[ %s0-9\n\r%s]+")' % (letters,dbl_quoted_punc) ## double quoted text
    _ABSOLUTE_URL=r'((http|https|ftp|mailto|file|about)[:/]+?[%s0-9_\@\.\,\?\!\/\:\;\-\#\~\=\&\%%\+]+)' % letters
    _ABS_AND_RELATIVE_URL=r'([%s0-9_\@\.\,\?\!\/\:\;\-\#\~\=\&\%%\+]+)' % letters

    _SPACES = r'(\s*)'


    def doc_href1(self, s,
                  expr=re.compile(_DQUOTEDTEXT + "(:)" + _ABS_AND_RELATIVE_URL + _SPACES).search
                   ):
        return self.doc_href(s, expr)

    def doc_href2(self, s,
                  expr=re.compile(_DQUOTEDTEXT + r'(\,\s+)' + _ABSOLUTE_URL + _SPACES).search
                   ):
        return self.doc_href(s, expr)


    def doc_href(self, s, expr, punctuation=re.compile(r"[\,\.\?\!\;]+").match):

        r=expr(s)

        if r:
            # need to grab the href part and the
            # beginning part

            start,e = r.span(1)
            name    = s[start:e]
            name    = name.replace('"','',2)
            #start   = start + 1
            st,end   = r.span(3)
            if punctuation(s[end-1:end]):
                end = end -1
            link    = s[st:end]
            #end     = end - 1

            # name is the href title, link is the target
            # of the href
            return (StructuredTextLink(name, href=link),
                    start, end)

            #return (StructuredTextLink(s[start:end], href=s[start:end]),
            #        start, end)
        else:
            return None

    def doc_sgml(self,s,expr=re.compile(r"\<[%s0-9\.\=\'\"\:\/\-\#\+\s\*]+\>" % letters).search):
        """
        SGML text is ignored and outputed as-is
        """
        r = expr(s)
        if r:
            start,end = r.span()
            text = s[start:end]
            return (StructuredTextSGML(text),start,end)
        else:
            return None


    def doc_xref(self, s,
        expr = re.compile('\[([%s0-9\-.:/;,\n\r\~]+)\]' % letters).search
        ):
        r = expr(s)
        if r:
            start, end = r.span(1)
            return (StructuredTextXref(s[start:end]), start-1, end+1)
        else:
            return None


=== Added File zopeproducts/bugtracker/browser/StructuredText/DocumentWithImages.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

import re
from DocumentClass import *

class StructuredTextImage(StructuredTextMarkup):
    "A simple embedded image"

class DocumentWithImages(DocumentClass):
    """

    """


    text_types = [
       'doc_img',
       ] + DocumentClass.text_types


    def doc_img(
        self, s,
        expr1=re.compile('\"([ _a-zA-Z0-9*.:/;,\-\n\~]+)\":img:([a-zA-Z0-9\_\-.:/;,\n\~]+)').search,
        expr2=re.compile('\"([ _a-zA-Z0-9*.:/;,\-\n\~]+)\":img:([a-zA-Z0-9\_\-.:/;,\n\~]+):([a-zA-Z0-9_\-.:/;,\n\~]+)').search
        ):

        r = expr2(s)
        if r:

            # Warning: the regex are getting confused when the string after :img:
            # is an URL containing ":" (Collector #2276)
            # Ugly workaround: check if have an absolute URL here. Not a cool solution,
            # but it works !

            if not r.group(2) in ['http','file','ftp']:

                startt, endt = r.span(1)
                startk, endk = r.span(2)
                starth, endh = r.span(3)
                start, end = r.span()

                key = s[startk:endk]

                return (StructuredTextImage(s[startt:endt], href=s[starth:endh], key=s[startk:endk]),
                     start, end)


        r=expr1(s)
        if r:
            startt, endt = r.span(1)
            starth, endh = r.span(2)
            start, end = r.span()
            return (StructuredTextImage(s[startt:endt], href=s[starth:endh]),
                    start, end)

        return None


=== Added File zopeproducts/bugtracker/browser/StructuredText/HTMLClass.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

from cgi import escape
import re, sys, ST

class HTMLClass:

    element_types={
        '#text': '_text',
        'StructuredTextDocument': 'document',
        'StructuredTextParagraph': 'paragraph',
        'StructuredTextExample': 'example',
        'StructuredTextBullet': 'bullet',
        'StructuredTextNumbered': 'numbered',
        'StructuredTextDescription': 'description',
        'StructuredTextDescriptionTitle': 'descriptionTitle',
        'StructuredTextDescriptionBody': 'descriptionBody',
        'StructuredTextSection': 'section',
        'StructuredTextSectionTitle': 'sectionTitle',
        'StructuredTextLiteral': 'literal',
        'StructuredTextEmphasis': 'emphasis',
        'StructuredTextStrong': 'strong',
        'StructuredTextLink': 'link',
        'StructuredTextXref': 'xref',
        'StructuredTextInnerLink':'innerLink',
        'StructuredTextNamedLink':'namedLink',
        'StructuredTextUnderline':'underline',
        'StructuredTextTable':'table',
        'StructuredTextSGML':'sgml',
        }


    def dispatch(self, doc, level, output):
        getattr(self, self.element_types[doc.getNodeName()])(doc, level, output)

    def __call__(self, doc, level=1, header=1):
        r=[]
        self.header = header
        self.dispatch(doc, level-1, r.append)
        return ''.join(r)

    def _text(self, doc, level, output):
        output(doc.getNodeValue())

    def document(self, doc, level, output):
        children=doc.getChildNodes()

        if self.header:
            output('<html>\n')
            if (children and
                 children[0].getNodeName() == 'StructuredTextSection'):
                output('<head>\n<title>%s</title>\n</head>\n' %
                         children[0].getChildNodes()[0].getNodeValue())
            output('<body>\n')

        for c in children:
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)

        if self.header:
            output('</body>\n')
            output('</html>\n')

    def section(self, doc, level, output):
        children=doc.getChildNodes()
        for c in children:
            getattr(self, self.element_types[c.getNodeName()])(c, level+1, output)

    def sectionTitle(self, doc, level, output):
        output('<h%d>' % (level))
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</h%d>\n' % (level))

    def description(self, doc, level, output):
        p=doc.getPreviousSibling()
        if p is None or  p.getNodeName() is not doc.getNodeName():
            output('<dl>\n')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        n=doc.getNextSibling()
        if n is None or n.getNodeName() is not doc.getNodeName():
            output('</dl>\n')

    def descriptionTitle(self, doc, level, output):
        output('<dt>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</dt>\n')

    def descriptionBody(self, doc, level, output):
        output('<dd>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</dd>\n')

    def bullet(self, doc, level, output):
        p=doc.getPreviousSibling()
        if p is None or p.getNodeName() is not doc.getNodeName():
            output('\n<ul>\n')
        output('<li>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        n=doc.getNextSibling()
        output('</li>\n')
        if n is None or n.getNodeName() is not doc.getNodeName():
            output('\n</ul>\n')

    def numbered(self, doc, level, output):
        p=doc.getPreviousSibling()
        if p is None or p.getNodeName() is not doc.getNodeName():
            output('\n<ol>\n')
        output('<li>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        n=doc.getNextSibling()
        output('</li>\n')
        if n is None or n.getNodeName() is not doc.getNodeName():
            output('\n</ol>\n')

    def example(self, doc, level, output):
        i=0
        for c in doc.getChildNodes():
            if i==0:
                output('\n<pre>\n')
                output(escape(c.getNodeValue()))
                output('\n</pre>\n')
            else:
                getattr(self, self.element_types[c.getNodeName()])(
                    c, level, output)

    def paragraph(self, doc, level, output):

        output('<p>')
        for c in doc.getChildNodes():
            if c.getNodeName() in ['StructuredTextParagraph']:
                getattr(self, self.element_types[c.getNodeName()])(
                    c, level, output)
            else:
                getattr(self, self.element_types[c.getNodeName()])(
                    c, level, output)
        output('</p>\n')

    def link(self, doc, level, output):
        output('<a href="%s">' % doc.href)
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</a>')

    def emphasis(self, doc, level, output):
        output('<em>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</em>')

    def literal(self, doc, level, output):
        output('<code>')
        for c in doc.getChildNodes():
            output(escape(c.getNodeValue()))
        output('</code>')

    def strong(self, doc, level, output):
        output('<strong>')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('</strong>')

    def underline(self, doc, level, output):
        output("<u>")
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output("</u>")

    def innerLink(self, doc, level, output):
        output('<a href="#ref');
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('">[')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output(']</a>')

    def namedLink(self, doc, level, output):
        output('<a name="ref')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output('">[')
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)
        output(']</a>')

    def sgml(self,doc,level,output):
        for c in doc.getChildNodes():
            getattr(self, self.element_types[c.getNodeName()])(c, level, output)

    def xref(self, doc, level, output):
        val = doc.getNodeValue()
        output('<a href="#ref%s">[%s]</a>' % (val, val) )

    def table(self,doc,level,output):
        """
        A StructuredTextTable holds StructuredTextRow(s) which
        holds StructuredTextColumn(s). A StructuredTextColumn
        is a type of StructuredTextParagraph and thus holds
        the actual data.
        """
        output('<table border="1" cellpadding="2">\n')
        for row in doc.getRows()[0]:
            output("<tr>\n")
            for column in row.getColumns()[0]:
                if hasattr(column,"getAlign"):
                    str = '<%s colspan="%s" align="%s" valign="%s">' % (column.getType(),
                                                                  column.getSpan(),
                                                                  column.getAlign(),
                                                                  column.getValign())
                else:
                    str = '<td colspan="%s">' % column.getSpan()
                output(str)
                for c in column.getChildNodes():
                    getattr(self, self.element_types[c.getNodeName()])(c, level, output)
                if hasattr(column,"getType"):
                    output("</"+column.getType()+">\n")
                else:
                    output("</td>\n")
            output("</tr>\n")
        output("</table>\n")


=== Added File zopeproducts/bugtracker/browser/StructuredText/HTMLWithImages.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################


from HTMLClass import HTMLClass

ets = HTMLClass.element_types
ets.update({'StructuredTextImage': 'image'})

class HTMLWithImages(HTMLClass):

    element_types = ets


    def image(self, doc, level, output):
        if hasattr(doc, 'key'):
            output('<a name="%s"></a>\n' % doc.key)
        output('<img src="%s" alt="%s">\n' % (doc.href, doc.getNodeValue()))
        if doc.getNodeValue() and hasattr(doc, 'key'):
            output('<p><b>Figure %s</b> %s</p>\n' % (doc.key, doc.getNodeValue()))


=== Added File zopeproducts/bugtracker/browser/StructuredText/ST.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

import re, STDOM
from types import ListType

#####################################################################
#                              Updated functions                    #
#####################################################################

def indention(str,front = re.compile("^\s+").match):
    """
    Find the number of leading spaces. If none, return 0.
    """

    result = front(str)
    if result is not None:
        start, end = result.span()
        return end-start
    else:
        return 0     # no leading spaces

def insert(struct, top, level):
    """
    find what will be the parant paragraph of
    a sentence and return that paragraph's
    sub-paragraphs. The new paragraph will be
    appended to those sub-paragraphs
    """
    #print "struct", struct, top-1
    if not top-1 in range(len(struct)):
        if struct:
            return struct[len(struct)-1].getSubparagraphs()
        return struct
    run = struct[top-1]
    i    = 0
    while i+1 < level:
        run = run.getSubparagraphs()[len(run.getSubparagraphs())-1]
        i = i + 1
    #print "parent for level ", level, " was => ", run.getColorizableTexts()
    return run.getSubparagraphs()

def display(struct):
    """
    runs through the structure and prints out
    the paragraphs. If the insertion works
    correctly, display's results should mimic
    the orignal paragraphs.
    """

    if struct.getColorizableTexts():
        print join(struct.getColorizableTexts()),"\n"
    if struct.getSubparagraphs():
        for x in struct.getSubparagraphs():
            display(x)

def display2(struct):
    """
    runs through the structure and prints out
    the paragraphs. If the insertion works
    correctly, display's results should mimic
    the orignal paragraphs.
    """

    if struct.getNodeValue():
        print struct.getNodeValue(),"\n"
    if struct.getSubparagraphs():
        for x in struct.getSubparagraphs():
            display(x)

def findlevel(levels,indent):
    """
    remove all level information of levels
    with a greater level of indentation.
    Then return which level should insert this
    paragraph
    """

    keys = levels.keys()
    for key in keys:
        if levels[key] > indent:
            del(levels[key])
    keys = levels.keys()
    if not(keys):
        return 0
    else:
        for key in keys:
            if levels[key] == indent:
                return key
    highest = 0
    for key in keys:
        if key > highest:
            highest = key
    return highest-1

para_delim = r'(\n\s*\n|\r\n\s*\r\n)' # UNIX or DOS line endings, respectively

#####################################################################

# Golly, the capitalization of this function always makes me think it's a class
def StructuredText(paragraphs, delimiter=re.compile(para_delim)):
    """
    StructuredText accepts paragraphs, which is a list of
    lines to be parsed. StructuredText creates a structure
    which mimics the structure of the paragraphs.
    Structure => [paragraph,[sub-paragraphs]]
    """

    currentlevel    = 0
    currentindent  = 0
    levels            = {0:0}
    level             = 0        # which header are we under
    struct            = []      # the structure to be returned
    run                = struct

    paragraphs = paragraphs.expandtabs()
    paragraphs = '%s%s%s' % ('\n\n', paragraphs, '\n\n')
    paragraphs = delimiter.split(paragraphs)
    paragraphs = [ x for x in  paragraphs if x.strip() ]

    if not paragraphs: return StructuredTextDocument()

    ind = []     # structure based on indention levels
    for paragraph in paragraphs:
        ind.append([indention(paragraph), paragraph])

    currentindent = indention(paragraphs[0])
    levels[0]        = currentindent

    #############################################################
    #                                  updated                  #
    #############################################################

    for indent,paragraph in ind :
        if indent == 0:
            level          = level + 1
            currentlevel   = 0
            currentindent  = 0
            levels         = {0:0}
            struct.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
        elif indent > currentindent:
            currentlevel            = currentlevel + 1
            currentindent           = indent
            levels[currentlevel]    = indent
            run = insert(struct,level,currentlevel)
            run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
        elif indent < currentindent:
            result   = findlevel(levels,indent)
            if result > 0:
                currentlevel = result
            currentindent  = indent
            if not level:
                struct.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
            else:
                run = insert(struct,level,currentlevel)
                run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
        else:
            if insert(struct,level,currentlevel):
                run = insert(struct,level,currentlevel)
            else:
                run = struct
                currentindent = indent
            run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))

    return StructuredTextDocument(struct)

Basic = StructuredText

class StructuredTextParagraph(STDOM.Element):

    indent=0

    def __init__(self, src, subs=None, **kw):
        if subs is None: subs=[]
        self._src=src
        self._subs=list(subs)

        self._attributes=kw.keys()
        for k, v in kw.items(): setattr(self, k, v)

    def getChildren(self):
        src=self._src
        if not isinstance(src, ListType): src=[src]
        return src+self._subs

    def getAttribute(self, name):
        return getattr(self, name, None)

    def getAttributeNode(self, name):
        if hasattr(self, name):
            return STDOM.Attr(name, getattr(self, name))
        else:
            return None

    def getAttributes(self):
        d={}
        for a in self._attributes:
            d[a]=getattr(self, a, '')
        return STDOM.NamedNodeMap(d)

    def getSubparagraphs(self):
        return self._subs

    def setSubparagraphs(self, subs):
        self._subs=subs

    def getColorizableTexts(self):
        return (self._src,)

    def setColorizableTexts(self, src):
        self._src=src[0]

    def __repr__(self):
        r=[]; a=r.append
        a((' '*(self.indent or 0))+
          ('%s(' % self.__class__.__name__)
          +str(self._src)+', ['
          )
        for p in self._subs: a(`p`)
        a((' '*(self.indent or 0))+'])')
        return '\n'.join(r)

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_Children(self):
        return self.getChildren()

    def _get_Attribute(self, name):
        return self.getAttribute(name)

    def _get_AttributeNode(self, name):
        return self.getAttributeNode(name)

    def _get_Attributes(self):
        return self.getAttributes()

    def _get_Subparagraphs(self):
        return self.getSubparagraphs()

    def _set_Subparagraphs(self, subs):
        return self.setSubparagraphs(subs)

    def _get_ColorizableTexts(self):
        return self.getColorizableTexts()

    def _set_ColorizableTexts(self, src):
        return self.setColorizableTexts(src)

class StructuredTextDocument(StructuredTextParagraph):
    """
    A StructuredTextDocument holds StructuredTextParagraphs
    as its subparagraphs.
    """
    _attributes=()

    def __init__(self, subs=None, **kw):
        StructuredTextParagraph.__init__(self, '', subs, **kw)

    def getChildren(self):
        return self._subs

    def getColorizableTexts(self):
        return ()

    def setColorizableTexts(self, src):
        pass

    def __repr__(self):
        r=[]; a=r.append
        a('%s([' % self.__class__.__name__)
        for p in self._subs: a(`p`+',')
        a('])')
        return '\n'.join(r)

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_Children(self):
        return self.getChildren()

    def _get_ColorizableTexts(self):
        return self.getColorizableTexts()

    def _set_ColorizableTexts(self, src):
        return self.setColorizableTexts(src)


=== Added File zopeproducts/bugtracker/browser/StructuredText/STDOM.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
DOM implementation in StructuredText : Read-Only methods

All standard Zope objects support DOM to a limited extent.
"""

from types import StringType, UnicodeType
StringTypes = (StringType, UnicodeType)

# Node type codes
# ---------------

ELEMENT_NODE                  = 1
ATTRIBUTE_NODE                = 2
TEXT_NODE                     = 3
CDATA_SECTION_NODE            = 4
ENTITY_REFERENCE_NODE         = 5
ENTITY_NODE                   = 6
PROCESSING_INSTRUCTION_NODE   = 7
COMMENT_NODE                  = 8
DOCUMENT_NODE                 = 9
DOCUMENT_TYPE_NODE            = 10
DOCUMENT_FRAGMENT_NODE        = 11
NOTATION_NODE                 = 12

# Exception codes
# ---------------

INDEX_SIZE_ERR                = 1
DOMSTRING_SIZE_ERR            = 2
HIERARCHY_REQUEST_ERR         = 3
WRONG_DOCUMENT_ERR            = 4
INVALID_CHARACTER_ERR         = 5
NO_DATA_ALLOWED_ERR           = 6
NO_MODIFICATION_ALLOWED_ERR   = 7
NOT_FOUND_ERR                 = 8
NOT_SUPPORTED_ERR             = 9
INUSE_ATTRIBUTE_ERR           = 10

# Exceptions
# ----------

class DOMException(Exception):
    pass
class IndexSizeException(DOMException):
    code = INDEX_SIZE_ERR
class DOMStringSizeException(DOMException):
    code = DOMSTRING_SIZE_ERR
class HierarchyRequestException(DOMException):
    code = HIERARCHY_REQUEST_ERR
class WrongDocumentException(DOMException):
    code = WRONG_DOCUMENT_ERR
class InvalidCharacterException(DOMException):
    code = INVALID_CHARACTER_ERR
class NoDataAllowedException(DOMException):
    code = NO_DATA_ALLOWED_ERR
class NoModificationAllowedException(DOMException):
    code = NO_MODIFICATION_ALLOWED_ERR
class NotFoundException(DOMException):
    code = NOT_FOUND_ERR
class NotSupportedException(DOMException):
    code = NOT_SUPPORTED_ERR
class InUseAttributeException(DOMException):
    code = INUSE_ATTRIBUTE_ERR

# Node classes
# ------------

class ParentNode:
    """
    A node that can have children, or, more precisely, that implements
    the child access methods of the DOM.
    """

    def getChildNodes(self, type=type, sts=StringTypes):
        """
        Returns a NodeList that contains all children of this node.
        If there are no children, this is a empty NodeList
        """

        r=[]
        for n in self.getChildren():
            if type(n) in sts: n=TextNode(n)
            r.append(n.__of__(self))

        return  NodeList(r)

    def getFirstChild(self, type=type, sts=StringTypes):
        """
        The first child of this node. If there is no such node
        this returns None
        """
        children = self.getChildren()

        if not children:
            return None

        n=children[0]

        if type(n) in sts:
            n=TextNode(n)

        return n.__of__(self)

    def getLastChild(self, type=type, sts=StringTypes):
        """
        The last child of this node.  If there is no such node
        this returns None.
        """
        children = self.getChildren()
        if not children: return None
        n=children[-1]
        if type(n) in sts: n=TextNode(n)
        return n.__of__(self)

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_ChildNodes(self, type=type, sts=StringTypes):
        return self.getChildNodes(type,sts)

    def _get_FirstChild(self, type=type, sts=StringTypes):
        return self.getFirstChild(type,sts)

    def _get_LastChild(self, type=type, sts=StringTypes):
        return self.getLastChild(type,sts)

class NodeWrapper(ParentNode):
    """
    This is an acquisition-like wrapper that provides parent access for
    DOM sans circular references!
    """

    def __init__(self, aq_self, aq_parent):
        self.aq_self=aq_self
        self.aq_parent=aq_parent

    def __getattr__(self, name):
        return getattr(self.aq_self, name)

    def getParentNode(self):
        """
        The parent of this node.  All nodes except Document
        DocumentFragment and Attr may have a parent
        """
        return self.aq_parent

    def _getDOMIndex(self, children, getattr=getattr):
        i=0
        self=self.aq_self
        for child in children:
            if getattr(child, 'aq_self', child) is self:
                self._DOMIndex=i
                return i
            i=i+1
        return None

    def getPreviousSibling(self):
        """
        The node immediately preceding this node.  If
        there is no such node, this returns None.
        """

        children = self.aq_parent.getChildren()
        if not children:
            return None

        index=getattr(self, '_DOMIndex', None)
        if index is None:
            index=self._getDOMIndex(children)
            if index is None: return None

        index=index-1
        if index < 0: return None
        try: n=children[index]
        except IndexError: return None
        else:
            if type(n) in StringTypes:
                n=TextNode(n)
            n._DOMIndex=index
            return n.__of__(self)


    def getNextSibling(self):
        """
        The node immediately preceding this node.  If
        there is no such node, this returns None.
        """
        children = self.aq_parent.getChildren()
        if not children:
            return None

        index=getattr(self, '_DOMIndex', None)
        if index is None:
            index=self._getDOMIndex(children)
            if index is None:
                return None

        index=index+1
        try: n=children[index]
        except IndexError:
            return None
        else:
            if type(n) in StringTypes:
                n=TextNode(n)
            n._DOMIndex=index
            return n.__of__(self)

    def getOwnerDocument(self):
        """
        The Document object associated with this node, if any.
        """
        return self.aq_parent.getOwnerDocument()

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_ParentNode(self):
        return self.getParentNode()

    def _get_DOMIndex(self, children, getattr=getattr):
        return self._getDOMIndex(children,getattr)

    def _get_PreviousSibling(self):
        return self.getPreviousSibling()

    def _get_NextSibling(self):
        return self.getNextSibling()

    def _get_OwnerDocument(self):
        return self.getOwnerDocument()

class Node(ParentNode):
    """
    Node Interface
    """

    # Get a DOM wrapper with a parent link
    def __of__(self, parent):
        return NodeWrapper(self, parent)

    # DOM attributes
    # --------------

    def getNodeName(self):
        """
        The name of this node, depending on its type
        """

    def getNodeValue(self):
        """
        The value of this node, depending on its type
        """
        return None

    def getParentNode(self):
        """
        The parent of this node.  All nodes except Document
        DocumentFragment and Attr may have a parent
        """

    def getChildren(self):
        """
        Get a Python sequence of children
        """
        return ()

    def getPreviousSibling(self,
                           type=type,
                           sts=StringTypes,
                           getattr=getattr):
        """
        The node immediately preceding this node.  If
        there is no such node, this returns None.
        """

    def getNextSibling(self, type=type, sts=StringTypes):
        """
        The node immediately preceding this node.  If
        there is no such node, this returns None.
        """

    def getAttributes(self):
        """
        Returns a NamedNodeMap containing the attributes
        of this node (if it is an element) or None otherwise.
        """
        return None

    def getOwnerDocument(self):
        """
        The Document object associated with this node, if any.
        """

    # DOM Methods
    # -----------

    def hasChildNodes(self):
        """
        Returns true if the node has any children, false
        if it doesn't.
        """
        return len(self.getChildren())

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_NodeName(self):
        return self.getNodeName()

    def _get_NodeValue(self):
        return self.getNodeValue()

    def _get_ParentNode(self):
        return self.getParentNode()

    def _get_Children(self):
        return self.getChildren()

    def _get_PreviousSibling(self):
        return self.getPreviousSibling()

    def _get_NextSibling(self):
        return self.getNextSibling()

    def _get_Attributes(self):
        return self.getAttributes()

    def _get_OwnerDocument(self):
        return self.getOwnerDocument()

    def _has_ChildNodes(self):
        return self.hasChildNodes()


class TextNode(Node):

    def __init__(self, str): self._value=str

    def getNodeType(self):
        return TEXT_NODE

    def getNodeName(self):
        return '#text'

    def getNodeValue(self):
        return self._value

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_NodeType(self):
        return self.getNodeType()

    def _get_NodeName(self):
        return self.getNodeName()

    def _get_NodeValue(self):
        return self.getNodeValue()

class Element(Node):
    """
    Element interface
    """

    # Element Attributes
    # ------------------

    def getTagName(self):
        """The name of the element"""
        return self.__class__.__name__

    def getNodeName(self):
        """The name of this node, depending on its type"""
        return self.__class__.__name__

    def getNodeType(self):
        """A code representing the type of the node."""
        return ELEMENT_NODE

    def getNodeValue(self):
        r=[]
        for c in self.getChildren():
            if type(c) not in StringTypes:
                c=c.getNodeValue()
            r.append(c)
        return ''.join(r)

    def getParentNode(self):
        """
        The parent of this node.  All nodes except Document
        DocumentFragment and Attr may have a parent
        """

    # Element Methods
    # ---------------

    _attributes=()

    def getAttribute(self, name): return getattr(self, name, None)
    def getAttributeNode(self, name):
        if hasattr(self, name):
            return Attr(name, getattr(self, name))

    def getAttributes(self):
        d={}
        for a in self._attributes:
            d[a]=getattr(self, a, '')
        return NamedNodeMap(d)

    def getAttribute(self, name):
        """Retrieves an attribute value by name."""
        return None

    def getAttributeNode(self, name):
        """ Retrieves an Attr node by name or None if
        there is no such attribute. """
        return None

    def getElementsByTagName(self, tagname):
        """
        Returns a NodeList of all the Elements with a given tag
        name in the order in which they would be encountered in a
        preorder traversal of the Document tree.  Parameter: tagname
        The name of the tag to match (* = all tags). Return Value: A new
        NodeList object containing all the matched Elements.
        """
        nodeList = []
        for child in self.getChildren():
            if not hasattr(child, 'getNodeType'): continue
            if (child.getNodeType()==ELEMENT_NODE and \
                child.getTagName()==tagname or tagname== '*'):

                nodeList.append(child)

            if hasattr(child, 'getElementsByTagName'):
                n1       = child.getElementsByTagName(tagname)
                nodeList = nodeList + n1._data
        return NodeList(nodeList)

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_TagName(self):
        return self.getTagName()

    def _get_NodeName(self):
        return self.getNodeName()

    def _get_NodeType(self):
        return self.getNodeType()

    def _get_NodeValue(self):
        return self.getNodeValue()

    def _get_ParentNode(self):
        return self.getParentNode()

    def _get_Attribute(self, name):
        return self.getAttribute(name)

    def _get_AttributeNode(self, name):
        return self.getAttributeNode(name)

    def _get_Attributes(self):
        return self.getAttributes()

    def _get_Attribute(self, name):
        return self.getAttribute(name)

    def _get_AttributeNode(self, name):
        return self.getAttributeNode(name)

    def _get_ElementsByTagName(self, tagname):
        return self.getElementsByTagName(tagname)


class NodeList:
    """
    NodeList interface - Provides the abstraction of an ordered
    collection of nodes.

    Python extensions: can use sequence-style 'len', 'getitem', and
    'for..in' constructs.
    """

    def __init__(self,list=None):
        self._data = list or []

    def __getitem__(self, index, type=type, sts=StringTypes):
        return self._data[index]

    def __getslice__(self, i, j):
        return self._data[i:j]

    def item(self, index):
        """
        Returns the index-th item in the collection
        """
        try:  return self._data[index]
        except IndexError: return None

    def getLength(self):
        """
        The length of the NodeList
        """
        return len(self._data)

    __len__=getLength

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_Length(self):
        return self.getLength()

class NamedNodeMap:
    """
    NamedNodeMap interface - Is used to represent collections
    of nodes that can be accessed by name.  NamedNodeMaps are not
    maintained in any particular order.

    Python extensions: can use sequence-style 'len', 'getitem', and
    'for..in' constructs, and mapping-style 'getitem'.
    """

    def __init__(self, data=None):
        if data is None:
            data = {}
        self._data = data

    def item(self, index):
        """
        Returns the index-th item in the map
        """
        try: return self._data.values()[index]
        except IndexError: return None

    def __getitem__(self, key):
        if type(key)==type(1):
            return self._data.values()[key]
        else:
            return self._data[key]

    def getLength(self):
        """
        The length of the NodeList
        """
        return len(self._data)

    __len__ = getLength

    def getNamedItem(self, name):
        """
        Retrieves a node specified by name. Parameters:
        name Name of a node to retrieve. Return Value A Node (of any
        type) with the specified name, or None if the specified name
        did not identify any node in the map.
        """
        if self._data.has_key(name):
            return self._data[name]
        return None

    """
    create aliases for all above functions in the pythony way.
    """
    def _get_Length(self):
        return self.getLength()

    def _get_NamedItem(self, name):
        return self.getNamedItem(name)

class Attr(Node):
    """
    Attr interface - The Attr interface represents an attriubte in an
    Element object. Attr objects inherit the Node Interface
    """

    def __init__(self, name, value, specified=1):
        self.name = name
        self.value = value
        self.specified = specified

    def getNodeName(self):
        """
        The name of this node, depending on its type
        """
        return self.name

    def getName(self):
        """
        Returns the name of this attribute.
        """
        return self.name

    def getNodeValue(self):
        """
        The value of this node, depending on its type
        """
        return self.value

    def getNodeType(self):
        """
        A code representing the type of the node.
        """
        return ATTRIBUTE_NODE

    def getSpecified(self):
        """
        If this attribute was explicitly given a value in the
        original document, this is true; otherwise, it is false.
        """
        return self.specified

    """
    create aliases for all above functions in the pythony way.
    """

    def _get_NodeName(self):
        return self.getNodeName()

    def _get_Name(self):
        return self.getName()

    def _get_NodeValue(self):
        return self.getNodeValue()

    def _get_NodeType(self):
        return self.getNodeType()

    def _get_Specified(self):
        return self.getSpecified()


=== Added File zopeproducts/bugtracker/browser/StructuredText/STNG.txt ===
Using Structured Text

  The goal of StructuredText is to make it possible to express
  structured text using a relatively simple plain text format. Simple
  structures, like bullets or headings are indicated through
  conventions that are natural, for some definition of
  "natural". Hierarchical structures are indicated through
  indentation. The use of indentation to express hierarchical
  structure is inspired by the Python programming language.

  Use of StructuredText consists of one to three logical steps. In the
  first step, a text string is converted to a network of objects using
  the 'StructuredText.Basic' facility, as in the following
  example::

    raw=open("mydocument.txt").read()
    import StructuredText
    st=StructuredText.Basic(raw)

  The output of 'StructuredText.Basic' is simply a
  StructuredTextDocument object containing StructuredTextParagraph
  objects arranged in a hierarchy. Paragraphs are delimited by strings
  of two or more whitespace characters beginning and ending with
  newline characters. Hierarchy is indicated by indentation. The
  indentation of a paragraph is the minimum number of leading spaces
  in a line containing non-white-space characters after converting tab
  characters to spaces (assuming a tab stop every eight characters).

  StructuredTextNode objects support the read-only subset of the
  Document Object Model (DOM) API. It should be possible to process
  'StructuredTextNode' hierarchies using XML tools such as XSLT.

  The second step in using StructuredText is to apply additional
  structuring rules based on text content. A variety of differentText
  rules can be used. Typically, these are used to implement a
  structured text language for producing documents, but any sort of
  structured text language could be implemented in the second
  step. For example, it is possible to use StructuredText to implement
  structured text formats for representing structured data. The second
  step, which could consist of multiple processing steps, is
  performed by processing, or "coloring", the hierarchy of generic
  StructuredTextParagraph objects into a network of more specialized
  objects. Typically, the objects produced should also implement the DOM
  API to allow processing with XML tools.

  A document processor is provided to convert a StructuredTextDocument
  object containing only StructuredStructuredTextParagraph objects
  into a StructuredTextDocument object containing a richer collection
  of objects such as bullets, headings, emphasis, and so on using
  hints in the text. Hints are selected based on conventions of the
  sort typically seen in electronic mail or news-group postings. It
  should be noted, however, that these conventions are somewhat
  culturally dependent, fortunately, the document processor is easily
  customized to implement alternative rules. Here's an example of
  using the DOC processor to convert the output of the previous example::

    doc=StructuredText.Document(st)

  The final step is to process the colored networks produced from the
  second step to produce additional outputs. The final step could be
  performed by Python programs, or by XML tools. A Python outputter is
  provided for the document processor output that produces Hypertext Markup
  Language (HTML) text::

    html=StructuredText.HTML(doc)

Customizing the document processor

  The document processor is driven by two tables. The first table,
  named 'paragraph_types', is a sequence of callable objects or method
  names for coloring paragraphs. If a table entry is a string, then it
  is the name of a method of the document processor to be used. For
  each input paragraph, the objects in the table are called until one
  returns a value (not 'None'). The value returned replaces the
  original input paragraph in the output. If none of the objects in
  the paragraph types table return a value, then a copy of the
  original paragraph is used.  The new object returned by calling a
  paragraph type should implement the ReadOnlyDOM,
  StructuredTextColorizable, and StructuredTextSubparagraphContainer
  interfaces. See the 'Document.py' source file for examples.

  A paragraph type may return a list or tuple of replacement
  paragraphs, this allowing a paragraph to be split into multiple
  paragraphs. 

  The second table, 'text_types', is a sequence of callable objects or
  method names for coloring text. The callable objects in this table
  are used in sequence to transform the input text into new text or
  objects.  The callable objects are passed a string and return
  nothing ('None') or a three-element tuple consisting of:

    - a replacement object,

    - a starting position, and

    - an ending position

  The text from the starting position is (logically) replaced with the
  replacement object. The replacement object is typically an object
  that implements that implements the ReadOnlyDOM, and
  StructuredTextColorizable interfaces. The replacement object can
  also be a string or a list of strings or objects. Replacement is
  done from beginning to end and text after the replacement ending
  position will be passed to the character type objects for processing.

Example: adding wiki links

  We want to add support for Wiki links. A Wiki link is a string of
  text containing mixed-case letters, such that at least two of the
  letters are upper case and such that the first letter is upper case.

  

     

     


=== Added File zopeproducts/bugtracker/browser/StructuredText/STletters.py ===
import string

def punc_func(exclude):
    punc = r''
    for char in string.punctuation:
        if char not in exclude:
            punc = punc + r'\%s' % char
    return punc

digits      = string.digits
letters     = string.letters
literal_punc = punc_func("'")
dbl_quoted_punc = punc_func("\"")
strongem_punc = punc_func('*')
under_punc = punc_func('_<>')
phrase_delimiters = r'\s\.\,\?\/\!\&\(\)'


=== Added File zopeproducts/bugtracker/browser/StructuredText/StructuredText.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

""" Alias module for StructuredTextClassic compatibility which makes
use of StructuredTextNG """


import HTMLClass, DocumentClass 
import DocumentWithImages, HTMLWithImages
from ST import Basic

import re, sys
from STletters import letters

Document = DocumentClass.DocumentClass()
HTMLNG = HTMLClass.HTMLClass()

DocumentImages = DocumentWithImages.DocumentWithImages()
HTMLNGImages = HTMLWithImages.HTMLWithImages()

def HTML(aStructuredString, level=1, header=1):
    st = Basic(aStructuredString)
    doc = DocumentImages(st)
    return HTMLNGImages(doc,header=header,level=level)

def StructuredText(aStructuredString, level=1):
    return HTML(aStructuredString,level)

def html_with_references(text, level=1, header=1):
    text = re.sub(
        r'[\000\n]\.\. \[([0-9_%s-]+)\]' % letters,
        r'\n  <a name="\1">[\1]</a>',
        text)

    text = re.sub(
        r'([\000- ,])\[(?P<ref>[0-9_%s-]+)\]([\000- ,.:])'   % letters,
        r'\1<a href="#\2">[\2]</a>\3',
        text)

    text = re.sub(
        r'([\000- ,])\[([^]]+)\.html\]([\000- ,.:])',
        r'\1<a href="\2.html">[\2]</a>\3',
        text)

    return HTML(text,level=level,header=header)

def html_quote(v,
               character_entities=(
                       (re.compile('&'), '&amp;'),
                       (re.compile("<"), '&lt;' ),
                       (re.compile(">"), '&gt;' ),
                       (re.compile('"'), '&quot;')
                       )): #"
    text=str(v)
    for re,name in character_entities:
        text=re.sub(name,text)
    return text


if __name__=='__main__':
    import getopt

    opts,args = getopt.getopt(sys.argv[1:],'',[])

    for k,v in opts:
        pass


    for f in args:
        print HTML(open(f).read())


=== Added File zopeproducts/bugtracker/browser/StructuredText/__init__.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

import HTMLClass, DocumentClass
import ClassicDocumentClass
from StructuredText import html_with_references, HTML, html_quote
from ST import Basic
import DocBookClass
import HTMLWithImages
from types import StringType, UnicodeType
import DocumentWithImages

ClassicHTML=HTML
HTMLNG=HTMLClass.HTMLClass()

def HTML(src, level=1):
    if isinstance(src, StringType) or isinstance(src, UnicodeType):
        return ClassicHTML(src, level)
    return HTMLNG(src, level)

Classic=ClassicDocumentClass.DocumentClass()
Document=DocumentClass.DocumentClass()
DocumentWithImages=DocumentWithImages.DocumentWithImages()
HTMLWithImages=HTMLWithImages.HTMLWithImages()

DocBookBook=DocBookClass.DocBookBook()
DocBookChapter=DocBookClass.DocBookChapter()
DocBookChapterWithFigures=DocBookClass.DocBookChapterWithFigures()
DocBookArticle=DocBookClass.DocBookArticle()


=== Added File zopeproducts/bugtracker/browser/StructuredText/ts_regex.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Provide a thread-safe interface to regex
"""
import regex, regsub #, Sync
from regex import *
from regsub import split, sub, gsub, splitx, capwords

try:
    import thread
except:
    class allocate_lock:
        def acquire(*args): pass
        def release(*args): pass

else:
    class SafeFunction:
        _l=thread.allocate_lock()
        _a=_l.acquire
        _r=_l.release

        def __init__(self, f):
            self._f=f

        def __call__(self, *args, **kw):
            self._a()
            try: return self._f(*args, **kw)
            finally: self._r()

    split=SafeFunction(split)
    sub=SafeFunction(sub)
    gsub=SafeFunction(gsub)
    splitx=SafeFunction(splitx)
    capwords=SafeFunction(capwords)

    allocate_lock=thread.allocate_lock

class compile:

    _r=None
    groupindex=None

    def __init__(self, *args):
        self._r=r=regex(*compile, **args)
        self._init(r)

    def _init(self, r):
        lock=allocate_lock()
        self.__a=lock.acquire
        self.__r=lock.release
        self.translate=r.translate
        self.givenpat=r.givenpat
        self.realpat=r.realpat

    def match(self, string, pos=0):
        self.__a()
        try: return self._r.match(string, pos)
        finally: self.__r()

    def search(self, string, pos=0):
        self.__a()
        try: return self._r.search(string, pos)
        finally: self.__r()

    def search_group(self, str, group, pos=0):
        """Search a string for a pattern.

        If the pattern was not found, then None is returned,
        otherwise, the location where the pattern was found,
        as well as any specified group are returned.
        """
        self.__a()
        try:
            r=self._r
            l=r.search(str, pos)
            if l < 0: return None
            return l, r.group(*group)
        finally: self.__r()

    def match_group(self, str, group, pos=0):
        """Match a pattern against a string

        If the string does not match the pattern, then None is
        returned, otherwise, the length of the match, as well
        as any specified group are returned.
        """
        self.__a()
        try:
            r=self._r
            l=r.match(str, pos)
            if l < 0: return None
            return l, r.group(*group)
        finally: self.__r()

    def search_regs(self, str, pos=0):
        """Search a string for a pattern.

        If the pattern was not found, then None is returned,
        otherwise, the 'regs' attribute of the expression is
        returned.
        """
        self.__a()
        try:
            r=self._r
            r.search(str, pos)
            return r.regs
        finally: self.__r()

    def match_regs(self, str, pos=0):
        """Match a pattern against a string

        If the string does not match the pattern, then None is
        returned, otherwise, the 'regs' attribute of the expression is
        returned.
        """
        self.__a()
        try:
            r=self._r
            r.match(str, pos)
            return r.regs
        finally: self.__r()

class symcomp(compile):

    def __init__(self, *args):
        self._r=r=regex.symcomp(*args)
        self._init(r)
        self.groupindex=r.groupindex