Logo Search packages:      
Sourcecode: zope-cps version File versions  Download package

StorageAdapter.py

# (C) Copyright 2003 Nuxeo SARL <http://nuxeo.com>
# Author: Florent Guillaume <fg@nuxeo.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# 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.
#
# $Id: StorageAdapter.py 30637 2005-12-14 18:16:55Z fguillaume $

"""
Storage Adapters

A storage adapter is used by a DataModel to get/set the data from/to
somewhere. It is is parameterised on one hand by some context, for
instance an object, and on the other hand by the schema that drives it.

A storage adapter can also be linked to no object, for instance for
object creation.

The most basic implementation is using attributes on an object. A more
complex one can be a storage to an SQL table. Another one could be a
setting of attributes in the ZODB that knows about File and Image fields
and makes them available as subobjects so that they are visible in the
ZMI.

"""

import warnings
from zLOG import LOG, DEBUG, ERROR
from Acquisition import aq_base
from OFS.ObjectManager import ObjectManager
from OFS.SimpleItem import SimpleItem

from Products.CPSSchemas.BasicFields import CPSSubObjectsField
from Products.CPSSchemas.DataModel import DEFAULT_VALUE_MARKER

def _isinstance(ob, cls):
    warnings.warn("_isinstance() is deprecated and will be removed in "
                  "CPS 3.4.1. Use isinstance() instead.",
                  DeprecationWarning, stacklevel=2)
    return isinstance(ob, cls)

00053 class BaseStorageAdapter:
    """Base Storage Adapter

    Base class for storage adapters.
    """

00059     def __init__(self, schema, field_ids=None, **kw):
        """Create a StorageAdapter for a schema.

        If field_ids is specified, only those fields will be managed.
        """
        self._schema = schema
        if field_ids is None:
            field_ids = schema.keys()
        field_items = []
        writable_field_items = []
        for field_id, field in schema.items():
            if field_id in field_ids:
                field_items.append((field_id, field))
                if not field.write_ignore_storage:
                    writable_field_items.append((field_id, field))
        self._field_items = field_items
        self._writable_field_items = writable_field_items

00077     def getContextObject(self):
        """Get the underlying context for this adapter.

        See setContextObject for the semantics.
        """
        return None

00084     def setContextObject(self, context, proxy=None):
        """Set a new underlying context for this adapter.

        If a getData/setData is later done, it will be done on this new
        context.

        This is used by CPS to switch to a writable object after unfreezing.
        Also used by directory entry creation process, to specifiy the
        id after an empty datamodel has been fetched.
        """
        raise NotImplementedError

00096     def getProxy(self):
        """Get the potentially associated proxy for this adapter.
        """
        return None

00101     def getSchema(self):
        """Get schema this adapter is about."""
        return self._schema

00105     def getFieldItems(self):
        """Get the field ids and the fields."""
        return self._field_items

    def getFieldIds(self):
        return [field_id for field_id, field in self.getFieldItems()]

    def getReadableFieldIds(self):
        return [field_id for field_id, field in self.getFieldItems()
                if not field.read_ignore_storage]

00116     def getWritableFieldItems(self):
        """Get the writable field ids and the fields."""
        return self._writable_field_items

00120     def getDefaultData(self):
        """Get the default data from the fields' default values.

        Returns a mapping.
        """
        data = {}
        for field_id in self.getFieldIds():
            data[field_id] = DEFAULT_VALUE_MARKER
        return data

00130     def finalizeDefaults(self, data):
        """This has to be called after getData to finalize default values.
        """
        for field_id, v in data.items():
            if v is DEFAULT_VALUE_MARKER:
                data[field_id] = self._schema[field_id].getDefault()

    #
    # API called by DataModel
    #
00140     def getData(self):
        """Get data from the object, returns a mapping."""
        return self._getData()

00144     def setData(self, data):
        """Set data to the object, from a mapping."""
        self._setData(data)

    #
    # Internal API for subclasses
    #
00151     def _getData(self, **kw):
        """Get data from the object, returns a mapping."""
        data = {}
        for field_id, field in self.getFieldItems():
            if field.read_ignore_storage:
                value = DEFAULT_VALUE_MARKER
            else:
                value = self._getFieldData(field_id, field, **kw)
            data[field_id] = value
        self._getDataDoProcess(data, **kw)
        return data

00163     def _getDataDoProcess(self, data, **kw):
        """Process data after read."""
        for field_id, field in self.getFieldItems():
            value = data[field_id]
            data[field_id] = field.processValueAfterRead(value, data,
                                                         self.getContextObject(),
                                                         self.getProxy())

00171     def _getFieldData(self, field_id, field, **kw):
        """Get data from one field."""
        raise NotImplementedError

00175     def _setData(self, data):
        """Set data to the object, from a mapping."""
        data = self._setDataDoProcess(data)
        for field_id, field in self.getWritableFieldItems():
            if not data.has_key(field_id):
                continue
            self._setFieldData(field_id, data[field_id])

00183     def _setDataDoProcess(self, data):
        """Process data before write.

        Returns a copy, without the fields that are not stored."""
        new_data = {}
        for field_id, field in self.getFieldItems():
            if not data.has_key(field_id):
                continue
            value = data[field_id]
            result = field.processValueBeforeWrite(value, data,
                                                   self.getContextObject(),
                                                   self.getProxy())
            if not field.write_ignore_storage:
                new_data[field_id] = result
        return new_data

00199     def _setFieldData(self, field_id, value):
        """Set data for one field."""
        raise NotImplementedError


00204 class AttributeStorageAdapter(BaseStorageAdapter):
    """Attribute Storage Adapter

    This adapter simply gets and sets data from/to an attribute.
    """

