171 lines
5.1 KiB
Python
171 lines
5.1 KiB
Python
#
|
|
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
|
|
"""
|
|
A simple generic, general-purpose link management framework.
|
|
|
|
$Id$
|
|
"""
|
|
|
|
from BTrees.IFBTree import intersection, union
|
|
from BTrees.IOBTree import IOBTree
|
|
from persistent import Persistent
|
|
from zope import component
|
|
from zope.component import adapts
|
|
from zope.index.field import FieldIndex
|
|
from zope.interface import implements
|
|
from zope.intid.interfaces import IIntIds
|
|
|
|
from cybertools.link.interfaces import ILinkManager, ILink
|
|
|
|
|
|
class LinkManager(Persistent):
|
|
""" A manager (storage, registry) for link objects.
|
|
"""
|
|
|
|
implements(ILinkManager)
|
|
|
|
uid = int
|
|
|
|
# initialization
|
|
|
|
def __init__(self):
|
|
self.links = IOBTree()
|
|
self.setupIndexes()
|
|
self.currentId = 0
|
|
|
|
def setupIndexes(self):
|
|
self.indexes = dict(
|
|
name=FieldIndex(),
|
|
source=FieldIndex(),
|
|
target=FieldIndex())
|
|
|
|
# public methods
|
|
|
|
def createLink(self, **kw):
|
|
if not 'name' in kw or not 'source' in kw:
|
|
raise ValueError("At least 'name' and 'source' attributes must "
|
|
"be given.")
|
|
identifier = self.generateIdentifier()
|
|
link = Link(self, identifier)
|
|
link.update(**kw)
|
|
self.getLinks()[identifier] = link
|
|
self.indexLink(link)
|
|
return link
|
|
|
|
def removeLink(self, link):
|
|
del self.getLinks()[link.identifier]
|
|
|
|
def getLink(self, identifier):
|
|
return self.getLinks()[identifier]
|
|
|
|
def query(self, name=None, source=None, target=None, **kw):
|
|
source = self.getUniqueId(source)
|
|
target = self.getUniqueId(target)
|
|
r = None
|
|
if name is not None:
|
|
r = self.indexes['name'].apply((name, name))
|
|
if source is not None:
|
|
r = self.intersect(r, self.indexes['source'].apply((source, source)))
|
|
if target is not None:
|
|
r = self.intersect(r, self.indexes['target'].apply((target, target)))
|
|
if r is None:
|
|
raise ValueError("At least one critera of 'name', 'source', or "
|
|
"'target' must be given.")
|
|
result = [self.getLink(id) for id in r]
|
|
#for k, v in kw:
|
|
# result = [link for link in result if getattr(link, k) == v]
|
|
return sorted(result, key=lambda x: (x.order, x.name))
|
|
|
|
def __iter__(self):
|
|
return self.getLinks().values()
|
|
|
|
# protected methods
|
|
|
|
def getLinks(self):
|
|
return self.links
|
|
|
|
def generateIdentifier(self):
|
|
self.currentId += 1
|
|
return self.currentId
|
|
|
|
def indexLink(self, link):
|
|
for attr, idx in self.indexes.items():
|
|
value = getattr(link, attr)
|
|
if value is None:
|
|
idx.unindex_doc(link.identifier)
|
|
else:
|
|
idx.index_doc(link.identifier, value)
|
|
|
|
def intersect(self, r1, r2):
|
|
return r1 is None and r2 or intersection(r1, r2)
|
|
|
|
def getUniqueId(self, obj):
|
|
if obj is None:
|
|
return None
|
|
if isinstance(obj, self.uid):
|
|
return obj
|
|
# TODO: take external objects (referenced by URIs) in to account
|
|
return component.getUtility(IIntIds).getId(obj)
|
|
|
|
def getObject(self, uid):
|
|
return component.getUtility(IIntIds).getObject(uid)
|
|
|
|
|
|
class Link(Persistent):
|
|
""" A basic link implementation.
|
|
"""
|
|
|
|
implements(ILink)
|
|
|
|
defaults = dict(target=None,
|
|
linkType=u'link',
|
|
state=u'valid',
|
|
relevance=1.0,
|
|
order=0,)
|
|
|
|
def __init__(self, manager, identifier):
|
|
self.manager = self.__parent__ = manager
|
|
self.identifier = self.__name__ = identifier
|
|
|
|
def getManager(self):
|
|
return self.manager
|
|
|
|
def getSource(self):
|
|
return self.getManager().getObject(self.source)
|
|
|
|
def getTarget(self):
|
|
return self.getManager().getObject(self.target)
|
|
|
|
def update(self, **kw):
|
|
manager = self.getManager()
|
|
for k in ('source', 'target'):
|
|
if k in kw:
|
|
kw[k] = manager.getUniqueId(kw[k])
|
|
for k, v in kw.items():
|
|
setattr(self, k, v)
|
|
for k in manager.indexes:
|
|
if k in kw:
|
|
manager.indexLink(self)
|
|
break
|
|
|
|
def __getattr__(self, attr):
|
|
if attr not in ILink:
|
|
raise AttributeError(attr)
|
|
value = self.defaults.get(attr, u'')
|
|
return value
|