docs: def identify_and_tag_URLs(line):
      """Given a reference line, identify URLs in the line, record the
         information about them, and replace them with a "<cds.URL />" tag.
         URLs are identified in 2 forms:
          + Raw: http://invenio-software.org/
          + HTML marked-up: <a href="http://invenio-software.org/">CERN Document
            Server Software Consortium</a>
         These URLs are considered to have 2 components: The URL itself
         (url string); and the URL description. The description is effectively
         the text used for the created Hyperlink when the URL is marked-up
         in HTML. When an HTML marked-up URL has been recognised, the text
         between the anchor tags is therefore taken as the URL description.
         In the case of a raw URL recognition, however, the URL itself will
         also be used as the URL description.
         For example, in the following reference line:
          [1] See <a href="http://invenio-software.org/">CERN Document Server
          Software Consortium</a>.
         ...the URL string will be "http://invenio-software.org/" and the URL
         description will be
         "CERN Document Server Software Consortium".
         The line returned from this function will be:
          [1] See <cds.URL />
         In the following line, however:
          [1] See http //invenio-software.org/ for more details.
         ...the URL string will be "http://invenio-software.org/" and the URL
         description will also be "http://invenio-software.org/".
         The line returned will be:
          [1] See <cds.URL /> for more details.
  
         @param line: (string) the reference line in which to search for URLs.
         @return: (tuple) - containing 2 items:
          + the line after URLs have been recognised and removed;
          + a list of 2-item tuples where each tuple represents a recognised URL
            and its description:
              [(url, url-description), (url, url-description), ... ]
         @Exceptions raised:
          + an IndexError if there is a problem with the number of URLs
            recognised (this should not happen.)
      """
      # Take a copy of the line:
      line_pre_url_check = line
      # Dictionaries to record details of matched URLs:
      found_url_full_matchlen = {}
      found_url_urlstring = {}
      found_url_urldescr = {}
  
      # List to contain details of all matched URLs:
      identified_urls = []
  
      # Attempt to identify and tag all HTML-MARKED-UP URLs in the line:
      m_tagged_url_iter = re_html_tagged_url.finditer(line)
      for m_tagged_url in m_tagged_url_iter:
          startposn = m_tagged_url.start()        # start position of matched URL
          endposn = m_tagged_url.end()          # end position of matched URL
          matchlen = len(m_tagged_url.group(0))  # total length of URL match
  
          found_url_full_matchlen[startposn] = matchlen
          found_url_urlstring[startposn] = m_tagged_url.group('url')
          found_url_urldescr[startposn] = m_tagged_url.group('desc')
          # temporarily replace the URL match with underscores so that
          # it won't be re-found
          line = line[0:startposn] + u"_" * matchlen + line[endposn:]
  
      # Attempt to identify and tag all RAW (i.e. not
      # HTML-marked-up) URLs in the line:
      m_raw_url_iter = re_raw_url.finditer(line)
      for m_raw_url in m_raw_url_iter:
          startposn = m_raw_url.start()        # start position of matched URL
          endposn = m_raw_url.end()          # end position of matched URL
          matchlen = len(m_raw_url.group(0))  # total length of URL match
          matched_url = m_raw_url.group('url')
  
          if len(matched_url) > 0 and matched_url[-1] in (".", ","):
              # Strip the full-stop or comma from the end of the url:
              matched_url = matched_url[:-1]
  
          found_url_full_matchlen[startposn] = matchlen
          found_url_urlstring[startposn] = matched_url
          found_url_urldescr[startposn] = matched_url
          # temporarily replace the URL match with underscores
          # so that it won't be re-found
          line = line[0:startposn] + u"_" * matchlen + line[endposn:]
  
      # Now that all URLs have been identified, insert them
      # back into the line, tagged:
      found_url_positions = found_url_urlstring.keys()
      found_url_positions.sort()
      found_url_positions.reverse()
      for url_position in found_url_positions:
          line = line[0:url_position] + "<cds.URL />" \
              + line[url_position + found_url_full_matchlen[url_position]:]
  
      # The line has been rebuilt. Now record the information about the
      # matched URLs:
      found_url_positions = found_url_urlstring.keys()
      found_url_positions.sort()
      for url_position in found_url_positions:
          identified_urls.append((found_url_urlstring[url_position],
                                  found_url_urldescr[url_position]))
  
      # Somehow the number of URLs found doesn't match the number of
      # URLs recorded in "identified_urls". Raise an IndexError.
      msg = """Error: The number of URLs found in the reference line """ \
            """does not match the number of URLs recorded in the """ \
            """list of identified URLs!\nLine pre-URL checking: %s\n""" \
            """Line post-URL checking: %s\n""" \
            % (line_pre_url_check, line)
      assert len(identified_urls) == len(found_url_positions), msg
  
      # return the line containing the tagged URLs:
      return line, identified_urls