Get any datastream on this object as a :class:`DatastreamObject`
        **or** add a new datastream.  If the datastream id corresponds
        to a predefined datastream, the configured object will be returned
        and the datastream object will be returned.  If type is not
        specified for an existing datastream, attempts to infer the
        appropriate subclass of datastream object to return based on the
        mimetype (for XML and RELS-EXT).

        Note that if you use this method to add new datastreams you should
        be sure to set all datastream metadata appropriately for your content
        (i.e., label, mimetype, control group, etc).

        :param dsid: datastream id
        :param dsobj_type: optional :class:`DatastreamObject` type to
            be returned
        :param as_of_date: optional datetime, used to load a historical
            version of the requested datastream

def getDatastreamObject(self, dsid, dsobj_type=None, as_of_date=None):
        '''Get any datastream on this object as a :class:`DatastreamObject`
        **or** add a new datastream.  If the datastream id corresponds
        to a predefined datastream, the configured object will be returned
        and the datastream object will be returned.  If type is not
        specified for an existing datastream, attempts to infer the
        appropriate subclass of datastream object to return based on the
        mimetype (for XML and RELS-EXT).

        Note that if you use this method to add new datastreams you should
        be sure to set all datastream metadata appropriately for your content
        (i.e., label, mimetype, control group, etc).

        :param dsid: datastream id
        :param dsobj_type: optional :class:`DatastreamObject` type to
            be returned
        :param as_of_date: optional datetime, used to load a historical
            version of the requested datastream
        '''

        # NOTE: disabling this option because it returns a Datastream instead of
        # a DatastreamObject, which is unexpected
        # if the requested datastream is a defined datastream, return it
        # if dsid in self._defined_datastreams:
        #   return self._defined_datastreams[dsid]

        if dsid in self._adhoc_datastreams:
            return self._adhoc_datastreams[dsid]

        if dsid in self.ds_list:
            ds_info = self.ds_list[dsid]
            # FIXME: can we take advantage of Datastream descriptor? or at least use dscache ?

            if dsobj_type is None:
                # if datastream mimetype matches one of our base datastream objects, use it

                # special case: rels-ext should always be loaded as rdf
                if ds_info.mimeType == RdfDatastreamObject.default_mimetype \
                  or dsid == 'RELS-EXT':
                    dsobj_type = RdfDatastreamObject
                elif ds_info.mimeType == XmlDatastreamObject.default_mimetype:
                    dsobj_type = XmlDatastreamObject
                else:
                    # default to base datastream object class
                    dsobj_type = DatastreamObject

            dsobj = dsobj_type(self, dsid, label=ds_info.label,
                               mimetype=ds_info.mimeType, as_of_date=as_of_date)

            # add to dscache so modifications will be saved on existing object
            self.dscache[dsid] = dsobj
            setattr(self, dsid, dsobj)

            return dsobj

        else:
            if dsobj_type is None:
                dsobj_type = DatastreamObject
            logger.info('Adding new datastream %s to %r', dsid, self)
            # NOTE: label is required to initialize a new datastream object;
            # using dsid as label since we don't have anything else.
            dsobj = dsobj_type(self, dsid, dsid)
            # add to the adhoc datastreams so it will get ingested to fedora (if new)
            self._adhoc_datastreams[dsid] = dsobj
            # make available like a defined datastream object so ingest will work
            setattr(self, dsid, dsobj)
            # add to dscache so new datastream will be saved on existing object
            self.dscache[dsid] = dsobj
            # default modification logic should be appropriate here since the
            # calling program should be setting content, label, etc

            return dsobj