Panda3D
 All Classes Functions Variables Enumerations
typedWritable.cxx
00001 // Filename: typedWritable.cxx
00002 // Created by:  jason (08Jun00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "typedWritable.h"
00016 #include "bamWriter.h"
00017 #include "bamReader.h"
00018 #include "datagramOutputFile.h"
00019 #include "datagramInputFile.h"
00020 #include "lightMutexHolder.h"
00021 #include "bam.h"
00022 
00023 LightMutex TypedWritable::_bam_writers_lock;
00024 
00025 TypeHandle TypedWritable::_type_handle;
00026 TypedWritable* const TypedWritable::Null = (TypedWritable*)0L;
00027 
00028 #ifdef HAVE_PYTHON
00029 #include "py_panda.h"  
00030 #ifndef CPPPARSER
00031 extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter;
00032 #endif  // CPPPARSER
00033 #endif  // HAVE_PYTHON
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: TypedWritable::Destructor
00037 //       Access: Public, Virtual
00038 //  Description:
00039 ////////////////////////////////////////////////////////////////////
00040 TypedWritable::
00041 ~TypedWritable() {
00042   // Remove the object pointer from the BamWriters that reference it.
00043   if (_bam_writers != (BamWriters *)NULL) {
00044     BamWriters temp;
00045     {
00046       LightMutexHolder holder(_bam_writers_lock);
00047       _bam_writers->swap(temp);
00048       delete _bam_writers;
00049       _bam_writers = NULL;
00050     }
00051     BamWriters::iterator wi;
00052     for (wi = temp.begin(); wi != temp.end(); ++wi) {
00053       BamWriter *writer = (*wi);
00054       writer->object_destructs(this);
00055     }
00056   }
00057 }
00058 
00059 ////////////////////////////////////////////////////////////////////
00060 //     Function: TypedWritable::write_datagram
00061 //       Access: Public, Virtual
00062 //  Description: Writes the contents of this object to the datagram
00063 //               for shipping out to a Bam file.
00064 ////////////////////////////////////////////////////////////////////
00065 void TypedWritable::
00066 write_datagram(BamWriter *, Datagram &) {
00067 }
00068 
00069 ////////////////////////////////////////////////////////////////////
00070 //     Function: TypedWritable::update_bam_nested
00071 //       Access: Public, Virtual
00072 //  Description: Called by the BamWriter when this object has not
00073 //               itself been modified recently, but it should check
00074 //               its nested objects for updates.
00075 ////////////////////////////////////////////////////////////////////
00076 void TypedWritable::
00077 update_bam_nested(BamWriter *) {
00078 }
00079 
00080 ////////////////////////////////////////////////////////////////////
00081 //     Function: TypedWritable::complete_pointers
00082 //       Access: Public, Virtual
00083 //  Description: Receives an array of pointers, one for each time
00084 //               manager->read_pointer() was called in fillin().
00085 //               Returns the number of pointers processed.
00086 //
00087 //               This is the callback function that is made by the
00088 //               BamReader at some later point, after all of the
00089 //               required pointers have been filled in.  It is
00090 //               necessary because there might be forward references
00091 //               in a bam file; when we call read_pointer() in
00092 //               fillin(), the object may not have been read from the
00093 //               file yet, so we do not have a pointer available at
00094 //               that time.  Thus, instead of returning a pointer,
00095 //               read_pointer() simply reserves a later callback.
00096 //               This function provides that callback.  The calling
00097 //               object is responsible for keeping track of the number
00098 //               of times it called read_pointer() and extracting the
00099 //               same number of pointers out of the supplied vector,
00100 //               and storing them appropriately within the object.
00101 ////////////////////////////////////////////////////////////////////
00102 int TypedWritable::
00103 complete_pointers(TypedWritable **, BamReader *) {
00104   return 0;
00105 }
00106 
00107 ////////////////////////////////////////////////////////////////////
00108 //     Function: TypedWritable::require_fully_complete
00109 //       Access: Public, Virtual
00110 //  Description: Some objects require all of their nested pointers to
00111 //               have been completed before the objects themselves can
00112 //               be completed.  If this is the case, override this
00113 //               method to return true, and be careful with circular
00114 //               references (which would make the object unreadable
00115 //               from a bam file).
00116 ////////////////////////////////////////////////////////////////////
00117 bool TypedWritable::
00118 require_fully_complete() const {
00119   return false;
00120 }
00121 
00122 ////////////////////////////////////////////////////////////////////
00123 //     Function: TypedWritable::fillin
00124 //       Access: Public, Virtual
00125 //  Description: This internal function is intended to be called by
00126 //               each class's make_from_bam() method to read in all of
00127 //               the relevant data from the BamFile for the new
00128 //               object.  It is also called directly by the BamReader
00129 //               to re-read the data for an object that has been
00130 //               placed on the stream for an update.
00131 ////////////////////////////////////////////////////////////////////
00132 void TypedWritable::
00133 fillin(DatagramIterator &, BamReader *) {
00134 }
00135 
00136 
00137 ////////////////////////////////////////////////////////////////////
00138 //     Function: TypedWritable::finalize
00139 //       Access: Public, Virtual
00140 //  Description: Called by the BamReader to perform any final actions
00141 //               needed for setting up the object after all objects
00142 //               have been read and all pointers have been completed.
00143 ////////////////////////////////////////////////////////////////////
00144 void TypedWritable::
00145 finalize(BamReader *) {
00146 }
00147 
00148 ////////////////////////////////////////////////////////////////////
00149 //     Function: TypedWritable::as_reference_count
00150 //       Access: Public, Virtual
00151 //  Description: Returns the pointer cast to a ReferenceCount pointer,
00152 //               if it is in fact of that type.
00153 ////////////////////////////////////////////////////////////////////
00154 ReferenceCount *TypedWritable::
00155 as_reference_count() {
00156   return NULL;
00157 }
00158 
00159 #ifdef HAVE_PYTHON
00160 ////////////////////////////////////////////////////////////////////
00161 //     Function: TypedWritable::__reduce__
00162 //       Access: Published
00163 //  Description: This special Python method is implement to provide
00164 //               support for the pickle module.
00165 //
00166 //               This hooks into the native pickle and cPickle
00167 //               modules, but it cannot properly handle
00168 //               self-referential BAM objects.
00169 ////////////////////////////////////////////////////////////////////
00170 PyObject *TypedWritable::
00171 __reduce__(PyObject *self) const {
00172   return __reduce_persist__(self, NULL);
00173 }
00174 #endif  // HAVE_PYTHON
00175 
00176 #ifdef HAVE_PYTHON
00177 ////////////////////////////////////////////////////////////////////
00178 //     Function: TypedWritable::__reduce_persist__
00179 //       Access: Published
00180 //  Description: This special Python method is implement to provide
00181 //               support for the pickle module.
00182 //
00183 //               This is similar to __reduce__, but it provides
00184 //               additional support for the missing persistent-state
00185 //               object needed to properly support self-referential
00186 //               BAM objects written to the pickle stream.  This hooks
00187 //               into the pickle and cPickle modules implemented in
00188 //               direct/src/stdpy.
00189 ////////////////////////////////////////////////////////////////////
00190 PyObject *TypedWritable::
00191 __reduce_persist__(PyObject *self, PyObject *pickler) const {
00192   // We should return at least a 2-tuple, (Class, (args)): the
00193   // necessary class object whose constructor we should call
00194   // (e.g. this), and the arguments necessary to reconstruct this
00195   // object.
00196 
00197   // Check that we have a decode_from_bam_stream python method.  If not,
00198   // we can't use this interface.
00199   PyObject *method = PyObject_GetAttrString(self, "decode_from_bam_stream");
00200   if (method == NULL) {
00201     ostringstream stream;
00202     stream << "Cannot pickle objects of type " << get_type() << "\n";
00203     string message = stream.str();
00204     PyErr_SetString(PyExc_TypeError, message.c_str());
00205     return NULL;
00206   }
00207   Py_DECREF(method);
00208 
00209   BamWriter *writer = NULL;
00210   if (pickler != NULL) {
00211     PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
00212     if (py_writer == NULL) {
00213       // It's OK if there's no bamWriter.
00214       PyErr_Clear();
00215     } else {
00216       DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer);
00217       Py_DECREF(py_writer);
00218     }
00219   }
00220 
00221   // First, streamify the object, if possible.
00222   string bam_stream;
00223   if (!encode_to_bam_stream(bam_stream, writer)) {
00224     ostringstream stream;
00225     stream << "Could not bamify object of type " << get_type() << "\n";
00226     string message = stream.str();
00227     PyErr_SetString(PyExc_TypeError, message.c_str());
00228     return NULL;
00229   }
00230 
00231   // Start by getting this class object.
00232   PyObject *this_class = PyObject_Type(self);
00233   if (this_class == NULL) {
00234     return NULL;
00235   }
00236 
00237   PyObject *func;
00238   if (writer != NULL) {
00239     // The modified pickle support: call the "persistent" version of
00240     // this function, which receives the unpickler itself as an
00241     // additional parameter.
00242     func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream_persist");
00243     if (func == NULL) {
00244       PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream_persist()");
00245       Py_DECREF(this_class);
00246       return NULL;
00247     }
00248 
00249   } else {
00250     // The traditional pickle support: call the non-persistent version
00251     // of this function.
00252 
00253     func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream");
00254     if (func == NULL) {
00255       PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream()");
00256       Py_DECREF(this_class);
00257       return NULL;
00258     }
00259   }
00260 
00261   PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size());
00262   Py_DECREF(func);
00263   Py_DECREF(this_class);
00264   return result;
00265 }
00266 #endif  // HAVE_PYTHON
00267 
00268 ////////////////////////////////////////////////////////////////////
00269 //     Function: TypedWritable::encode_to_bam_stream
00270 //       Access: Published
00271 //  Description: Converts the TypedWritable object into a single
00272 //               stream of data using a BamWriter, and stores that
00273 //               data in the indicated string.  Returns true on
00274 //               success, false on failure.
00275 //
00276 //               This is a convenience method particularly useful for
00277 //               cases when you are only serializing a single object.
00278 //               If you have many objects to process, it is more
00279 //               efficient to use the same BamWriter to serialize all
00280 //               of them together.
00281 ////////////////////////////////////////////////////////////////////
00282 bool TypedWritable::
00283 encode_to_bam_stream(string &data, BamWriter *writer) const {
00284   data.clear();
00285   ostringstream stream;
00286 
00287   // We use nested scoping to ensure the destructors get called in the
00288   // right order.
00289   {
00290     DatagramOutputFile dout;
00291     if (!dout.open(stream)) {
00292       return false;
00293     }
00294     
00295     if (writer == NULL) {
00296       // Create our own writer.
00297     
00298       if (!dout.write_header(_bam_header)) {
00299         return false;
00300       }
00301 
00302       BamWriter writer(&dout);
00303       if (!writer.init()) {
00304         return false;
00305       }
00306       
00307       if (!writer.write_object(this)) {
00308         return false;
00309       }
00310     } else {
00311       // Use the existing writer.
00312       writer->set_target(&dout);
00313       bool result = writer->write_object(this);
00314       writer->set_target(NULL);
00315       if (!result) {
00316         return false;
00317       }
00318     }
00319   }
00320 
00321   data = stream.str();
00322   return true;
00323 }
00324 
00325 ////////////////////////////////////////////////////////////////////
00326 //     Function: TypedWritable::decode_raw_from_bam_stream
00327 //       Access: Published, Static
00328 //  Description: Reads the string created by a previous call to
00329 //               encode_to_bam_stream(), and extracts the single
00330 //               object on that string.  Returns true on success,
00331 //               false on on error.
00332 //
00333 //               This variant sets the TypedWritable and
00334 //               ReferenceCount pointers separately; both are pointers
00335 //               to the same object.  The reference count is not
00336 //               incremented; it is the caller's responsibility to
00337 //               manage the reference count.
00338 //
00339 //               Note that this method cannot be used to retrieve
00340 //               objects that do not inherit from ReferenceCount,
00341 //               because these objects cannot persist beyond the
00342 //               lifetime of the BamReader that reads them.  To
00343 //               retrieve these objects from a bam stream, you must
00344 //               construct a BamReader directly.
00345 //
00346 //               If you happen to know that the particular object in
00347 //               question inherits from TypedWritableReferenceCount or
00348 //               PandaNode, consider calling the variant of
00349 //               decode_from_bam_stream() defined for those methods,
00350 //               which presents a simpler interface.
00351 ////////////////////////////////////////////////////////////////////
00352 bool TypedWritable::
00353 decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr,
00354                            const string &data, BamReader *reader) {
00355   istringstream stream(data);
00356 
00357   DatagramInputFile din;
00358   if (!din.open(stream)) {
00359     return false;
00360   }
00361 
00362   if (reader == NULL) {
00363     // Create a local reader.
00364   
00365     string head;
00366     if (!din.read_header(head, _bam_header.size())) {
00367       return false;
00368     }
00369     
00370     if (head != _bam_header) {
00371       return false;
00372     }
00373 
00374     BamReader reader(&din);
00375     if (!reader.init()) {
00376       return false;
00377     }
00378     
00379     if (!reader.read_object(ptr, ref_ptr)) {
00380       return false;
00381     }
00382     
00383     if (!reader.resolve()) {
00384       return false;
00385     }
00386     
00387     if (ref_ptr == NULL) {
00388       // Can't support non-reference-counted objects.
00389       return false;
00390     }
00391 
00392     // Protect the pointer from accidental deletion when the BamReader
00393     // goes away.
00394     ref_ptr->ref();
00395 
00396   } else {
00397     // Use the existing reader.
00398     reader->set_source(&din);
00399     if (!reader->read_object(ptr, ref_ptr)) {
00400       reader->set_source(NULL);
00401       return false;
00402     }
00403     
00404     if (!reader->resolve()) {
00405       reader->set_source(NULL);
00406       return false;
00407     }
00408     
00409     if (ref_ptr == NULL) {
00410       // Can't support non-reference-counted objects.
00411       reader->set_source(NULL);
00412       return false;
00413     }
00414 
00415     // This BamReader isn't going away, but we have to balance the
00416     // unref() below.
00417     ref_ptr->ref();
00418     reader->set_source(NULL);
00419   }
00420 
00421 
00422   // Now decrement the ref count, without deleting the object.  This
00423   // may reduce the reference count to zero, but that's OK--we trust
00424   // the caller to manage the reference count from this point on.
00425   ref_ptr->unref();
00426   return true;
00427 }
00428 
00429 #ifdef HAVE_PYTHON
00430 ////////////////////////////////////////////////////////////////////
00431 //     Function: TypedWritable::find_global_decode
00432 //       Access: Public, Static
00433 //  Description: This is a support function for __reduce__().  It
00434 //               searches for the global function
00435 //               py_decode_TypedWritable_from_bam_stream() in this
00436 //               class's module, or in the module for any base class.
00437 //               (It's really looking for the libpanda module, but we
00438 //               can't be sure what name that module was loaded under,
00439 //               so we search upwards this way.)
00440 //
00441 //               Returns: new reference on success, or NULL on failure.
00442 ////////////////////////////////////////////////////////////////////
00443 PyObject *TypedWritable::
00444 find_global_decode(PyObject *this_class, const char *func_name) {
00445   PyObject *module_name = PyObject_GetAttrString(this_class, "__module__");
00446   if (module_name != NULL) {
00447     // borrowed reference
00448     PyObject *sys_modules = PyImport_GetModuleDict();
00449     if (sys_modules != NULL) {
00450       // borrowed reference
00451       PyObject *module = PyDict_GetItem(sys_modules, module_name);
00452       if (module != NULL){ 
00453         PyObject *func = PyObject_GetAttrString(module, (char *)func_name);
00454         if (func != NULL) {
00455           Py_DECREF(module_name);
00456           return func;
00457         }
00458       }
00459     }
00460   }
00461   Py_DECREF(module_name);
00462 
00463   PyObject *bases = PyObject_GetAttrString(this_class, "__bases__");
00464   if (bases != NULL) {
00465     if (PySequence_Check(bases)) {
00466       Py_ssize_t size = PySequence_Size(bases);
00467       for (Py_ssize_t i = 0; i < size; ++i) {
00468         PyObject *base = PySequence_GetItem(bases, i);
00469         if (base != NULL) {
00470           PyObject *func = find_global_decode(base, func_name);
00471           Py_DECREF(base);
00472           if (func != NULL) {
00473             Py_DECREF(bases);
00474             return func;
00475           }
00476         }
00477       }
00478     }
00479     Py_DECREF(bases);
00480   }
00481 
00482   return NULL;
00483 }
00484 #endif  // HAVE_PYTHON
00485 
00486 #ifdef HAVE_PYTHON
00487 ////////////////////////////////////////////////////////////////////
00488 //     Function: py_decode_TypedWritable_from_bam_stream
00489 //       Access: Published
00490 //  Description: This wrapper is defined as a global function to suit
00491 //               pickle's needs.
00492 //
00493 //               This hooks into the native pickle and cPickle
00494 //               modules, but it cannot properly handle
00495 //               self-referential BAM objects.
00496 ////////////////////////////////////////////////////////////////////
00497 PyObject *
00498 py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) {
00499   return py_decode_TypedWritable_from_bam_stream_persist(NULL, this_class, data);
00500 }
00501 #endif  // HAVE_PYTHON
00502 
00503 
00504 #ifdef HAVE_PYTHON
00505 ////////////////////////////////////////////////////////////////////
00506 //     Function: py_decode_TypedWritable_from_bam_stream_persist
00507 //       Access: Published
00508 //  Description: This wrapper is defined as a global function to suit
00509 //               pickle's needs.
00510 //
00511 //               This is similar to
00512 //               py_decode_TypedWritable_from_bam_stream, but it
00513 //               provides additional support for the missing
00514 //               persistent-state object needed to properly support
00515 //               self-referential BAM objects written to the pickle
00516 //               stream.  This hooks into the pickle and cPickle
00517 //               modules implemented in direct/src/stdpy.
00518 ////////////////////////////////////////////////////////////////////
00519 PyObject *
00520 py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *this_class, const string &data) {
00521 
00522   PyObject *py_reader = NULL;
00523   if (pickler != NULL) {
00524     py_reader = PyObject_GetAttrString(pickler, "bamReader");
00525     if (py_reader == NULL) {
00526       // It's OK if there's no bamReader.
00527       PyErr_Clear();
00528     }
00529   }
00530 
00531   // We need the function PandaNode::decode_from_bam_stream or
00532   // TypedWritableReferenceCount::decode_from_bam_stream, which
00533   // invokes the BamReader to reconstruct this object.  Since we use
00534   // the specific object's class as the pointer, we get the particular
00535   // instance of decode_from_bam_stream appropriate to this class.
00536 
00537   PyObject *func = PyObject_GetAttrString(this_class, "decode_from_bam_stream");
00538   if (func == NULL) {
00539     return NULL;
00540   }
00541 
00542   PyObject *result;
00543   if (py_reader != NULL){
00544     result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), data.size(), py_reader);
00545     Py_DECREF(py_reader);
00546   } else {
00547     result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size());
00548   }
00549 
00550   if (result == NULL) {
00551     return NULL;
00552   }
00553 
00554   if (result == Py_None) {
00555     Py_DECREF(result);
00556     PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream");
00557     return NULL;
00558   }    
00559 
00560   return result;
00561 }
00562 #endif  // HAVE_PYTHON
00563 
 All Classes Functions Variables Enumerations