00210     def __init__(self, schema, ob, proxy=None, **kw):
        """Create an Attribute Storage Adapter for a schema.

        The object passed is the one on which to get/set attributes.
        """
        self._ob = ob
        self._proxy = proxy
        BaseStorageAdapter.__init__(self, schema, **kw)

00219     def getContextObject(self):
        """Get the underlying context for this adapter."""
        return self._ob

00223     def setContextObject(self, context, proxy=None):
        """Set a new underlying context for this adapter."""
        self._ob = context
        self._proxy = proxy

00228     def getProxy(self):
        """Get the potentially associated proxy for this adapter."""
        return self._proxy

00232     def _getFieldData(self, field_id, field):
        """Get data from one field."""
        ob = self._ob
        if not hasattr(aq_base(ob), field_id):
            # Use default from field.
            return DEFAULT_VALUE_MARKER
        if isinstance(field, CPSSubObjectsField):
            return field.getFromAttribute(ob, field_id)
        else:
            return getattr(ob, field_id)

00243     def _setFieldData(self, field_id, value):
        """Set data for one field."""
        # No kw arguments are expected.
        ob = self._ob
        field = self._schema[field_id] # XXX should be an arg
        if isinstance(field, CPSSubObjectsField):
            field.setAsAttribute(ob, field_id, value)
        else:

            # If the field is stored as a subobject first delete it.
            if hasattr(aq_base(ob),'objectIds') and field_id in ob.objectIds():
                ob._delObject(field_id)

            # If it is a Zope object, store as subobject and not attribute
            if hasattr(aq_base(value), 'manage_beforeDelete'):
                if hasattr(aq_base(ob), field_id):
                    delattr(ob, field_id)
                ob._setObject(field_id, value)
            else:
                setattr(ob, field_id, value)

    def _getContentUrl(self, object, field_id, file_name):
        return '%s/downloadFile/%s/%s' % (
            object.absolute_url(), field_id, file_name)


ACCESSOR = object()
ACCESSOR_READ_ONLY = object()

00272 class MetaDataStorageAdapter(BaseStorageAdapter):
    """MetaData Storage Adapter

    This adapter simply gets and sets metadata using X() and setX() methods for
    standard CMF Dublin Core methods, or using specific attributes otherwise.
    """

    _field_attributes = {
        'Creator': ACCESSOR_READ_ONLY,
        'CreationDate': ('creation_date', None),
        'Title': ACCESSOR,
        'Subject': ACCESSOR,
        'Description': ACCESSOR,
        'Contributors': ACCESSOR,
        'ModificationDate': ('modification_date', 'setModificationDate'),
        'EffectiveDate': ('effective_date', 'setEffectiveDate'),
        'ExpirationDate': ('expiration_date', 'setExpirationDate'),
        'Format': ACCESSOR,
        'Language': ACCESSOR,
        'Rights': ACCESSOR,
        'Coverage': ACCESSOR,
        'Source': ACCESSOR,
        'Relation': ACCESSOR,
        }

    def __init__(self, schema, ob, proxy=None, **kw):
        self._ob = ob
        self._proxy = proxy
        BaseStorageAdapter.__init__(self, schema, **kw)

00302     def getContextObject(self):
        """Get the underlying context for this adapter."""
        return self._ob

00306     def setContextObject(self, context, proxy=None):
        """Set a new underlying context for this adapter."""
        self._ob = context
        self._proxy = proxy

00311     def getProxy(self):
        """Get the potentially associated proxy for this adapter."""
        return self._proxy

00315     def _getFieldData(self, field_id, field):
        """Get data from one field.

        Calls the getter method.
        """
        ob = self._ob
        if ob is None:
            # Creation
            return DEFAULT_VALUE_MARKER
        attr = self._field_attributes.get(field_id, field_id)
        if attr is ACCESSOR or attr is ACCESSOR_READ_ONLY:
            return getattr(ob, field_id)()
        elif isinstance(attr, tuple):
            return getattr(ob, attr[0])
        elif hasattr(aq_base(ob), attr):
            return getattr(ob, attr)
        else:
            # Use default from field.
            return DEFAULT_VALUE_MARKER

00335     def _setFieldData(self, field_id, value):
        """Set data for one field.

        Calls the setter method.
        """
        # No kw arguments are expected.
        ob = self._ob
        attr = self._field_attributes.get(field_id, field_id)
        if attr is ACCESSOR:
            getattr(ob, 'set' + field_id)(value)
        elif attr is ACCESSOR_READ_ONLY:
            raise ValueError("Field %s is read-only" % field_id)
        elif isinstance(attr, tuple):
            if attr[1] is None:
                raise ValueError("Field %s is read-only" % field_id)
            getattr(ob, attr[1])(value)
        else:
            setattr(ob, attr, value)


00355 class MappingStorageAdapter(BaseStorageAdapter):
    """Mapping or dictionnary Storage Adapter

    This adapter simply store data into a dictionnary.
    """

00361     def __init__(self, schema, ob, **kw):
        """Create an Attribute Storage Adapter for a schema.

        The object passed is the one on which to get/set attributes.
        """
        self._ob = ob
        BaseStorageAdapter.__init__(self, schema, **kw)

00369     def getContextObject(self):
        """Get the underlying context for this adapter."""
        return self._ob

00373     def setContextObject(self, context, proxy=None):
        """Set a new underlying context for this adapter."""
        pass

00377     def _getFieldData(self, field_id, field):
        """Get data from one field."""
        return self._ob.get(field_id, DEFAULT_VALUE_MARKER)

00381     def _setFieldData(self, field_id, value):
        """Set data for one field."""
        # No kw arguments are expected.
        self._ob.update({field_id : value})

Generated by  Doxygen 1.6.0   Back to index