#!/www/python/bin/python

"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/tools/zodb_referrers.py $
$Id: zodb_referrers.py 22951 2003-11-03 00:08:31Z dbinger $

Find and print references to a ZODB object specified by OID.
"""

from dulcinea.script_util import Option, OptionParser, run
from dulcinea.database import pack_oid, format_oid

import sys
import ZODB
import cPickle
import re

def create_index(db):
    index_db = {} # { oid : [refers] }
    storage = db.get_storage()
    # this is fast but may return false positives (i.e. it may find references
    # that don't exist).  It can also return false negatives if the DB has
    # used up more than 4e9 OIDs.
    oid_pat = re.compile(r"U\x08(\x00\x00\x00\x00....)")
    for oid in storage._index.keys():
        pickle_data = storage.load(oid, '')[0]
        for r_oid in oid_pat.findall(pickle_data):
            oids = index_db.setdefault(r_oid, [])
            if oid not in oids:
                oids.append(oid)
    return index_db


def main(db, default_dbspec):

    usage = "usage: %prog [options] [index file]"
    option_list = [
        Option("-c", "--create-index",
               action="store_true", dest="create_index",
               help="create the index file before proceeding"),
        Option("-d", "--database",
               type="string", dest="database",
               help="database specification (e.g. file:db.fs)"),
        ]
    parser = OptionParser(usage, option_list)
    (options, args) = parser.parse_args()

    if len(args) != 1:
        parser.error("you must provide one argument")

    db.open(options.database or default_dbspec)

    if options.create_index:
        if not hasattr(db.get_storage(), "_index"):
            raise SystemExit, "must use FileStorage when creating index"
        index_db = create_index(db)
        cPickle.dump(index_db, open(args[0], "w"), 1)
    else:
        try:
            index_file = open(args[0])
        except IOError:
            parser.error("index file does not exist -- run with -c to create")
        index_db = cPickle.load(index_file)


    connection = db.get_connection()

    while 1:
        try:
            input = raw_input("Enter an OID (or blank line to quit): ").strip()
        except EOFError:
            break
        if not input:
            break
        try:
            if input.startswith("00"):
                # DulcineaBase.__repr__ format
                oid = pack_oid(long(input, 16))
            elif input[0] in "'\"":
                # packed OID as quoted string
                oid = eval(input)
            else:
                raise ValueError
        except ValueError:
            print "Invalid OID: %r" % input
            continue

        def format_object(object):
            name = " " + object.__class__.__name__
            if hasattr(object, "id"):
                name += " (%s)" % object.id
            return "%s%s" % (format_oid(object._p_oid), name)

        seen = {} # {oid : 1}

        def report_references(oids, indent='  '):
            for oid in oids:
                if oid not in seen:
                    seen[oid] = 1
                    object = connection[oid]
                    sys.stdout.write("%s%s\n" % (indent, format_object(object)))
                    #report_references(index_db.get(oid, []), indent+'  ')
        print format_object(connection[oid])
        report_references(index_db.get(oid, []))


if __name__ == "__main__":
    run()
