ov-class.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2007-2012 John W. Eaton
00004 Copyright (C) 2009 VZLU Prague
00005 
00006 This file is part of Octave.
00007 
00008 Octave is free software; you can redistribute it and/or modify it
00009 under the terms of the GNU General Public License as published by the
00010 Free Software Foundation; either version 3 of the License, or (at your
00011 option) any later version.
00012 
00013 Octave is distributed in the hope that it will be useful, but WITHOUT
00014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00015 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00016 for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with Octave; see the file COPYING.  If not, see
00020 <http://www.gnu.org/licenses/>.
00021 
00022 */
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027 
00028 #include <iostream>
00029 
00030 #include "Array-util.h"
00031 #include "byte-swap.h"
00032 #include "oct-locbuf.h"
00033 #include "lo-mappers.h"
00034 
00035 #include "Cell.h"
00036 #include "defun.h"
00037 #include "error.h"
00038 #include "file-ops.h"
00039 #include "gripes.h"
00040 #include "load-path.h"
00041 #include "ls-hdf5.h"
00042 #include "ls-oct-ascii.h"
00043 #include "ls-oct-binary.h"
00044 #include "ls-utils.h"
00045 #include "oct-lvalue.h"
00046 #include "ov-class.h"
00047 #include "ov-fcn.h"
00048 #include "ov-usr-fcn.h"
00049 #include "pager.h"
00050 #include "parse.h"
00051 #include "pr-output.h"
00052 #include "toplev.h"
00053 #include "unwind-prot.h"
00054 #include "variables.h"
00055 
00056 DEFINE_OCTAVE_ALLOCATOR(octave_class);
00057 
00058 int octave_class::t_id (-1);
00059 
00060 const std::string octave_class::t_name ("class");
00061 
00062 void
00063 octave_class::register_type (void)
00064 {
00065   t_id = octave_value_typeinfo::register_type
00066     (octave_class::t_name, "<unknown>", octave_value (new octave_class ()));
00067 }
00068 
00069 octave_class::octave_class (const octave_map& m, const std::string& id,
00070                             const octave_value_list& parents)
00071   : octave_base_value (), map (m), c_name (id), obsolete_copies (0)
00072 {
00073   octave_idx_type n = parents.length ();
00074 
00075   for (octave_idx_type idx = 0; idx < n; idx++)
00076     {
00077       octave_value parent = parents(idx);
00078 
00079       if (! parent.is_object ())
00080         error ("parents must be objects");
00081       else
00082         {
00083           std::string pcnm = parent.class_name ();
00084 
00085           if (find_parent_class (pcnm))
00086             error ("duplicate class in parent tree");
00087           else
00088             {
00089               parent_list.push_back (pcnm);
00090 
00091               octave_idx_type nel = map.numel ();
00092               octave_idx_type p_nel = parent.numel ();
00093 
00094               if (nel == 0)
00095                 {
00096                   if (p_nel == 0)
00097                     {
00098                       // No elements in MAP or the parent class object,
00099                       // so just add the field name.
00100 
00101                       map.assign (pcnm, Cell (map.dims ()));
00102                     }
00103                   else if (p_nel == 1)
00104                     {
00105                       if (map.nfields () == 0)
00106                         {
00107                           // No elements or fields in MAP, but the
00108                           // parent is class object with one element.
00109                           // Resize to match size of parent class and
00110                           // make the parent a field in MAP.
00111 
00112                           map.resize (parent.dims ());
00113 
00114                           map.assign (pcnm, parent);
00115                         }
00116                       else
00117                         {
00118                           // No elements in MAP, but we have at least
00119                           // one field.  So don't resize, just add the
00120                           // field name.
00121 
00122                           map.assign (pcnm, Cell (map.dims ()));
00123                         }
00124                     }
00125                   else if (map.nfields () == 0)
00126                     {
00127                       // No elements or fields in MAP and more than one
00128                       // element in the parent class object, so we can
00129                       // resize MAP to match parent dimsenions, then
00130                       // distribute the elements of the parent object to
00131                       // the elements of MAP.
00132 
00133                       dim_vector parent_dims = parent.dims ();
00134 
00135                       map.resize (parent_dims);
00136 
00137                       Cell c (parent_dims);
00138 
00139                       octave_map pmap = parent.map_value ();
00140 
00141                       std::list<std::string> plist
00142                         = parent.parent_class_name_list ();
00143 
00144                       for (octave_idx_type i = 0; i < p_nel; i++)
00145                         c(i) = octave_value (pmap.index(i), pcnm, plist);
00146 
00147                       map.assign (pcnm, c);
00148                     }
00149                   else
00150                     error ("class: parent class dimension mismatch");
00151                 }
00152               else if (nel == 1 && p_nel == 1)
00153                 {
00154                   // Simple assignment.
00155 
00156                   map.assign (pcnm, parent);
00157                 }
00158               else
00159                 {
00160                   if (p_nel == 1)
00161                     {
00162                       // Broadcast the scalar parent class object to
00163                       // each element of MAP.
00164 
00165                       Cell pcell (map.dims (), parent);
00166 
00167                       map.assign (pcnm, pcell);
00168                     }
00169 
00170                   else if (nel == p_nel)
00171                     {
00172                       // FIXME -- is there a better way to do this?
00173 
00174                       // The parent class object has the same number of
00175                       // elements as the map we are using to create the
00176                       // new object, so distribute those elements to
00177                       // each element of the new object by first
00178                       // splitting the elements of the parent class
00179                       // object into a cell array with one element per
00180                       // cell.  Then do the assignment all at once.
00181 
00182                       Cell c (parent.dims ());
00183 
00184                       octave_map pmap = parent.map_value ();
00185 
00186                       std::list<std::string> plist
00187                         = parent.parent_class_name_list ();
00188 
00189                       for (octave_idx_type i = 0; i < p_nel; i++)
00190                         c(i) = octave_value (pmap.index(i), pcnm, plist);
00191 
00192                       map.assign (pcnm, c);
00193                     }
00194                   else
00195                     error ("class: parent class dimension mismatch");
00196                 }
00197             }
00198         }
00199     }
00200 
00201   if (! error_state)
00202     symbol_table::add_to_parent_map (id, parent_list);
00203 }
00204 
00205 octave_base_value *
00206 octave_class::unique_clone (void)
00207 {
00208   if (count == obsolete_copies)
00209     {
00210       // All remaining copies are obsolete. We don't actually need to clone.
00211       count++;
00212       return this;
00213     }
00214   else
00215     {
00216       // In theory, this shouldn't be happening, but it's here just in case.
00217       if (count < obsolete_copies)
00218         obsolete_copies = 0;
00219 
00220       return clone ();
00221     }
00222 }
00223 
00224 std::string
00225 octave_class::get_current_method_class (void)
00226 {
00227   std::string retval = class_name ();
00228 
00229   if (nparents () > 0)
00230     {
00231       octave_function *fcn = octave_call_stack::current ();
00232 
00233       // Here we are just looking to see if FCN is a method or constructor
00234       // for any class, not specifically this one.
00235       if (fcn && (fcn->is_class_method () || fcn->is_class_constructor ()))
00236         retval = fcn->dispatch_class ();
00237     }
00238 
00239   return retval;
00240 }
00241 
00242 static void
00243 gripe_invalid_index1 (void)
00244 {
00245   error ("invalid index for class");
00246 }
00247 
00248 static void
00249 gripe_invalid_index_for_assignment (void)
00250 {
00251   error ("invalid index for class assignment");
00252 }
00253 
00254 static void
00255 gripe_invalid_index_type (const std::string& nm, char t)
00256 {
00257   error ("%s cannot be indexed with %c", nm.c_str (), t);
00258 }
00259 
00260 static void
00261 gripe_failed_assignment (void)
00262 {
00263   error ("assignment to class element failed");
00264 }
00265 
00266 static inline octave_value_list
00267 sanitize (const octave_value_list& ovl)
00268 {
00269   octave_value_list retval = ovl;
00270 
00271   for (octave_idx_type i = 0; i < ovl.length (); i++)
00272     {
00273       if (retval(i).is_magic_colon ())
00274         retval(i) = ":";
00275     }
00276 
00277   return retval;
00278 }
00279 
00280 static inline octave_value
00281 make_idx_args (const std::string& type,
00282                const std::list<octave_value_list>& idx,
00283                const std::string& who)
00284 {
00285   octave_value retval;
00286 
00287   size_t len = type.length ();
00288 
00289   if (len == idx.size ())
00290     {
00291       Cell type_field (1, len);
00292       Cell subs_field (1, len);
00293 
00294       std::list<octave_value_list>::const_iterator p = idx.begin ();
00295 
00296       for (size_t i = 0; i < len; i++)
00297         {
00298           char t = type[i];
00299 
00300           switch (t)
00301             {
00302             case '(':
00303               type_field(i) = "()";
00304               subs_field(i) = Cell (sanitize (*p++));
00305               break;
00306 
00307             case '{':
00308               type_field(i) = "{}";
00309               subs_field(i) = Cell (sanitize (*p++));
00310               break;
00311 
00312             case '.':
00313               {
00314                 type_field(i) = ".";
00315 
00316                 octave_value_list vlist = *p++;
00317 
00318                 if (vlist.length () == 1)
00319                   {
00320                     octave_value val = vlist(0);
00321 
00322                     if (val.is_string ())
00323                       subs_field(i) = val;
00324                     else
00325                       {
00326                         error ("expecting character string argument for '.' index");
00327                         return retval;
00328                       }
00329                   }
00330                 else
00331                   {
00332                     error ("expecting single argument for '.' index");
00333                     return retval;
00334                   }
00335               }
00336               break;
00337 
00338             default:
00339               panic_impossible ();
00340               break;
00341             }
00342         }
00343 
00344       octave_map m;
00345 
00346       m.assign ("type", type_field);
00347       m.assign ("subs", subs_field);
00348 
00349       retval = m;
00350     }
00351   else
00352     error ("invalid index for %s", who.c_str ());
00353 
00354   return retval;
00355 }
00356 
00357 Cell
00358 octave_class::dotref (const octave_value_list& idx)
00359 {
00360   Cell retval;
00361 
00362   assert (idx.length () == 1);
00363 
00364   std::string method_class = get_current_method_class ();
00365 
00366   // Find the class in which this method resides before attempting to access
00367   // the requested field.
00368 
00369   octave_base_value *obvp = find_parent_class (method_class);
00370 
00371   if (obvp == 0)
00372     {
00373       error ("malformed class");
00374       return retval;
00375     }
00376 
00377   octave_map my_map = (obvp != this) ? obvp->map_value () : map;
00378 
00379   std::string nm = idx(0).string_value ();
00380 
00381   if (! error_state)
00382     {
00383       octave_map::const_iterator p = my_map.seek (nm);
00384 
00385       if (p != my_map.end ())
00386         retval = my_map.contents (p);
00387       else
00388         error ("class has no member '%s'", nm.c_str ());
00389     }
00390   else
00391     gripe_invalid_index1 ();
00392 
00393   return retval;
00394 }
00395 
00396 static bool
00397 called_from_builtin (void)
00398 {
00399   octave_function *fcn = octave_call_stack::caller ();
00400 
00401   // FIXME -- we probably need a better check here, or some other
00402   // mechanism to avoid overloaded functions when builtin is used.
00403   // For example, what if someone overloads the builtin function?
00404   // Also, are there other places where using builtin is not properly
00405   // avoiding dispatch?
00406 
00407   return (fcn && fcn->name () == "builtin");
00408 }
00409 
00410 Matrix
00411 octave_class::size (void)
00412 {
00413   if (in_class_method () || called_from_builtin ())
00414     return octave_base_value::size ();
00415 
00416   Matrix retval (1, 2, 1.0);
00417   octave_value meth = symbol_table::find_method ("size", class_name ());
00418 
00419   if (meth.is_defined ())
00420     {
00421       count++;
00422       octave_value_list args (1, octave_value (this));
00423 
00424       octave_value_list lv = feval (meth.function_value (), args, 1);
00425       if (lv.length () > 0 && lv(0).is_matrix_type () && lv(0).dims ().is_vector ())
00426         retval = lv(0).matrix_value ();
00427       else
00428         error ("@%s/size: invalid return value", class_name ().c_str ());
00429     }
00430   else
00431     {
00432       dim_vector dv = dims ();
00433 
00434       int nd = dv.length ();
00435 
00436       retval.resize (1, nd);
00437 
00438       for (int i = 0; i < nd; i++)
00439         retval(i) = dv(i);
00440     }
00441 
00442   return retval;
00443 }
00444 
00445 octave_idx_type
00446 octave_class::numel (const octave_value_list& idx)
00447 {
00448   if (in_class_method () || called_from_builtin ())
00449     return octave_base_value::numel (idx);
00450 
00451   octave_idx_type retval = -1;
00452   const std::string cn = class_name ();
00453 
00454   octave_value meth = symbol_table::find_method ("numel", cn);
00455 
00456   if (meth.is_defined ())
00457     {
00458       octave_value_list args (idx.length () + 1, octave_value ());
00459 
00460       count++;
00461       args(0) = octave_value (this);
00462 
00463       for (octave_idx_type i = 0; i < idx.length (); i++)
00464         args(i+1) = idx(i);
00465 
00466       octave_value_list lv = feval (meth.function_value (), args, 1);
00467       if (lv.length () == 1 && lv(0).is_scalar_type ())
00468         retval = lv(0).idx_type_value (true);
00469       else
00470         error ("@%s/numel: invalid return value", cn.c_str ());
00471     }
00472   else
00473     retval = octave_base_value::numel (idx);
00474 
00475   return retval;
00476 }
00477 
00478 octave_value_list
00479 octave_class::subsref (const std::string& type,
00480                        const std::list<octave_value_list>& idx,
00481                        int nargout)
00482 {
00483   octave_value_list retval;
00484 
00485   if (in_class_method () || called_from_builtin ())
00486     {
00487       // FIXME -- this block of code is the same as the body of
00488       // octave_struct::subsref.  Maybe it could be shared instead of
00489       // duplicated.
00490 
00491       int skip = 1;
00492 
00493       switch (type[0])
00494         {
00495         case '(':
00496           {
00497             if (type.length () > 1 && type[1] == '.')
00498               {
00499                 std::list<octave_value_list>::const_iterator p = idx.begin ();
00500                 octave_value_list key_idx = *++p;
00501 
00502                 Cell tmp = dotref (key_idx);
00503 
00504                 if (! error_state)
00505                   {
00506                     Cell t = tmp.index (idx.front ());
00507 
00508                     retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true);
00509 
00510                     // We handled two index elements, so tell
00511                     // next_subsref to skip both of them.
00512 
00513                     skip++;
00514                   }
00515               }
00516             else
00517               retval(0) = octave_value (map.index (idx.front ()),
00518                                         c_name, parent_list);
00519           }
00520           break;
00521 
00522         case '.':
00523           {
00524             if (map.numel() > 0)
00525               {
00526                 Cell t = dotref (idx.front ());
00527 
00528                 retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true);
00529               }
00530           }
00531           break;
00532 
00533         case '{':
00534           gripe_invalid_index_type (type_name (), type[0]);
00535           break;
00536 
00537         default:
00538           panic_impossible ();
00539         }
00540 
00541       // FIXME -- perhaps there should be an
00542       // octave_value_list::next_subsref member function?  See also
00543       // octave_user_function::subsref.
00544 
00545       if (idx.size () > 1)
00546         retval = retval(0).next_subsref (nargout, type, idx, skip);
00547     }
00548   else
00549     {
00550       octave_value meth = symbol_table::find_method ("subsref", class_name ());
00551 
00552       if (meth.is_defined ())
00553         {
00554           octave_value_list args;
00555 
00556           args(1) = make_idx_args (type, idx, "subsref");
00557 
00558           if (error_state)
00559             return octave_value_list ();
00560 
00561           count++;
00562           args(0) = octave_value (this);
00563 
00564           // FIXME: for Matlab compatibility, let us attempt to set up a proper
00565           // value for nargout at least in the simple case where the
00566           // cs-list-type expression - i.e., {} or ().x, is the leading one.
00567           // Note that Octave does not actually need this, since it will
00568           // be able to properly react to varargout a posteriori.
00569           bool maybe_cs_list_query = (type[0] == '.' || type[0] == '{'
00570                                       || (type.length () > 1 && type[0] == '('
00571                                           && type[1] == '.'));
00572 
00573           int true_nargout = nargout;
00574 
00575           if (maybe_cs_list_query)
00576             {
00577               // Set up a proper nargout for the subsref call by calling numel.
00578               octave_value_list tmp;
00579               if (type[0] != '.') tmp = idx.front ();
00580               true_nargout = numel (tmp);
00581             }
00582 
00583           retval = feval (meth.function_value (), args, true_nargout);
00584 
00585           // Since we're handling subsref, return the list in the first value
00586           // if it has more than one element, to be able to pass through
00587           // rvalue1 calls.
00588           if (retval.length () > 1)
00589             retval = octave_value (retval, true);
00590         }
00591       else
00592         {
00593           if (type.length () == 1 && type[0] == '(')
00594             retval(0) = octave_value (map.index (idx.front ()), c_name,
00595                                       parent_list);
00596           else
00597             gripe_invalid_index1 ();
00598         }
00599     }
00600 
00601   return retval;
00602 }
00603 
00604 octave_value
00605 octave_class::numeric_conv (const Cell& val, const std::string& type)
00606 {
00607   octave_value retval;
00608 
00609   if (val.length () == 1)
00610     {
00611       retval = val(0);
00612 
00613       if (type.length () > 0 && type[0] == '.' && ! retval.is_map ())
00614         retval = octave_map ();
00615     }
00616   else
00617     gripe_invalid_index_for_assignment ();
00618 
00619   return retval;
00620 }
00621 
00622 octave_value
00623 octave_class::subsasgn (const std::string& type,
00624                         const std::list<octave_value_list>& idx,
00625                         const octave_value& rhs)
00626 {
00627   count++;
00628   return subsasgn_common (octave_value (this), type, idx, rhs);
00629 }
00630 
00631 octave_value
00632 octave_class::undef_subsasgn (const std::string& type,
00633                               const std::list<octave_value_list>& idx,
00634                               const octave_value& rhs)
00635 {
00636   // For compatibility with Matlab, pass [] as the first argument to the
00637   // the subsasgn function when the LHS of an indexed assignment is
00638   // undefined.
00639 
00640   return subsasgn_common (Matrix (), type, idx, rhs);
00641 }
00642 
00643 octave_value
00644 octave_class::subsasgn_common (const octave_value& obj,
00645                                const std::string& type,
00646                                const std::list<octave_value_list>& idx,
00647                                const octave_value& rhs)
00648 {
00649   octave_value retval;
00650 
00651   if (! (in_class_method () || called_from_builtin ()))
00652     {
00653       octave_value meth = symbol_table::find_method ("subsasgn", class_name ());
00654 
00655       if (meth.is_defined ())
00656         {
00657           octave_value_list args;
00658 
00659           if (rhs.is_cs_list ())
00660             {
00661               octave_value_list lrhs = rhs.list_value ();
00662               args.resize (2 + lrhs.length ());
00663               for (octave_idx_type i = 0; i < lrhs.length (); i++)
00664                 args(2+i) = lrhs(i);
00665             }
00666           else
00667             args(2) = rhs;
00668 
00669           args(1) = make_idx_args (type, idx, "subsasgn");
00670 
00671           if (error_state)
00672             return octave_value_list ();
00673 
00674           args(0) = obj;
00675 
00676           // Now comes the magic. Count copies with me:
00677           // 1. myself (obsolete)
00678           // 2. the copy inside args (obsolete)
00679           // 3. the copy in method's symbol table (working)
00680           // ... possibly more (not obsolete).
00681           //
00682           // So we mark 2 copies as obsolete and hold our fingers crossed.
00683           // But prior to doing that, check whether the routine is amenable
00684           // to the optimization.
00685           // It is essential that the handling function doesn't store extra
00686           // copies anywhere. If it does, things will not break but the
00687           // optimization won't work.
00688 
00689           octave_value_list tmp;
00690 
00691           if (obsolete_copies == 0 && meth.is_user_function ()
00692               && meth.user_function_value ()->subsasgn_optimization_ok ())
00693             {
00694               unwind_protect frame;
00695               frame.protect_var (obsolete_copies);
00696               obsolete_copies = 2;
00697 
00698               tmp = feval (meth.function_value (), args);
00699             }
00700           else
00701             tmp = feval (meth.function_value (), args);
00702 
00703           // FIXME -- should the subsasgn method be able to return
00704           // more than one value?
00705 
00706           if (tmp.length () > 1)
00707             error ("expecting single return value from @%s/subsasgn",
00708                    class_name().c_str ());
00709 
00710           else
00711             retval = tmp(0);
00712 
00713           return retval;
00714         }
00715     }
00716 
00717   // Find the class in which this method resides before
00718   // attempting to do the indexed assignment.
00719 
00720   std::string method_class = get_current_method_class ();
00721 
00722   octave_base_value *obvp = unique_parent_class (method_class);
00723   if (obvp != this)
00724     {
00725 
00726       if (obvp)
00727         {
00728           obvp->subsasgn (type, idx, rhs);
00729           if (! error_state)
00730             {
00731               count++;
00732               retval = octave_value (this);
00733             }
00734           else
00735             gripe_failed_assignment ();
00736         }
00737       else
00738         error ("malformed class");
00739 
00740       return retval;
00741     }
00742 
00743   // FIXME -- this block of code is the same as the body of
00744   // octave_struct::subsasgn.  Maybe it could be shared instead of
00745   // duplicated.
00746 
00747   int n = type.length ();
00748 
00749   octave_value t_rhs = rhs;
00750 
00751   if (n > 1 && ! (type.length () == 2 && type[0] == '(' && type[1] == '.'))
00752     {
00753       switch (type[0])
00754         {
00755         case '(':
00756           {
00757             if (type.length () > 1 && type[1] == '.')
00758               {
00759                 std::list<octave_value_list>::const_iterator p = idx.begin ();
00760                 octave_value_list t_idx = *p;
00761 
00762                 octave_value_list key_idx = *++p;
00763 
00764                 assert (key_idx.length () == 1);
00765 
00766                 std::string key = key_idx(0).string_value ();
00767 
00768                 if (! error_state)
00769                   {
00770                     octave_value u;
00771 
00772                     if (! map.contains (key))
00773                       u = octave_value::empty_conv (type.substr (2), rhs);
00774                     else
00775                       {
00776                         Cell map_val = map.contents (key);
00777 
00778                         Cell map_elt = map_val.index (idx.front (), true);
00779 
00780                         u = numeric_conv (map_elt, type.substr (2));
00781                       }
00782 
00783                     if (! error_state)
00784                       {
00785                         std::list<octave_value_list> next_idx (idx);
00786 
00787                         // We handled two index elements, so subsasgn to
00788                         // needs to skip both of them.
00789 
00790                         next_idx.erase (next_idx.begin ());
00791                         next_idx.erase (next_idx.begin ());
00792 
00793                         u.make_unique ();
00794 
00795                         t_rhs = u.subsasgn (type.substr (2), next_idx, rhs);
00796                       }
00797                   }
00798                 else
00799                   gripe_invalid_index_for_assignment ();
00800               }
00801             else
00802               gripe_invalid_index_for_assignment ();
00803           }
00804           break;
00805 
00806         case '.':
00807           {
00808             octave_value_list key_idx = idx.front ();
00809 
00810             assert (key_idx.length () == 1);
00811 
00812             std::string key = key_idx(0).string_value ();
00813 
00814             std::list<octave_value_list> next_idx (idx);
00815 
00816             next_idx.erase (next_idx.begin ());
00817 
00818             std::string next_type = type.substr (1);
00819 
00820             Cell tmpc (1, 1);
00821             octave_map::iterator pkey = map.seek (key);
00822             if (pkey != map.end ())
00823               {
00824                 map.contents (pkey).make_unique ();
00825                 tmpc = map.contents (pkey);
00826               }
00827 
00828             // FIXME: better code reuse?
00829             if (! error_state)
00830               {
00831                 if (tmpc.numel () == 1)
00832                   {
00833                     octave_value& tmp = tmpc(0);
00834 
00835                     if (! tmp.is_defined () || tmp.is_zero_by_zero ())
00836                       {
00837                         tmp = octave_value::empty_conv (next_type, rhs);
00838                         tmp.make_unique (); // probably a no-op.
00839                       }
00840                     else
00841                       // optimization: ignore the copy still stored inside our map.
00842                       tmp.make_unique (1);
00843 
00844                     if (! error_state)
00845                       t_rhs = tmp.subsasgn (next_type, next_idx, rhs);
00846                   }
00847                 else
00848                   gripe_indexed_cs_list ();
00849               }
00850           }
00851           break;
00852 
00853         case '{':
00854           gripe_invalid_index_type (type_name (), type[0]);
00855           break;
00856 
00857         default:
00858           panic_impossible ();
00859         }
00860     }
00861 
00862   if (! error_state)
00863     {
00864       switch (type[0])
00865         {
00866         case '(':
00867           {
00868             if (n > 1 && type[1] == '.')
00869               {
00870                 std::list<octave_value_list>::const_iterator p = idx.begin ();
00871                 octave_value_list key_idx = *++p;
00872 
00873                 assert (key_idx.length () == 1);
00874 
00875                 std::string key = key_idx(0).string_value ();
00876 
00877                 if (! error_state)
00878                   {
00879                     map.assign (idx.front (), key, t_rhs);
00880 
00881                     if (! error_state)
00882                       {
00883                         count++;
00884                         retval = octave_value (this);
00885                       }
00886                     else
00887                       gripe_failed_assignment ();
00888                   }
00889                 else
00890                   gripe_failed_assignment ();
00891               }
00892             else
00893               {
00894                 if (t_rhs.is_object () || t_rhs.is_map ())
00895                   {
00896                     octave_map rhs_map = t_rhs.map_value ();
00897 
00898                     if (! error_state)
00899                       {
00900                         map.assign (idx.front (), rhs_map);
00901 
00902                         if (! error_state)
00903                           {
00904                             count++;
00905                             retval = octave_value (this);
00906                           }
00907                         else
00908                           gripe_failed_assignment ();
00909                       }
00910                     else
00911                       error ("invalid class assignment");
00912                   }
00913                 else
00914                   {
00915                     if (t_rhs.is_empty ())
00916                       {
00917                         map.delete_elements (idx.front());
00918 
00919                         if (! error_state)
00920                           {
00921                             count++;
00922                             retval = octave_value (this);
00923                           }
00924                         else
00925                           gripe_failed_assignment ();
00926                       }
00927                     else
00928                       error ("invalid class assignment");
00929                   }
00930               }
00931           }
00932           break;
00933 
00934         case '.':
00935           {
00936             octave_value_list key_idx = idx.front ();
00937 
00938             assert (key_idx.length () == 1);
00939 
00940             std::string key = key_idx(0).string_value ();
00941 
00942             if (t_rhs.is_cs_list ())
00943               {
00944                 Cell tmp_cell = Cell (t_rhs.list_value ());
00945 
00946                 // The shape of the RHS is irrelevant, we just want
00947                 // the number of elements to agree and to preserve the
00948                 // shape of the left hand side of the assignment.
00949 
00950                 if (numel () == tmp_cell.numel ())
00951                   tmp_cell = tmp_cell.reshape (dims ());
00952 
00953                 map.setfield (key, tmp_cell);
00954               }
00955             else
00956               {
00957                 Cell tmp_cell(1, 1);
00958                 tmp_cell(0) = t_rhs.storable_value ();
00959                 map.setfield (key, tmp_cell);
00960               }
00961 
00962             if (! error_state)
00963               {
00964                 count++;
00965                 retval = octave_value (this);
00966               }
00967             else
00968               gripe_failed_assignment ();
00969           }
00970           break;
00971 
00972         case '{':
00973           gripe_invalid_index_type (type_name (), type[0]);
00974           break;
00975 
00976         default:
00977           panic_impossible ();
00978         }
00979     }
00980   else
00981     gripe_failed_assignment ();
00982 
00983   return retval;
00984 }
00985 
00986 idx_vector
00987 octave_class::index_vector (void) const
00988 {
00989   idx_vector retval;
00990 
00991   octave_value meth = symbol_table::find_method ("subsindex", class_name ());
00992 
00993   if (meth.is_defined ())
00994     {
00995       octave_value_list args;
00996       args(0) = octave_value (new octave_class (map, c_name, parent_list));
00997 
00998       octave_value_list tmp = feval (meth.function_value (), args, 1);
00999 
01000       if (!error_state && tmp.length () >= 1)
01001         {
01002           if (tmp(0).is_object())
01003             error ("subsindex function must return a valid index vector");
01004           else
01005             // Index vector returned by subsindex is zero based
01006             // (why this inconsistency Mathworks?), and so we must
01007             // add one to the value returned as the index_vector method
01008             // expects it to be one based.
01009             retval = do_binary_op (octave_value::op_add, tmp (0),
01010                                    octave_value (1.0)).index_vector ();
01011         }
01012     }
01013   else
01014     error ("no subsindex method defined for class %s",
01015            class_name().c_str ());
01016 
01017   return retval;
01018 }
01019 
01020 size_t
01021 octave_class::byte_size (void) const
01022 {
01023   // Neglect the size of the fieldnames.
01024 
01025   size_t retval = 0;
01026 
01027   for (octave_map::const_iterator p = map.begin (); p != map.end (); p++)
01028     {
01029       std::string key = map.key (p);
01030 
01031       octave_value val = octave_value (map.contents (p));
01032 
01033       retval += val.byte_size ();
01034     }
01035 
01036   return retval;
01037 }
01038 
01039 string_vector
01040 octave_class::map_keys (void) const
01041 {
01042   string_vector retval;
01043   gripe_wrong_type_arg ("octave_class::map_keys()", type_name ());
01044   return retval;
01045 }
01046 
01047 octave_base_value *
01048 octave_class::find_parent_class (const std::string& parent_class_name)
01049 {
01050   octave_base_value* retval = 0;
01051 
01052   if (parent_class_name == class_name ())
01053     retval = this;
01054   else
01055     {
01056       for (std::list<std::string>::iterator pit = parent_list.begin ();
01057            pit != parent_list.end ();
01058            pit++)
01059         {
01060           octave_map::const_iterator smap = map.seek (*pit);
01061 
01062           const Cell& tmp = map.contents (smap);
01063 
01064           octave_value vtmp = tmp(0);
01065 
01066           octave_base_value *obvp = vtmp.internal_rep ();
01067 
01068           retval = obvp->find_parent_class (parent_class_name);
01069 
01070           if (retval)
01071             break;
01072         }
01073     }
01074 
01075   return retval;
01076 }
01077 
01078 octave_base_value *
01079 octave_class::unique_parent_class (const std::string& parent_class_name)
01080 {
01081   octave_base_value* retval = 0;
01082 
01083   if (parent_class_name == class_name ())
01084     retval = this;
01085   else
01086     {
01087       for (std::list<std::string>::iterator pit = parent_list.begin ();
01088            pit != parent_list.end ();
01089            pit++)
01090         {
01091           octave_map::iterator smap = map.seek (*pit);
01092 
01093           Cell& tmp = map.contents (smap);
01094 
01095           octave_value& vtmp = tmp(0);
01096 
01097           octave_base_value *obvp = vtmp.internal_rep ();
01098 
01099           // Use find_parent_class first to avoid uniquifying if not necessary.
01100           retval = obvp->find_parent_class (parent_class_name);
01101 
01102           if (retval)
01103             {
01104               vtmp.make_unique ();
01105               obvp = vtmp.internal_rep ();
01106               retval = obvp->unique_parent_class (parent_class_name);
01107 
01108               break;
01109             }
01110         }
01111     }
01112 
01113   return retval;
01114 }
01115 
01116 string_vector
01117 octave_class::all_strings (bool pad) const
01118 {
01119   string_vector retval;
01120 
01121   octave_value meth = symbol_table::find_method ("char", class_name ());
01122 
01123   if (meth.is_defined ())
01124     {
01125       octave_value_list args;
01126       args(0) = octave_value (new octave_class (map, c_name, parent_list));
01127 
01128       octave_value_list tmp = feval (meth.function_value (), args, 1);
01129 
01130       if (!error_state && tmp.length () >= 1)
01131         {
01132           if (tmp(0).is_string ())
01133             retval = tmp(0).all_strings (pad);
01134           else
01135             error ("cname/char method did not return a character string");
01136         }
01137     }
01138   else
01139     error ("no char method defined for class %s", class_name().c_str ());
01140 
01141   return retval;
01142 }
01143 
01144 
01145 void
01146 octave_class::print (std::ostream& os, bool) const
01147 {
01148   print_raw (os);
01149 }
01150 
01151 void
01152 octave_class::print_raw (std::ostream& os, bool) const
01153 {
01154   unwind_protect frame;
01155 
01156   indent (os);
01157   os << "  <class " << class_name () << ">";
01158   newline (os);
01159 }
01160 
01161 bool
01162 octave_class::print_name_tag (std::ostream& os, const std::string& name) const
01163 {
01164   bool retval = false;
01165 
01166   indent (os);
01167   os << name << " =";
01168   newline (os);
01169   if (! Vcompact_format)
01170     newline (os);
01171 
01172   return retval;
01173 }
01174 
01175 void
01176 octave_class::print_with_name (std::ostream& os, const std::string& name,
01177                                bool)
01178 {
01179   octave_value fcn = symbol_table::find_method ("display", class_name ());
01180 
01181   if (fcn.is_defined ())
01182     {
01183       octave_value_list args;
01184 
01185       count++;
01186       args(0) = octave_value (this);
01187 
01188       string_vector arg_names (1);
01189 
01190       arg_names[0] = name;
01191 
01192       args.stash_name_tags (arg_names);
01193 
01194       feval (fcn.function_value (), args);
01195     }
01196   else
01197     {
01198       indent (os);
01199       os << name << " = <class " << class_name () << ">";
01200       newline (os);
01201     }
01202 }
01203 
01204 // Loading a class properly requires an exemplar map entry for success.
01205 // If we don't have one, we attempt to create one by calling the constructor
01206 // with no arguments.
01207 bool
01208 octave_class::reconstruct_exemplar (void)
01209 {
01210   bool retval = false;
01211 
01212   octave_class::exemplar_const_iterator it
01213     = octave_class::exemplar_map.find (c_name);
01214 
01215   if (it != octave_class::exemplar_map.end ())
01216     retval = true;
01217   else
01218     {
01219       octave_value ctor = symbol_table::find_method (c_name, c_name);
01220 
01221       bool have_ctor = false;
01222 
01223       if (ctor.is_defined () && ctor.is_function ())
01224         {
01225           octave_function *fcn = ctor.function_value ();
01226 
01227           if (fcn && fcn->is_class_constructor (c_name))
01228             have_ctor = true;
01229 
01230           // Something has gone terribly wrong if
01231           // symbol_table::find_method (c_name, c_name) does not return
01232           // a class constructor for the class c_name...
01233           assert (have_ctor);
01234         }
01235 
01236       if (have_ctor)
01237         {
01238           octave_value_list result
01239             = ctor.do_multi_index_op (1, octave_value_list ());
01240 
01241           if (result.length () == 1)
01242             retval = true;
01243           else
01244             warning ("call to constructor for class %s failed", c_name.c_str ());
01245         }
01246       else
01247         warning ("no constructor for class %s", c_name.c_str ());
01248     }
01249 
01250   return retval;
01251 }
01252 
01253 void
01254 octave_class::clear_exemplar_map (void)
01255 {
01256   exemplar_map.clear ();
01257 }
01258 
01259 //  Load/save does not provide enough information to reconstruct the
01260 //  class inheritance structure.  reconstruct_parents () attempts to
01261 //  do so.  If successful, a "true" value is returned.
01262 //
01263 //  Note that we don't check the loaded object structure against the
01264 //  class structure here so the user's loadobj method has a chance
01265 //  to do its magic.
01266 bool
01267 octave_class::reconstruct_parents (void)
01268 {
01269   bool retval = true, might_have_inheritance = false;
01270   std::string dbgstr = "dork";
01271 
01272   // First, check to see if there might be an issue with inheritance.
01273   for (octave_map::const_iterator p = map.begin (); p != map.end (); p++)
01274     {
01275       std::string  key = map.key (p);
01276       Cell         val = map.contents (p);
01277       if ( val(0).is_object() )
01278         {
01279           dbgstr = "blork";
01280           if( key == val(0).class_name() )
01281             {
01282               might_have_inheritance = true;
01283               dbgstr = "cork";
01284               break;
01285             }
01286         }
01287     }
01288 
01289   if (might_have_inheritance)
01290     {
01291       octave_class::exemplar_const_iterator it
01292         = octave_class::exemplar_map.find (c_name);
01293 
01294       if (it == octave_class::exemplar_map.end ())
01295         retval = false;
01296       else
01297         {
01298           octave_class::exemplar_info exmplr = it->second;
01299           parent_list = exmplr.parents ();
01300           for (std::list<std::string>::iterator pit = parent_list.begin ();
01301                pit != parent_list.end ();
01302                pit++)
01303             {
01304               dbgstr = *pit;
01305               bool dbgbool = map.contains (*pit);
01306               if (!dbgbool)
01307                 {
01308                   retval = false;
01309                   break;
01310                 }
01311             }
01312         }
01313     }
01314 
01315   return retval;
01316 }
01317 
01318 bool
01319 octave_class::save_ascii (std::ostream& os)
01320 {
01321   os << "# classname: " << class_name () << "\n";
01322   octave_map m;
01323   if (load_path::find_method (class_name (), "saveobj") != std::string ())
01324     {
01325       octave_value in = new octave_class (*this);
01326       octave_value_list tmp = feval ("saveobj", in, 1);
01327       if (! error_state)
01328         m = tmp(0).map_value ();
01329       else
01330         return false;
01331     }
01332   else
01333     m = map_value ();
01334 
01335   os << "# length: " << m.nfields () << "\n";
01336 
01337   octave_map::iterator i = m.begin ();
01338   while (i != m.end ())
01339     {
01340       octave_value val = map.contents (i);
01341 
01342       bool b = save_ascii_data (os, val, m.key (i), false, 0);
01343 
01344       if (! b)
01345         return os;
01346 
01347       i++;
01348     }
01349 
01350   return true;
01351 }
01352 
01353 bool
01354 octave_class::load_ascii (std::istream& is)
01355 {
01356   octave_idx_type len = 0;
01357   std::string classname;
01358   bool success = true;
01359 
01360   if (extract_keyword (is, "classname", classname) && classname != "")
01361     {
01362       if (extract_keyword (is, "length", len) && len >= 0)
01363         {
01364           if (len > 0)
01365             {
01366               octave_map m (map);
01367 
01368               for (octave_idx_type j = 0; j < len; j++)
01369                 {
01370                   octave_value t2;
01371                   bool dummy;
01372 
01373                   // recurse to read cell elements
01374                   std::string nm
01375                     = read_ascii_data (is, std::string (), dummy, t2, j);
01376 
01377                   if (! is)
01378                     break;
01379 
01380                   Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
01381 
01382                   if (error_state)
01383                     {
01384                       error ("load: internal error loading class elements");
01385                       return false;
01386                     }
01387 
01388                   m.assign (nm, tcell);
01389                 }
01390 
01391               if (is)
01392                 {
01393                   c_name = classname;
01394                   reconstruct_exemplar ();
01395 
01396                   map = m;
01397 
01398                   if (! reconstruct_parents ())
01399                     warning ("load: unable to reconstruct object inheritance");
01400                   else
01401                     {
01402                       if (load_path::find_method (classname, "loadobj")
01403                           != std::string ())
01404                         {
01405                           octave_value in = new octave_class (*this);
01406                           octave_value_list tmp = feval ("loadobj", in, 1);
01407 
01408                           if (! error_state)
01409                             map = tmp(0).map_value ();
01410                           else
01411                             success = false;
01412                         }
01413                     }
01414                 }
01415               else
01416                 {
01417                   error ("load: failed to load class");
01418                   success = false;
01419                 }
01420             }
01421           else if (len == 0 )
01422             {
01423               map = octave_map (dim_vector (1, 1));
01424               c_name = classname;
01425             }
01426           else
01427             panic_impossible ();
01428         }
01429       else
01430         {
01431           error ("load: failed to extract number of elements in class");
01432           success = false;
01433         }
01434     }
01435   else
01436     {
01437       error ("load: failed to extract name of class");
01438       success = false;
01439     }
01440 
01441   return success;
01442 }
01443 
01444 bool
01445 octave_class::save_binary (std::ostream& os, bool& save_as_floats)
01446 {
01447   int32_t classname_len = class_name().length ();
01448 
01449   os.write (reinterpret_cast<char *> (&classname_len), 4);
01450   os << class_name ();
01451 
01452   octave_map m;
01453   if (load_path::find_method (class_name (), "saveobj") != std::string ())
01454     {
01455       octave_value in = new octave_class (*this);
01456       octave_value_list tmp = feval ("saveobj", in, 1);
01457       if (! error_state)
01458         m = tmp(0).map_value ();
01459       else
01460         return false;
01461     }
01462   else
01463     m = map_value ();
01464 
01465   int32_t len = m.nfields();
01466   os.write (reinterpret_cast<char *> (&len), 4);
01467 
01468   octave_map::iterator i = m.begin ();
01469   while (i != m.end ())
01470     {
01471       octave_value val = map.contents (i);
01472 
01473       bool b = save_binary_data (os, val, m.key (i), "", 0, save_as_floats);
01474 
01475       if (! b)
01476         return os;
01477 
01478       i++;
01479     }
01480 
01481   return true;
01482 }
01483 
01484 bool
01485 octave_class::load_binary (std::istream& is, bool swap,
01486                             oct_mach_info::float_format fmt)
01487 {
01488   bool success = true;
01489 
01490   int32_t classname_len;
01491 
01492   is.read (reinterpret_cast<char *> (&classname_len), 4);
01493   if (! is)
01494     return false;
01495   else if (swap)
01496     swap_bytes<4> (&classname_len);
01497 
01498   {
01499     OCTAVE_LOCAL_BUFFER (char, classname, classname_len+1);
01500     classname[classname_len] = '\0';
01501     if (! is.read (reinterpret_cast<char *> (classname), classname_len))
01502       return false;
01503     c_name = classname;
01504   }
01505   reconstruct_exemplar ();
01506 
01507   int32_t len;
01508   if (! is.read (reinterpret_cast<char *> (&len), 4))
01509     return false;
01510   if (swap)
01511     swap_bytes<4> (&len);
01512 
01513   if (len > 0)
01514     {
01515       octave_map m (map);
01516 
01517       for (octave_idx_type j = 0; j < len; j++)
01518         {
01519           octave_value t2;
01520           bool dummy;
01521           std::string doc;
01522 
01523           // recurse to read cell elements
01524           std::string nm = read_binary_data (is, swap, fmt, std::string (),
01525                                              dummy, t2, doc);
01526 
01527           if (! is)
01528             break;
01529 
01530           Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
01531 
01532           if (error_state)
01533             {
01534               error ("load: internal error loading class elements");
01535               return false;
01536             }
01537 
01538           m.assign (nm, tcell);
01539         }
01540 
01541       if (is)
01542         {
01543           map = m;
01544 
01545           if (! reconstruct_parents ())
01546             warning ("load: unable to reconstruct object inheritance");
01547           else
01548             {
01549               if (load_path::find_method (c_name, "loadobj") != std::string ())
01550                 {
01551                   octave_value in = new octave_class (*this);
01552                   octave_value_list tmp = feval ("loadobj", in, 1);
01553 
01554                   if (! error_state)
01555                     map = tmp(0).map_value ();
01556                   else
01557                     success = false;
01558                 }
01559             }
01560         }
01561       else
01562         {
01563           warning ("load: failed to load class");
01564           success = false;
01565         }
01566     }
01567   else if (len == 0 )
01568     map = octave_map (dim_vector (1, 1));
01569   else
01570     panic_impossible ();
01571 
01572   return success;
01573 }
01574 
01575 #if defined (HAVE_HDF5)
01576 
01577 bool
01578 octave_class::save_hdf5 (hid_t loc_id, const char *name, bool save_as_floats)
01579 {
01580   hsize_t hdims[3];
01581   hid_t group_hid = -1;
01582   hid_t type_hid = -1;
01583   hid_t space_hid = -1;
01584   hid_t class_hid = -1;
01585   hid_t data_hid = -1;
01586   octave_map m;
01587   octave_map::iterator i;
01588 
01589 #if HAVE_HDF5_18
01590   group_hid = H5Gcreate (loc_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
01591 #else
01592   group_hid = H5Gcreate (loc_id, name, 0);
01593 #endif
01594   if (group_hid < 0)
01595     goto error_cleanup;
01596 
01597   // Add the class name to the group
01598   type_hid = H5Tcopy (H5T_C_S1); H5Tset_size (type_hid, c_name.length () + 1);
01599   if (type_hid < 0)
01600     goto error_cleanup;
01601 
01602   hdims[0] = 0;
01603   space_hid = H5Screate_simple (0 , hdims, 0);
01604   if (space_hid < 0)
01605     goto error_cleanup;
01606 #if HAVE_HDF5_18
01607   class_hid = H5Dcreate (group_hid, "classname",  type_hid, space_hid,
01608                          H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
01609 #else
01610   class_hid = H5Dcreate (group_hid, "classname",  type_hid, space_hid,
01611                          H5P_DEFAULT);
01612 #endif
01613   if (class_hid < 0 || H5Dwrite (class_hid, type_hid, H5S_ALL, H5S_ALL,
01614                                     H5P_DEFAULT, c_name.c_str ()) < 0)
01615     goto error_cleanup;
01616 
01617 #if HAVE_HDF5_18
01618   data_hid = H5Gcreate (group_hid, "value", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
01619 #else
01620   data_hid = H5Gcreate (group_hid, "value", 0);
01621 #endif
01622   if (data_hid < 0)
01623     goto error_cleanup;
01624 
01625   if (load_path::find_method (class_name (), "saveobj") != std::string ())
01626     {
01627       octave_value in = new octave_class (*this);
01628       octave_value_list tmp = feval ("saveobj", in, 1);
01629       if (! error_state)
01630         m = tmp(0).map_value ();
01631       else
01632         goto error_cleanup;
01633     }
01634   else
01635     m = map_value ();
01636 
01637   // recursively add each element of the class to this group
01638   i = m.begin ();
01639   while (i != m.end ())
01640     {
01641       octave_value val = map.contents (i);
01642 
01643       bool retval2 = add_hdf5_data (data_hid, val, m.key (i), "", false,
01644                                     save_as_floats);
01645 
01646       if (! retval2)
01647         break;
01648 
01649       i++;
01650     }
01651 
01652  error_cleanup:
01653 
01654   if (data_hid > 0)
01655     H5Gclose (data_hid);
01656 
01657   if (class_hid > 0)
01658     H5Dclose (class_hid);
01659 
01660   if (space_hid > 0)
01661     H5Sclose (space_hid);
01662 
01663   if (type_hid > 0)
01664     H5Tclose (type_hid);
01665 
01666   if (group_hid > 0)
01667     H5Gclose (group_hid);
01668 
01669   return true;
01670 }
01671 
01672 bool
01673 octave_class::load_hdf5 (hid_t loc_id, const char *name)
01674 {
01675   bool retval = false;
01676 
01677   hid_t group_hid = -1;
01678   hid_t data_hid = -1;
01679   hid_t type_hid = -1;
01680   hid_t type_class_hid = -1;
01681   hid_t space_hid = -1;
01682   hid_t subgroup_hid = -1;
01683   hid_t st_id = -1;
01684 
01685   hdf5_callback_data dsub;
01686 
01687   herr_t retval2 = 0;
01688   octave_map m (dim_vector (1, 1));
01689   int current_item = 0;
01690   hsize_t num_obj = 0;
01691   int slen = 0;
01692   hsize_t rank = 0;
01693 
01694 #if HAVE_HDF5_18
01695   group_hid = H5Gopen (loc_id, name, H5P_DEFAULT);
01696 #else
01697   group_hid = H5Gopen (loc_id, name);
01698 #endif
01699   if (group_hid < 0)
01700     goto error_cleanup;
01701 
01702 #if HAVE_HDF5_18
01703   data_hid = H5Dopen (group_hid, "classname", H5P_DEFAULT);
01704 #else
01705   data_hid = H5Dopen (group_hid, "classname");
01706 #endif
01707 
01708   if (data_hid < 0)
01709     goto error_cleanup;
01710 
01711   type_hid = H5Dget_type (data_hid);
01712 
01713   type_class_hid = H5Tget_class (type_hid);
01714 
01715   if (type_class_hid != H5T_STRING)
01716     goto error_cleanup;
01717 
01718   space_hid = H5Dget_space (data_hid);
01719   rank = H5Sget_simple_extent_ndims (space_hid);
01720 
01721   if (rank != 0)
01722     goto error_cleanup;
01723 
01724   slen = H5Tget_size (type_hid);
01725   if (slen < 0)
01726     goto error_cleanup;
01727 
01728   // do-while loop here to prevent goto crossing initialization of classname
01729   do
01730     {
01731       OCTAVE_LOCAL_BUFFER (char, classname, slen);
01732 
01733       // create datatype for (null-terminated) string to read into:
01734       st_id = H5Tcopy (H5T_C_S1);
01735       H5Tset_size (st_id, slen);
01736 
01737       if (H5Dread (data_hid, st_id, H5S_ALL, H5S_ALL, H5P_DEFAULT,
01738                    classname) < 0)
01739         {
01740           H5Tclose (st_id);
01741           H5Dclose (data_hid);
01742           H5Gclose (group_hid);
01743           return false;
01744         }
01745 
01746       H5Tclose (st_id);
01747       H5Dclose (data_hid);
01748       data_hid = -1;
01749 
01750       c_name = classname;
01751     }
01752   while (0);
01753   reconstruct_exemplar ();
01754 
01755 #if HAVE_HDF5_18
01756   subgroup_hid = H5Gopen (group_hid, name, H5P_DEFAULT);
01757 #else
01758   subgroup_hid = H5Gopen (group_hid, name);
01759 #endif
01760   H5Gget_num_objs (subgroup_hid, &num_obj);
01761   H5Gclose (subgroup_hid);
01762 
01763   while (current_item < static_cast<int> (num_obj)
01764          && (retval2 = H5Giterate (group_hid, name, &current_item,
01765                                    hdf5_read_next_data, &dsub)) > 0)
01766     {
01767       octave_value t2 = dsub.tc;
01768 
01769       Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
01770 
01771       if (error_state)
01772         {
01773           error ("load: internal error loading class elements");
01774           return false;
01775         }
01776 
01777       m.assign (dsub.name, tcell);
01778 
01779     }
01780 
01781   if (retval2 >= 0)
01782     {
01783       map = m;
01784 
01785       if (!reconstruct_parents ())
01786         warning ("load: unable to reconstruct object inheritance");
01787       else
01788         {
01789           if (load_path::find_method (c_name, "loadobj") != std::string ())
01790             {
01791               octave_value in = new octave_class (*this);
01792               octave_value_list tmp = feval ("loadobj", in, 1);
01793 
01794               if (! error_state)
01795                 {
01796                   map = tmp(0).map_value ();
01797                   retval = true;
01798                 }
01799               else
01800                 retval = false;
01801             }
01802           else
01803             retval = true;
01804         }
01805     }
01806 
01807  error_cleanup:
01808   if (data_hid > 0)
01809     H5Dclose (data_hid);
01810 
01811   if (data_hid > 0)
01812     H5Gclose (group_hid);
01813 
01814   return retval;
01815 }
01816 
01817 #endif
01818 
01819 mxArray *
01820 octave_class::as_mxArray (void) const
01821 {
01822   gripe_wrong_type_arg ("octave_class::as_mxArray ()", type_name ());
01823 
01824   return 0;
01825 }
01826 
01827 bool
01828 octave_class::in_class_method (void)
01829 {
01830   octave_function *fcn = octave_call_stack::current ();
01831 
01832   return (fcn
01833           && (fcn->is_class_method ()
01834               || fcn->is_class_constructor ()
01835               || fcn->is_anonymous_function_of_class ()
01836               || fcn->is_private_function_of_class (class_name ()))
01837           && find_parent_class (fcn->dispatch_class ()));
01838 }
01839 
01840 octave_class::exemplar_info::exemplar_info (const octave_value& obj)
01841   : field_names (), parent_class_names ()
01842 {
01843   if (obj.is_object ())
01844     {
01845       octave_map m = obj.map_value ();
01846       field_names = m.keys ();
01847 
01848       parent_class_names = obj.parent_class_name_list ();
01849     }
01850   else
01851     error ("invalid call to exemplar_info constructor");
01852 }
01853 
01854 
01855 // A map from class names to lists of fields.
01856 std::map<std::string, octave_class::exemplar_info> octave_class::exemplar_map;
01857 
01858 bool
01859 octave_class::exemplar_info::compare (const octave_value& obj) const
01860 {
01861   bool retval = true;
01862 
01863   if (obj.is_object ())
01864     {
01865       if (nfields () == obj.nfields ())
01866         {
01867           octave_map obj_map = obj.map_value ();
01868           string_vector obj_fnames = obj_map.keys ();
01869           string_vector fnames = fields ();
01870 
01871           for (octave_idx_type i = 0; i < nfields (); i++)
01872             {
01873               if (obj_fnames[i] != fnames[i])
01874                 {
01875                   retval = false;
01876                   error ("mismatch in field names");
01877                   break;
01878                 }
01879             }
01880 
01881           if (nparents () == obj.nparents ())
01882             {
01883               std::list<std::string> obj_parents
01884                 = obj.parent_class_name_list ();
01885               std::list<std::string> pnames = parents ();
01886 
01887               std::list<std::string>::const_iterator p = obj_parents.begin ();
01888               std::list<std::string>::const_iterator q = pnames.begin ();
01889 
01890               while (p != obj_parents.end ())
01891                 {
01892                   if (*p++ != *q++)
01893                     {
01894                       retval = false;
01895                       error ("mismatch in parent classes");
01896                       break;
01897                     }
01898                 }
01899             }
01900           else
01901             {
01902               retval = false;
01903               error ("mismatch in number of parent classes");
01904             }
01905         }
01906       else
01907         {
01908           retval = false;
01909           error ("mismatch in number of fields");
01910         }
01911     }
01912   else
01913     {
01914       retval = false;
01915       error ("invalid comparison of class exemplar to non-class object");
01916     }
01917 
01918   return retval;
01919 }
01920 
01921 DEFUN (class, args, ,
01922   "-*- texinfo -*-\n\
01923 @deftypefn  {Built-in Function} {} class (@var{expr})\n\
01924 @deftypefnx {Built-in Function} {} class (@var{s}, @var{id})\n\
01925 @deftypefnx {Built-in Function} {} class (@var{s}, @var{id}, @var{p}, @dots{})\n\
01926 Return the class of the expression @var{expr} or create a class with\n\
01927 fields from structure @var{s} and name (string) @var{id}.  Additional\n\
01928 arguments name a list of parent classes from which the new class is\n\
01929 derived.\n\
01930 @end deftypefn")
01931 {
01932   octave_value retval;
01933 
01934   int nargin = args.length ();
01935 
01936   if (nargin == 0)
01937     print_usage ();
01938   else if (nargin == 1)
01939     retval = args(0).class_name ();
01940   else
01941     {
01942       octave_function *fcn = octave_call_stack::caller ();
01943 
01944       std::string id = args(1).string_value ();
01945 
01946       if (! error_state)
01947         {
01948           if (fcn)
01949             {
01950               if (fcn->is_class_constructor (id) || fcn->is_class_method (id))
01951                 {
01952                   octave_map m = args(0).map_value ();
01953 
01954                   if (! error_state)
01955                     {
01956                       if (nargin == 2)
01957                         retval
01958                           = octave_value (new octave_class
01959                                           (m, id, std::list<std::string> ()));
01960                       else
01961                         {
01962                           octave_value_list parents = args.slice (2, nargin-2);
01963 
01964                           retval
01965                             = octave_value (new octave_class (m, id, parents));
01966                         }
01967 
01968                       if (! error_state)
01969                         {
01970                           octave_class::exemplar_const_iterator it
01971                             = octave_class::exemplar_map.find (id);
01972 
01973                           if (it == octave_class::exemplar_map.end ())
01974                             octave_class::exemplar_map[id]
01975                               = octave_class::exemplar_info (retval);
01976                           else if (! it->second.compare (retval))
01977                             error ("class: object of class '%s' does not match previously constructed objects",
01978                                    id.c_str ());
01979                         }
01980                     }
01981                   else
01982                     error ("class: expecting structure S as first argument");
01983                 }
01984               else
01985                 error ("class: '%s' is invalid as a class name in this context",
01986                        id.c_str ());
01987             }
01988           else
01989             error ("class: invalid call from outside class constructor or method");
01990         }
01991       else
01992         error ("class: ID (class name) must be a character string");
01993     }
01994 
01995   return retval;
01996 }
01997 
01998 DEFUN (__isa_parent__, args, ,
01999   "-*- texinfo -*-\n\
02000 @deftypefn {Built-in Function} {} __isa_parent__ (@var{class}, @var{name})\n\
02001 Undocumented internal function.\n\
02002 @end deftypefn")
02003 {
02004   octave_value retval = false;
02005 
02006   if (args.length () == 2)
02007     {
02008       octave_value cls = args(0);
02009       octave_value nm = args(1);
02010 
02011       if (! error_state)
02012         {
02013           if (cls.find_parent_class (nm.string_value ()))
02014             retval = true;
02015         }
02016       else
02017         error ("__isa_parent__: expecting arguments to be character strings");
02018     }
02019   else
02020     print_usage ();
02021 
02022   return retval;
02023 }
02024 
02025 DEFUN (__parent_classes__, args, ,
02026   "-*- texinfo -*-\n\
02027 @deftypefn {Built-in Function} {} __parent_classes__ (@var{x})\n\
02028 Undocumented internal function.\n\
02029 @end deftypefn")
02030 {
02031   octave_value retval = Cell ();
02032 
02033   if (args.length () == 1)
02034     {
02035       octave_value arg = args(0);
02036 
02037       if (arg.is_object ())
02038         retval = Cell (arg.parent_class_names ());
02039     }
02040   else
02041     print_usage ();
02042 
02043   return retval;
02044 }
02045 
02046 DEFUN (isobject, args, ,
02047   "-*- texinfo -*-\n\
02048 @deftypefn {Built-in Function} {} isobject (@var{x})\n\
02049 Return true if @var{x} is a class object.\n\
02050 @seealso{class, typeinfo, isa, ismethod}\n\
02051 @end deftypefn")
02052 {
02053   octave_value retval;
02054 
02055   if (args.length () == 1)
02056     retval = args(0).is_object ();
02057   else
02058     print_usage ();
02059 
02060   return retval;
02061 }
02062 
02063 DEFUN (ismethod, args, ,
02064   "-*- texinfo -*-\n\
02065 @deftypefn {Built-in Function} {} ismethod (@var{x}, @var{method})\n\
02066 Return true if @var{x} is a class object and the string @var{method}\n\
02067 is a method of this class.\n\
02068 @seealso{isobject}\n\
02069 @end deftypefn")
02070 {
02071   octave_value retval;
02072 
02073   if (args.length () == 2)
02074     {
02075       octave_value arg = args(0);
02076 
02077       std::string class_name;
02078 
02079       if (arg.is_object ())
02080         class_name = arg.class_name ();
02081       else if (arg.is_string ())
02082         class_name = arg.string_value ();
02083       else
02084         error ("ismethod: expecting object or class name as first argument");
02085 
02086       if (! error_state)
02087         {
02088           std::string method = args(1).string_value ();
02089 
02090           if (! error_state)
02091             {
02092               if (load_path::find_method (class_name, method) != std::string ())
02093                 retval = true;
02094               else
02095                 retval = false;
02096             }
02097         }
02098     }
02099   else
02100     print_usage ();
02101 
02102   return retval;
02103 }
02104 
02105 DEFUN (methods, args, nargout,
02106   "-*- texinfo -*-\n\
02107 @deftypefn  {Built-in Function} {} methods (@var{x})\n\
02108 @deftypefnx {Built-in Function} {} methods (\"classname\")\n\
02109 Return a cell array containing the names of the methods for the\n\
02110 object @var{x} or the named class.\n\
02111 @end deftypefn")
02112 {
02113   octave_value retval;
02114 
02115   if (args.length () == 1)
02116     {
02117       octave_value arg = args(0);
02118 
02119       std::string class_name;
02120 
02121       if (arg.is_object ())
02122         class_name = arg.class_name ();
02123       else if (arg.is_string ())
02124         class_name = arg.string_value ();
02125       else
02126         error ("methods: expecting object or class name as argument");
02127 
02128       if (! error_state)
02129         {
02130           string_vector sv = load_path::methods (class_name);
02131 
02132           if (nargout == 0)
02133             {
02134               octave_stdout << "Methods for class " << class_name << ":\n\n";
02135 
02136               sv.list_in_columns (octave_stdout);
02137 
02138               octave_stdout << std::endl;
02139             }
02140           else
02141             retval = Cell (sv);
02142         }
02143     }
02144   else
02145     print_usage ();
02146 
02147   return retval;
02148 }
02149 
02150 static bool
02151 is_built_in_class (const std::string& cn)
02152 {
02153   static std::set<std::string> built_in_class_names;
02154 
02155   if (built_in_class_names.empty ())
02156     {
02157       built_in_class_names.insert ("double");
02158       built_in_class_names.insert ("single");
02159       built_in_class_names.insert ("cell");
02160       built_in_class_names.insert ("struct");
02161       built_in_class_names.insert ("logical");
02162       built_in_class_names.insert ("char");
02163       built_in_class_names.insert ("function handle");
02164       built_in_class_names.insert ("int8");
02165       built_in_class_names.insert ("uint8");
02166       built_in_class_names.insert ("int16");
02167       built_in_class_names.insert ("uint16");
02168       built_in_class_names.insert ("int32");
02169       built_in_class_names.insert ("uint32");
02170       built_in_class_names.insert ("int64");
02171       built_in_class_names.insert ("uint64");
02172     }
02173 
02174   return built_in_class_names.find (cn) != built_in_class_names.end ();
02175 }
02176 
02177 DEFUN (superiorto, args, ,
02178   "-*- texinfo -*-\n\
02179 @deftypefn {Built-in Function} {} superiorto (@var{class_name}, @dots{})\n\
02180 When called from a class constructor, mark the object currently\n\
02181 constructed as having a higher precedence than @var{class_name}.\n\
02182 More that one such class can be specified in a single call.\n\
02183 This function may only be called from a class constructor.\n\
02184 @end deftypefn")
02185 {
02186   octave_value retval;
02187 
02188   octave_function *fcn = octave_call_stack::caller ();
02189 
02190   if (fcn && fcn->is_class_constructor ())
02191     {
02192       for (int i = 0; i < args.length(); i++)
02193         {
02194           std::string class_name = args(i).string_value ();
02195 
02196           if (! error_state)
02197             {
02198               if (! is_built_in_class (class_name))
02199                 {
02200                   std::string this_class_name = fcn->name ();
02201 
02202                   if (! symbol_table::set_class_relationship (this_class_name,
02203                                                               class_name))
02204                     {
02205                       error ("superiorto: precedence already set for %s and %s",
02206                              this_class_name.c_str (), class_name.c_str ());
02207                       break;
02208                     }
02209                 }
02210               else
02211                 {
02212                   // User defined classes always have higher precedence
02213                   // than built-in classes.
02214                 }
02215             }
02216           else
02217             {
02218               error ("superiorto: expecting argument to be class name");
02219               break;
02220             }
02221         }
02222     }
02223   else
02224     error ("superiorto: invalid call from outside class constructor");
02225 
02226   return retval;
02227 }
02228 
02229 DEFUN (inferiorto, args, ,
02230   "-*- texinfo -*-\n\
02231 @deftypefn {Built-in Function} {} inferiorto (@var{class_name}, @dots{})\n\
02232 When called from a class constructor, mark the object currently\n\
02233 constructed as having a lower precedence than @var{class_name}.\n\
02234 More that one such class can be specified in a single call.\n\
02235 This function may only be called from a class constructor.\n\
02236 @end deftypefn")
02237 {
02238   octave_value retval;
02239 
02240   octave_function *fcn = octave_call_stack::caller ();
02241 
02242   if (fcn && fcn->is_class_constructor ())
02243     {
02244       for (int i = 0; i < args.length(); i++)
02245         {
02246           std::string class_name = args(i).string_value ();
02247 
02248           if (! error_state)
02249             {
02250               if (! is_built_in_class (class_name))
02251                 {
02252                   std::string this_class_name = fcn->name ();
02253 
02254                   symbol_table::set_class_relationship (class_name,
02255                                                         this_class_name);
02256 
02257                   if (! symbol_table::set_class_relationship (this_class_name,
02258                                                               class_name))
02259                     {
02260                       error ("inferiorto: precedence already set for %s and %s",
02261                              this_class_name.c_str (), class_name.c_str ());
02262                       break;
02263                     }
02264                 }
02265               else
02266                 {
02267                   error ("inferiorto: cannot give user-defined class lower precedence than built-in class");
02268                   break;
02269                 }
02270             }
02271           else
02272             {
02273               error ("inferiorto: expecting argument to be class name");
02274               break;
02275             }
02276         }
02277     }
02278   else
02279     error ("inferiorto: invalid call from outside class constructor");
02280 
02281   return retval;
02282 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines