GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ov-class.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2013 John W. Eaton
4 Copyright (C) 2009 VZLU Prague
5 
6 This file is part of Octave.
7 
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <iostream>
29 
30 #include "Array-util.h"
31 #include "byte-swap.h"
32 #include "oct-locbuf.h"
33 #include "lo-mappers.h"
34 
35 #include "Cell.h"
36 #include "defun.h"
37 #include "error.h"
38 #include "file-ops.h"
39 #include "gripes.h"
40 #include "load-path.h"
41 #include "ls-hdf5.h"
42 #include "ls-oct-ascii.h"
43 #include "ls-oct-binary.h"
44 #include "ls-utils.h"
45 #include "mxarray.h"
46 #include "oct-lvalue.h"
47 #include "ov-class.h"
48 #ifdef HAVE_JAVA
49 #include "ov-java.h"
50 #endif
51 #include "ov-fcn.h"
52 #include "ov-usr-fcn.h"
53 #include "pager.h"
54 #include "parse.h"
55 #include "pr-output.h"
56 #include "toplev.h"
57 #include "unwind-prot.h"
58 #include "variables.h"
59 
61 
62 int octave_class::t_id (-1);
63 
64 const std::string octave_class::t_name ("class");
65 
66 void
68 {
70  (octave_class::t_name, "<unknown>",
71  octave_value (new octave_class ()));
72 }
73 
74 octave_class::octave_class (const octave_map& m, const std::string& id,
75  const octave_value_list& parents)
76  : octave_base_value (), map (m), c_name (id), obsolete_copies (0)
77 {
78  octave_idx_type n = parents.length ();
79 
80  for (octave_idx_type idx = 0; idx < n; idx++)
81  {
82  octave_value parent = parents(idx);
83 
84  if (! parent.is_object ())
85  error ("parents must be objects");
86  else
87  {
88  std::string pcnm = parent.class_name ();
89 
90  if (find_parent_class (pcnm))
91  error ("duplicate class in parent tree");
92  else
93  {
94  parent_list.push_back (pcnm);
95 
96  octave_idx_type nel = map.numel ();
97  octave_idx_type p_nel = parent.numel ();
98 
99  if (nel == 0)
100  {
101  if (p_nel == 0)
102  {
103  // No elements in MAP or the parent class object,
104  // so just add the field name.
105 
106  map.assign (pcnm, Cell (map.dims ()));
107  }
108  else if (p_nel == 1)
109  {
110  if (map.nfields () == 0)
111  {
112  // No elements or fields in MAP, but the
113  // parent is class object with one element.
114  // Resize to match size of parent class and
115  // make the parent a field in MAP.
116 
117  map.resize (parent.dims ());
118 
119  map.assign (pcnm, parent);
120  }
121  else
122  {
123  // No elements in MAP, but we have at least
124  // one field. So don't resize, just add the
125  // field name.
126 
127  map.assign (pcnm, Cell (map.dims ()));
128  }
129  }
130  else if (map.nfields () == 0)
131  {
132  // No elements or fields in MAP and more than one
133  // element in the parent class object, so we can
134  // resize MAP to match parent dimsenions, then
135  // distribute the elements of the parent object to
136  // the elements of MAP.
137 
138  dim_vector parent_dims = parent.dims ();
139 
140  map.resize (parent_dims);
141 
142  Cell c (parent_dims);
143 
144  octave_map pmap = parent.map_value ();
145 
146  std::list<std::string> plist
147  = parent.parent_class_name_list ();
148 
149  for (octave_idx_type i = 0; i < p_nel; i++)
150  c(i) = octave_value (pmap.index (i), pcnm, plist);
151 
152  map.assign (pcnm, c);
153  }
154  else
155  error ("class: parent class dimension mismatch");
156  }
157  else if (nel == 1 && p_nel == 1)
158  {
159  // Simple assignment.
160 
161  map.assign (pcnm, parent);
162  }
163  else
164  {
165  if (p_nel == 1)
166  {
167  // Broadcast the scalar parent class object to
168  // each element of MAP.
169 
170  Cell pcell (map.dims (), parent);
171 
172  map.assign (pcnm, pcell);
173  }
174 
175  else if (nel == p_nel)
176  {
177  // FIXME: is there a better way to do this?
178 
179  // The parent class object has the same number of
180  // elements as the map we are using to create the
181  // new object, so distribute those elements to
182  // each element of the new object by first
183  // splitting the elements of the parent class
184  // object into a cell array with one element per
185  // cell. Then do the assignment all at once.
186 
187  Cell c (parent.dims ());
188 
189  octave_map pmap = parent.map_value ();
190 
191  std::list<std::string> plist
192  = parent.parent_class_name_list ();
193 
194  for (octave_idx_type i = 0; i < p_nel; i++)
195  c(i) = octave_value (pmap.index (i), pcnm, plist);
196 
197  map.assign (pcnm, c);
198  }
199  else
200  error ("class: parent class dimension mismatch");
201  }
202  }
203  }
204  }
205 
206  if (! error_state)
208 }
209 
212 {
213  if (count == obsolete_copies)
214  {
215  // All remaining copies are obsolete. We don't actually need to clone.
216  count++;
217  return this;
218  }
219  else
220  {
221  // In theory, this shouldn't be happening, but it's here just in case.
222  if (count < obsolete_copies)
223  obsolete_copies = 0;
224 
225  return clone ();
226  }
227 }
228 
229 std::string
231 {
232  std::string retval = class_name ();
233 
234  if (nparents () > 0)
235  {
237 
238  // Here we are just looking to see if FCN is a method or constructor
239  // for any class, not specifically this one.
240  if (fcn && (fcn->is_class_method () || fcn->is_class_constructor ()))
241  retval = fcn->dispatch_class ();
242  }
243 
244  return retval;
245 }
246 
247 static void
249 {
250  error ("invalid index for class");
251 }
252 
253 static void
255 {
256  error ("invalid index for class assignment");
257 }
258 
259 static void
260 gripe_invalid_index_type (const std::string& nm, char t)
261 {
262  error ("%s cannot be indexed with %c", nm.c_str (), t);
263 }
264 
265 static void
267 {
268  error ("assignment to class element failed");
269 }
270 
271 static inline octave_value_list
273 {
274  octave_value_list retval = ovl;
275 
276  for (octave_idx_type i = 0; i < ovl.length (); i++)
277  {
278  if (retval(i).is_magic_colon ())
279  retval(i) = ":";
280  }
281 
282  return retval;
283 }
284 
285 static inline octave_value
286 make_idx_args (const std::string& type,
287  const std::list<octave_value_list>& idx,
288  const std::string& who)
289 {
290  octave_value retval;
291 
292  size_t len = type.length ();
293 
294  if (len == idx.size ())
295  {
296  Cell type_field (1, len);
297  Cell subs_field (1, len);
298 
299  std::list<octave_value_list>::const_iterator p = idx.begin ();
300 
301  for (size_t i = 0; i < len; i++)
302  {
303  char t = type[i];
304 
305  switch (t)
306  {
307  case '(':
308  type_field(i) = "()";
309  subs_field(i) = Cell (sanitize (*p++));
310  break;
311 
312  case '{':
313  type_field(i) = "{}";
314  subs_field(i) = Cell (sanitize (*p++));
315  break;
316 
317  case '.':
318  {
319  type_field(i) = ".";
320 
321  octave_value_list vlist = *p++;
322 
323  if (vlist.length () == 1)
324  {
325  octave_value val = vlist(0);
326 
327  if (val.is_string ())
328  subs_field(i) = val;
329  else
330  {
331  error ("expecting character string argument for '.' index");
332  return retval;
333  }
334  }
335  else
336  {
337  error ("expecting single argument for '.' index");
338  return retval;
339  }
340  }
341  break;
342 
343  default:
344  panic_impossible ();
345  break;
346  }
347  }
348 
349  octave_map m;
350 
351  m.assign ("type", type_field);
352  m.assign ("subs", subs_field);
353 
354  retval = m;
355  }
356  else
357  error ("invalid index for %s", who.c_str ());
358 
359  return retval;
360 }
361 
362 Cell
364 {
365  Cell retval;
366 
367  assert (idx.length () == 1);
368 
369  std::string method_class = get_current_method_class ();
370 
371  // Find the class in which this method resides before attempting to access
372  // the requested field.
373 
374  octave_base_value *obvp = find_parent_class (method_class);
375 
376  if (obvp == 0)
377  {
378  error ("malformed class");
379  return retval;
380  }
381 
382  octave_map my_map = (obvp != this) ? obvp->map_value () : map;
383 
384  std::string nm = idx(0).string_value ();
385 
386  if (! error_state)
387  {
388  octave_map::const_iterator p = my_map.seek (nm);
389 
390  if (p != my_map.end ())
391  retval = my_map.contents (p);
392  else
393  error ("class has no member '%s'", nm.c_str ());
394  }
395  else
397 
398  return retval;
399 }
400 
401 static bool
403 {
405 
406  // FIXME: we probably need a better check here, or some other
407  // mechanism to avoid overloaded functions when builtin is used.
408  // For example, what if someone overloads the builtin function?
409  // Also, are there other places where using builtin is not properly
410  // avoiding dispatch?
411 
412  return (fcn && fcn->name () == "builtin");
413 }
414 
415 Matrix
417 {
419  return octave_base_value::size ();
420 
421  Matrix retval (1, 2, 1.0);
423 
424  if (meth.is_defined ())
425  {
426  count++;
427  octave_value_list args (1, octave_value (this));
428 
429  octave_value_list lv = feval (meth.function_value (), args, 1);
430  if (lv.length () > 0
431  && lv(0).is_matrix_type () && lv(0).dims ().is_vector ())
432  retval = lv(0).matrix_value ();
433  else
434  error ("@%s/size: invalid return value", class_name ().c_str ());
435  }
436  else
437  {
438  dim_vector dv = dims ();
439 
440  int nd = dv.length ();
441 
442  retval.resize (1, nd);
443 
444  for (int i = 0; i < nd; i++)
445  retval(i) = dv(i);
446  }
447 
448  return retval;
449 }
450 
453 {
455  return octave_base_value::numel (idx);
456 
457  octave_idx_type retval = -1;
458  const std::string cn = class_name ();
459 
460  octave_value meth = symbol_table::find_method ("numel", cn);
461 
462  if (meth.is_defined ())
463  {
464  octave_value_list args (idx.length () + 1, octave_value ());
465 
466  count++;
467  args(0) = octave_value (this);
468 
469  for (octave_idx_type i = 0; i < idx.length (); i++)
470  args(i+1) = idx(i);
471 
472  octave_value_list lv = feval (meth.function_value (), args, 1);
473  if (lv.length () == 1 && lv(0).is_scalar_type ())
474  retval = lv(0).idx_type_value (true);
475  else
476  error ("@%s/numel: invalid return value", cn.c_str ());
477  }
478  else
479  retval = octave_base_value::numel (idx);
480 
481  return retval;
482 }
483 
485 octave_class::subsref (const std::string& type,
486  const std::list<octave_value_list>& idx,
487  int nargout)
488 {
489  octave_value_list retval;
490 
492  {
493  // FIXME: this block of code is the same as the body of
494  // octave_struct::subsref. Maybe it could be shared instead of
495  // duplicated.
496 
497  int skip = 1;
498 
499  switch (type[0])
500  {
501  case '(':
502  {
503  if (type.length () > 1 && type[1] == '.')
504  {
505  std::list<octave_value_list>::const_iterator p = idx.begin ();
506  octave_value_list key_idx = *++p;
507 
508  Cell tmp = dotref (key_idx);
509 
510  if (! error_state)
511  {
512  Cell t = tmp.index (idx.front ());
513 
514  retval(0) = (t.length () == 1) ? t(0)
515  : octave_value (t, true);
516 
517  // We handled two index elements, so tell
518  // next_subsref to skip both of them.
519 
520  skip++;
521  }
522  }
523  else
524  retval(0) = octave_value (map.index (idx.front ()),
526  }
527  break;
528 
529  case '.':
530  {
531  if (map.numel () > 0)
532  {
533  Cell t = dotref (idx.front ());
534 
535  retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true);
536  }
537  }
538  break;
539 
540  case '{':
541  gripe_invalid_index_type (type_name (), type[0]);
542  break;
543 
544  default:
545  panic_impossible ();
546  }
547 
548  // FIXME: perhaps there should be an
549  // octave_value_list::next_subsref member function? See also
550  // octave_user_function::subsref.
551 
552  if (idx.size () > 1)
553  retval = retval(0).next_subsref (nargout, type, idx, skip);
554  }
555  else
556  {
557  octave_value meth = symbol_table::find_method ("subsref", class_name ());
558 
559  if (meth.is_defined ())
560  {
561  octave_value_list args;
562 
563  args(1) = make_idx_args (type, idx, "subsref");
564 
565  if (error_state)
566  return octave_value_list ();
567 
568  count++;
569  args(0) = octave_value (this);
570 
571  // FIXME: for Matlab compatibility, let us attempt to set up a proper
572  // value for nargout at least in the simple case where the
573  // cs-list-type expression - i.e., {} or ().x, is the leading one.
574  // Note that Octave does not actually need this, since it will
575  // be able to properly react to varargout a posteriori.
576  bool maybe_cs_list_query = (type[0] == '.' || type[0] == '{'
577  || (type.length () > 1 && type[0] == '('
578  && type[1] == '.'));
579 
580  int true_nargout = nargout;
581 
582  if (maybe_cs_list_query)
583  {
584  // Set up a proper nargout for the subsref call by calling numel.
585  octave_value_list tmp;
586  if (type[0] != '.') tmp = idx.front ();
587  true_nargout = numel (tmp);
588  }
589 
590  retval = feval (meth.function_value (), args, true_nargout);
591 
592  // Since we're handling subsref, return the list in the first value
593  // if it has more than one element, to be able to pass through
594  // rvalue1 calls.
595  if (retval.length () > 1)
596  retval = octave_value (retval, true);
597  }
598  else
599  {
600  if (type.length () == 1 && type[0] == '(')
601  retval(0) = octave_value (map.index (idx.front ()), c_name,
602  parent_list);
603  else
605  }
606  }
607 
608  return retval;
609 }
610 
612 octave_class::numeric_conv (const Cell& val, const std::string& type)
613 {
614  octave_value retval;
615 
616  if (val.length () == 1)
617  {
618  retval = val(0);
619 
620  if (type.length () > 0 && type[0] == '.' && ! retval.is_map ())
621  retval = octave_map ();
622  }
623  else
625 
626  return retval;
627 }
628 
630 octave_class::subsasgn (const std::string& type,
631  const std::list<octave_value_list>& idx,
632  const octave_value& rhs)
633 {
634  count++;
635  return subsasgn_common (octave_value (this), type, idx, rhs);
636 }
637 
639 octave_class::undef_subsasgn (const std::string& type,
640  const std::list<octave_value_list>& idx,
641  const octave_value& rhs)
642 {
643  // For compatibility with Matlab, pass [] as the first argument to the
644  // the subsasgn function when the LHS of an indexed assignment is
645  // undefined.
646 
647  return subsasgn_common (Matrix (), type, idx, rhs);
648 }
649 
652  const std::string& type,
653  const std::list<octave_value_list>& idx,
654  const octave_value& rhs)
655 {
656  octave_value retval;
657 
658  if (! (in_class_method () || called_from_builtin ()))
659  {
660  octave_value meth = symbol_table::find_method ("subsasgn", class_name ());
661 
662  if (meth.is_defined ())
663  {
664  octave_value_list args;
665 
666  if (rhs.is_cs_list ())
667  {
668  octave_value_list lrhs = rhs.list_value ();
669  args.resize (2 + lrhs.length ());
670  for (octave_idx_type i = 0; i < lrhs.length (); i++)
671  args(2+i) = lrhs(i);
672  }
673  else
674  args(2) = rhs;
675 
676  args(1) = make_idx_args (type, idx, "subsasgn");
677 
678  if (error_state)
679  return octave_value_list ();
680 
681  args(0) = obj;
682 
683  // Now comes the magic. Count copies with me:
684  // 1. myself (obsolete)
685  // 2. the copy inside args (obsolete)
686  // 3. the copy in method's symbol table (working)
687  // ... possibly more (not obsolete).
688  //
689  // So we mark 2 copies as obsolete and hold our fingers crossed.
690  // But prior to doing that, check whether the routine is amenable
691  // to the optimization.
692  // It is essential that the handling function doesn't store extra
693  // copies anywhere. If it does, things will not break but the
694  // optimization won't work.
695 
696  octave_value_list tmp;
697 
698  if (obsolete_copies == 0 && meth.is_user_function ()
700  {
701  unwind_protect frame;
703  obsolete_copies = 2;
704 
705  tmp = feval (meth.function_value (), args);
706  }
707  else
708  tmp = feval (meth.function_value (), args);
709 
710  // FIXME: should the subsasgn method be able to return
711  // more than one value?
712 
713  if (tmp.length () > 1)
714  error ("expecting single return value from @%s/subsasgn",
715  class_name ().c_str ());
716 
717  else
718  retval = tmp(0);
719 
720  return retval;
721  }
722  }
723 
724  // Find the class in which this method resides before
725  // attempting to do the indexed assignment.
726 
727  std::string method_class = get_current_method_class ();
728 
729  octave_base_value *obvp = unique_parent_class (method_class);
730  if (obvp != this)
731  {
732 
733  if (obvp)
734  {
735  obvp->subsasgn (type, idx, rhs);
736  if (! error_state)
737  {
738  count++;
739  retval = octave_value (this);
740  }
741  else
743  }
744  else
745  error ("malformed class");
746 
747  return retval;
748  }
749 
750  // FIXME: this block of code is the same as the body of
751  // octave_struct::subsasgn. Maybe it could be shared instead of
752  // duplicated.
753 
754  int n = type.length ();
755 
756  octave_value t_rhs = rhs;
757 
758  if (n > 1 && ! (type.length () == 2 && type[0] == '(' && type[1] == '.'))
759  {
760  switch (type[0])
761  {
762  case '(':
763  {
764  if (type.length () > 1 && type[1] == '.')
765  {
766  std::list<octave_value_list>::const_iterator p = idx.begin ();
767  octave_value_list t_idx = *p;
768 
769  octave_value_list key_idx = *++p;
770 
771  assert (key_idx.length () == 1);
772 
773  std::string key = key_idx(0).string_value ();
774 
775  if (! error_state)
776  {
777  octave_value u;
778 
779  if (! map.contains (key))
780  u = octave_value::empty_conv (type.substr (2), rhs);
781  else
782  {
783  Cell map_val = map.contents (key);
784 
785  Cell map_elt = map_val.index (idx.front (), true);
786 
787  u = numeric_conv (map_elt, type.substr (2));
788  }
789 
790  if (! error_state)
791  {
792  std::list<octave_value_list> next_idx (idx);
793 
794  // We handled two index elements, so subsasgn to
795  // needs to skip both of them.
796 
797  next_idx.erase (next_idx.begin ());
798  next_idx.erase (next_idx.begin ());
799 
800  u.make_unique ();
801 
802  t_rhs = u.subsasgn (type.substr (2), next_idx, rhs);
803  }
804  }
805  else
807  }
808  else
810  }
811  break;
812 
813  case '.':
814  {
815  octave_value_list key_idx = idx.front ();
816 
817  assert (key_idx.length () == 1);
818 
819  std::string key = key_idx(0).string_value ();
820 
821  std::list<octave_value_list> next_idx (idx);
822 
823  next_idx.erase (next_idx.begin ());
824 
825  std::string next_type = type.substr (1);
826 
827  Cell tmpc (1, 1);
828  octave_map::iterator pkey = map.seek (key);
829  if (pkey != map.end ())
830  {
831  map.contents (pkey).make_unique ();
832  tmpc = map.contents (pkey);
833  }
834 
835  // FIXME: better code reuse?
836  if (! error_state)
837  {
838  if (tmpc.numel () == 1)
839  {
840  octave_value& tmp = tmpc(0);
841 
842  if (! tmp.is_defined () || tmp.is_zero_by_zero ())
843  {
844  tmp = octave_value::empty_conv (next_type, rhs);
845  tmp.make_unique (); // probably a no-op.
846  }
847  else
848  // optimization: ignore copy still stored inside our map.
849  tmp.make_unique (1);
850 
851  if (! error_state)
852  t_rhs = tmp.subsasgn (next_type, next_idx, rhs);
853  }
854  else
856  }
857  }
858  break;
859 
860  case '{':
861  gripe_invalid_index_type (type_name (), type[0]);
862  break;
863 
864  default:
865  panic_impossible ();
866  }
867  }
868 
869  if (! error_state)
870  {
871  switch (type[0])
872  {
873  case '(':
874  {
875  if (n > 1 && type[1] == '.')
876  {
877  std::list<octave_value_list>::const_iterator p = idx.begin ();
878  octave_value_list key_idx = *++p;
879 
880  assert (key_idx.length () == 1);
881 
882  std::string key = key_idx(0).string_value ();
883 
884  if (! error_state)
885  {
886  map.assign (idx.front (), key, t_rhs);
887 
888  if (! error_state)
889  {
890  count++;
891  retval = octave_value (this);
892  }
893  else
895  }
896  else
898  }
899  else
900  {
901  if (t_rhs.is_object () || t_rhs.is_map ())
902  {
903  octave_map rhs_map = t_rhs.map_value ();
904 
905  if (! error_state)
906  {
907  map.assign (idx.front (), rhs_map);
908 
909  if (! error_state)
910  {
911  count++;
912  retval = octave_value (this);
913  }
914  else
916  }
917  else
918  error ("invalid class assignment");
919  }
920  else
921  {
922  if (t_rhs.is_empty ())
923  {
924  map.delete_elements (idx.front ());
925 
926  if (! error_state)
927  {
928  count++;
929  retval = octave_value (this);
930  }
931  else
933  }
934  else
935  error ("invalid class assignment");
936  }
937  }
938  }
939  break;
940 
941  case '.':
942  {
943  octave_value_list key_idx = idx.front ();
944 
945  assert (key_idx.length () == 1);
946 
947  std::string key = key_idx(0).string_value ();
948 
949  if (t_rhs.is_cs_list ())
950  {
951  Cell tmp_cell = Cell (t_rhs.list_value ());
952 
953  // The shape of the RHS is irrelevant, we just want
954  // the number of elements to agree and to preserve the
955  // shape of the left hand side of the assignment.
956 
957  if (numel () == tmp_cell.numel ())
958  tmp_cell = tmp_cell.reshape (dims ());
959 
960  map.setfield (key, tmp_cell);
961  }
962  else
963  {
964  Cell tmp_cell(1, 1);
965  tmp_cell(0) = t_rhs.storable_value ();
966  map.setfield (key, tmp_cell);
967  }
968 
969  if (! error_state)
970  {
971  count++;
972  retval = octave_value (this);
973  }
974  else
976  }
977  break;
978 
979  case '{':
980  gripe_invalid_index_type (type_name (), type[0]);
981  break;
982 
983  default:
984  panic_impossible ();
985  }
986  }
987  else
989 
990  return retval;
991 }
992 
995 {
996  idx_vector retval;
997 
998  octave_value meth = symbol_table::find_method ("subsindex", class_name ());
999 
1000  if (meth.is_defined ())
1001  {
1002  octave_value_list args;
1003  args(0) = octave_value (new octave_class (map, c_name, parent_list));
1004 
1005  octave_value_list tmp = feval (meth.function_value (), args, 1);
1006 
1007  if (!error_state && tmp.length () >= 1)
1008  {
1009  if (tmp(0).is_object ())
1010  error ("subsindex function must return a valid index vector");
1011  else
1012  // Index vector returned by subsindex is zero based
1013  // (why this inconsistency Mathworks?), and so we must
1014  // add one to the value returned as the index_vector method
1015  // expects it to be one based.
1016  retval = do_binary_op (octave_value::op_add, tmp (0),
1017  octave_value (1.0)).index_vector ();
1018  }
1019  }
1020  else
1021  error ("no subsindex method defined for class %s",
1022  class_name ().c_str ());
1023 
1024  return retval;
1025 }
1026 
1027 size_t
1029 {
1030  // Neglect the size of the fieldnames.
1031 
1032  size_t retval = 0;
1033 
1034  for (octave_map::const_iterator p = map.begin (); p != map.end (); p++)
1035  {
1036  std::string key = map.key (p);
1037 
1038  octave_value val = octave_value (map.contents (p));
1039 
1040  retval += val.byte_size ();
1041  }
1042 
1043  return retval;
1044 }
1045 
1048 {
1049  string_vector retval;
1050  gripe_wrong_type_arg ("octave_class::map_keys()", type_name ());
1051  return retval;
1052 }
1053 
1055 octave_class::find_parent_class (const std::string& parent_class_name)
1056 {
1057  octave_base_value* retval = 0;
1058 
1059  if (parent_class_name == class_name ())
1060  retval = this;
1061  else
1062  {
1063  for (std::list<std::string>::iterator pit = parent_list.begin ();
1064  pit != parent_list.end ();
1065  pit++)
1066  {
1067  octave_map::const_iterator smap = map.seek (*pit);
1068 
1069  const Cell& tmp = map.contents (smap);
1070 
1071  octave_value vtmp = tmp(0);
1072 
1073  octave_base_value *obvp = vtmp.internal_rep ();
1074 
1075  retval = obvp->find_parent_class (parent_class_name);
1076 
1077  if (retval)
1078  break;
1079  }
1080  }
1081 
1082  return retval;
1083 }
1084 
1086 octave_class::unique_parent_class (const std::string& parent_class_name)
1087 {
1088  octave_base_value* retval = 0;
1089 
1090  if (parent_class_name == class_name ())
1091  retval = this;
1092  else
1093  {
1094  for (std::list<std::string>::iterator pit = parent_list.begin ();
1095  pit != parent_list.end ();
1096  pit++)
1097  {
1098  octave_map::iterator smap = map.seek (*pit);
1099 
1100  Cell& tmp = map.contents (smap);
1101 
1102  octave_value& vtmp = tmp(0);
1103 
1104  octave_base_value *obvp = vtmp.internal_rep ();
1105 
1106  // Use find_parent_class first to avoid uniquifying if not necessary.
1107  retval = obvp->find_parent_class (parent_class_name);
1108 
1109  if (retval)
1110  {
1111  vtmp.make_unique ();
1112  obvp = vtmp.internal_rep ();
1113  retval = obvp->unique_parent_class (parent_class_name);
1114 
1115  break;
1116  }
1117  }
1118  }
1119 
1120  return retval;
1121 }
1122 
1125 {
1126  string_vector retval;
1127 
1129 
1130  if (meth.is_defined ())
1131  {
1132  octave_value_list args;
1133  args(0) = octave_value (new octave_class (map, c_name, parent_list));
1134 
1135  octave_value_list tmp = feval (meth.function_value (), args, 1);
1136 
1137  if (!error_state && tmp.length () >= 1)
1138  {
1139  if (tmp(0).is_string ())
1140  retval = tmp(0).all_strings (pad);
1141  else
1142  error ("cname/char method did not return a character string");
1143  }
1144  }
1145  else
1146  error ("no char method defined for class %s", class_name ().c_str ());
1147 
1148  return retval;
1149 }
1150 
1151 
1152 void
1153 octave_class::print (std::ostream& os, bool) const
1154 {
1155  print_raw (os);
1156 }
1157 
1158 void
1159 octave_class::print_raw (std::ostream& os, bool) const
1160 {
1161  unwind_protect frame;
1162 
1163  indent (os);
1164  os << " <class " << class_name () << ">";
1165  newline (os);
1166 }
1167 
1168 bool
1169 octave_class::print_name_tag (std::ostream& os, const std::string& name) const
1170 {
1171  bool retval = false;
1172 
1173  indent (os);
1174  os << name << " =";
1175  newline (os);
1176  if (! Vcompact_format)
1177  newline (os);
1178 
1179  return retval;
1180 }
1181 
1182 void
1183 octave_class::print_with_name (std::ostream& os, const std::string& name,
1184  bool)
1185 {
1186  octave_value fcn = symbol_table::find_method ("display", class_name ());
1187 
1188  if (fcn.is_defined ())
1189  {
1190  octave_value_list args;
1191 
1192  count++;
1193  args(0) = octave_value (this);
1194 
1195  string_vector arg_names (1);
1196 
1197  arg_names[0] = name;
1198 
1199  args.stash_name_tags (arg_names);
1200 
1201  feval (fcn.function_value (), args);
1202  }
1203  else
1204  {
1205  indent (os);
1206  os << name << " = <class " << class_name () << ">";
1207  newline (os);
1208  }
1209 }
1210 
1211 // Loading a class properly requires an exemplar map entry for success.
1212 // If we don't have one, we attempt to create one by calling the constructor
1213 // with no arguments.
1214 bool
1216 {
1217  bool retval = false;
1218 
1221 
1222  if (it != octave_class::exemplar_map.end ())
1223  retval = true;
1224  else
1225  {
1227 
1228  bool have_ctor = false;
1229 
1230  if (ctor.is_defined () && ctor.is_function ())
1231  {
1232  octave_function *fcn = ctor.function_value ();
1233 
1234  if (fcn && fcn->is_class_constructor (c_name))
1235  have_ctor = true;
1236 
1237  // Something has gone terribly wrong if
1238  // symbol_table::find_method (c_name, c_name) does not return
1239  // a class constructor for the class c_name...
1240  assert (have_ctor);
1241  }
1242 
1243  if (have_ctor)
1244  {
1245  unwind_protect frame;
1246 
1247  // Simulate try/catch.
1248 
1249  interpreter_try (frame);
1250 
1251  octave_value_list result
1252  = ctor.do_multi_index_op (1, octave_value_list ());
1253 
1254  if (! error_state && result.length () == 1)
1255  retval = true;
1256 
1257  error_state = false;
1258  }
1259  else
1260  warning ("no constructor for class %s", c_name.c_str ());
1261  }
1262 
1263  return retval;
1264 }
1265 
1266 void
1268 {
1269  exemplar_map.clear ();
1270 }
1271 
1272 // Load/save does not provide enough information to reconstruct the
1273 // class inheritance structure. reconstruct_parents () attempts to
1274 // do so. If successful, a "true" value is returned.
1275 //
1276 // Note that we don't check the loaded object structure against the
1277 // class structure here so the user's loadobj method has a chance
1278 // to do its magic.
1279 bool
1281 {
1282  bool retval = true, might_have_inheritance = false;
1283  std::string dbgstr = "dork";
1284 
1285  // First, check to see if there might be an issue with inheritance.
1286  for (octave_map::const_iterator p = map.begin (); p != map.end (); p++)
1287  {
1288  std::string key = map.key (p);
1289  Cell val = map.contents (p);
1290  if ( val(0).is_object () )
1291  {
1292  dbgstr = "blork";
1293  if ( key == val(0).class_name () )
1294  {
1295  might_have_inheritance = true;
1296  dbgstr = "cork";
1297  break;
1298  }
1299  }
1300  }
1301 
1302  if (might_have_inheritance)
1303  {
1306 
1307  if (it == octave_class::exemplar_map.end ())
1308  retval = false;
1309  else
1310  {
1311  octave_class::exemplar_info exmplr = it->second;
1312  parent_list = exmplr.parents ();
1313  for (std::list<std::string>::iterator pit = parent_list.begin ();
1314  pit != parent_list.end ();
1315  pit++)
1316  {
1317  dbgstr = *pit;
1318  bool dbgbool = map.contains (*pit);
1319  if (!dbgbool)
1320  {
1321  retval = false;
1322  break;
1323  }
1324  }
1325  }
1326  }
1327 
1328  return retval;
1329 }
1330 
1331 bool
1332 octave_class::save_ascii (std::ostream& os)
1333 {
1334  os << "# classname: " << class_name () << "\n";
1335  octave_map m;
1336  if (load_path::find_method (class_name (), "saveobj") != std::string ())
1337  {
1338  octave_value in = new octave_class (*this);
1339  octave_value_list tmp = feval ("saveobj", in, 1);
1340  if (! error_state)
1341  m = tmp(0).map_value ();
1342  else
1343  return false;
1344  }
1345  else
1346  m = map_value ();
1347 
1348  os << "# length: " << m.nfields () << "\n";
1349 
1350  octave_map::iterator i = m.begin ();
1351  while (i != m.end ())
1352  {
1353  octave_value val = map.contents (i);
1354 
1355  bool b = save_ascii_data (os, val, m.key (i), false, 0);
1356 
1357  if (! b)
1358  return os;
1359 
1360  i++;
1361  }
1362 
1363  return true;
1364 }
1365 
1366 bool
1367 octave_class::load_ascii (std::istream& is)
1368 {
1369  octave_idx_type len = 0;
1370  std::string classname;
1371  bool success = true;
1372 
1373  if (extract_keyword (is, "classname", classname) && classname != "")
1374  {
1375  if (extract_keyword (is, "length", len) && len >= 0)
1376  {
1377  if (len > 0)
1378  {
1379  octave_map m (map);
1380 
1381  for (octave_idx_type j = 0; j < len; j++)
1382  {
1383  octave_value t2;
1384  bool dummy;
1385 
1386  // recurse to read cell elements
1387  std::string nm
1388  = read_ascii_data (is, std::string (), dummy, t2, j);
1389 
1390  if (! is)
1391  break;
1392 
1393  Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
1394 
1395  if (error_state)
1396  {
1397  error ("load: internal error loading class elements");
1398  return false;
1399  }
1400 
1401  m.assign (nm, tcell);
1402  }
1403 
1404  if (is)
1405  {
1406  c_name = classname;
1408 
1409  map = m;
1410 
1411  if (! reconstruct_parents ())
1412  warning ("load: unable to reconstruct object inheritance");
1413 
1414  if (load_path::find_method (classname, "loadobj")
1415  != std::string ())
1416  {
1417  octave_value in = new octave_class (*this);
1418  octave_value_list tmp = feval ("loadobj", in, 1);
1419 
1420  if (! error_state)
1421  map = tmp(0).map_value ();
1422  else
1423  success = false;
1424  }
1425  }
1426  else
1427  {
1428  error ("load: failed to load class");
1429  success = false;
1430  }
1431  }
1432  else if (len == 0 )
1433  {
1434  map = octave_map (dim_vector (1, 1));
1435  c_name = classname;
1436  }
1437  else
1438  panic_impossible ();
1439  }
1440  else
1441  {
1442  error ("load: failed to extract number of elements in class");
1443  success = false;
1444  }
1445  }
1446  else
1447  {
1448  error ("load: failed to extract name of class");
1449  success = false;
1450  }
1451 
1452  return success;
1453 }
1454 
1455 bool
1456 octave_class::save_binary (std::ostream& os, bool& save_as_floats)
1457 {
1458  int32_t classname_len = class_name ().length ();
1459 
1460  os.write (reinterpret_cast<char *> (&classname_len), 4);
1461  os << class_name ();
1462 
1463  octave_map m;
1464  if (load_path::find_method (class_name (), "saveobj") != std::string ())
1465  {
1466  octave_value in = new octave_class (*this);
1467  octave_value_list tmp = feval ("saveobj", in, 1);
1468  if (! error_state)
1469  m = tmp(0).map_value ();
1470  else
1471  return false;
1472  }
1473  else
1474  m = map_value ();
1475 
1476  int32_t len = m.nfields ();
1477  os.write (reinterpret_cast<char *> (&len), 4);
1478 
1479  octave_map::iterator i = m.begin ();
1480  while (i != m.end ())
1481  {
1482  octave_value val = map.contents (i);
1483 
1484  bool b = save_binary_data (os, val, m.key (i), "", 0, save_as_floats);
1485 
1486  if (! b)
1487  return os;
1488 
1489  i++;
1490  }
1491 
1492  return true;
1493 }
1494 
1495 bool
1496 octave_class::load_binary (std::istream& is, bool swap,
1498 {
1499  bool success = true;
1500 
1501  int32_t classname_len;
1502 
1503  is.read (reinterpret_cast<char *> (&classname_len), 4);
1504  if (! is)
1505  return false;
1506  else if (swap)
1507  swap_bytes<4> (&classname_len);
1508 
1509  {
1510  OCTAVE_LOCAL_BUFFER (char, classname, classname_len+1);
1511  classname[classname_len] = '\0';
1512  if (! is.read (reinterpret_cast<char *> (classname), classname_len))
1513  return false;
1514  c_name = classname;
1515  }
1517 
1518  int32_t len;
1519  if (! is.read (reinterpret_cast<char *> (&len), 4))
1520  return false;
1521  if (swap)
1522  swap_bytes<4> (&len);
1523 
1524  if (len > 0)
1525  {
1526  octave_map m (map);
1527 
1528  for (octave_idx_type j = 0; j < len; j++)
1529  {
1530  octave_value t2;
1531  bool dummy;
1532  std::string doc;
1533 
1534  // recurse to read cell elements
1535  std::string nm = read_binary_data (is, swap, fmt, std::string (),
1536  dummy, t2, doc);
1537 
1538  if (! is)
1539  break;
1540 
1541  Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
1542 
1543  if (error_state)
1544  {
1545  error ("load: internal error loading class elements");
1546  return false;
1547  }
1548 
1549  m.assign (nm, tcell);
1550  }
1551 
1552  if (is)
1553  {
1554  map = m;
1555 
1556  if (! reconstruct_parents ())
1557  warning ("load: unable to reconstruct object inheritance");
1558 
1559  if (load_path::find_method (c_name, "loadobj") != std::string ())
1560  {
1561  octave_value in = new octave_class (*this);
1562  octave_value_list tmp = feval ("loadobj", in, 1);
1563 
1564  if (! error_state)
1565  map = tmp(0).map_value ();
1566  else
1567  success = false;
1568  }
1569  }
1570  else
1571  {
1572  warning ("load: failed to load class");
1573  success = false;
1574  }
1575  }
1576  else if (len == 0 )
1577  map = octave_map (dim_vector (1, 1));
1578  else
1579  panic_impossible ();
1580 
1581  return success;
1582 }
1583 
1584 #if defined (HAVE_HDF5)
1585 
1586 bool
1587 octave_class::save_hdf5 (hid_t loc_id, const char *name, bool save_as_floats)
1588 {
1589  hsize_t hdims[3];
1590  hid_t group_hid = -1;
1591  hid_t type_hid = -1;
1592  hid_t space_hid = -1;
1593  hid_t class_hid = -1;
1594  hid_t data_hid = -1;
1595  octave_map m;
1597 
1598 #if HAVE_HDF5_18
1599  group_hid = H5Gcreate (loc_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1600 #else
1601  group_hid = H5Gcreate (loc_id, name, 0);
1602 #endif
1603  if (group_hid < 0)
1604  goto error_cleanup;
1605 
1606  // Add the class name to the group
1607  type_hid = H5Tcopy (H5T_C_S1); H5Tset_size (type_hid, c_name.length () + 1);
1608  if (type_hid < 0)
1609  goto error_cleanup;
1610 
1611  hdims[0] = 0;
1612  space_hid = H5Screate_simple (0 , hdims, 0);
1613  if (space_hid < 0)
1614  goto error_cleanup;
1615 #if HAVE_HDF5_18
1616  class_hid = H5Dcreate (group_hid, "classname", type_hid, space_hid,
1617  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1618 #else
1619  class_hid = H5Dcreate (group_hid, "classname", type_hid, space_hid,
1620  H5P_DEFAULT);
1621 #endif
1622  if (class_hid < 0 || H5Dwrite (class_hid, type_hid, H5S_ALL, H5S_ALL,
1623  H5P_DEFAULT, c_name.c_str ()) < 0)
1624  goto error_cleanup;
1625 
1626 #if HAVE_HDF5_18
1627  data_hid = H5Gcreate (group_hid, "value", H5P_DEFAULT, H5P_DEFAULT,
1628  H5P_DEFAULT);
1629 #else
1630  data_hid = H5Gcreate (group_hid, "value", 0);
1631 #endif
1632  if (data_hid < 0)
1633  goto error_cleanup;
1634 
1635  if (load_path::find_method (class_name (), "saveobj") != std::string ())
1636  {
1637  octave_value in = new octave_class (*this);
1638  octave_value_list tmp = feval ("saveobj", in, 1);
1639  if (! error_state)
1640  m = tmp(0).map_value ();
1641  else
1642  goto error_cleanup;
1643  }
1644  else
1645  m = map_value ();
1646 
1647  // recursively add each element of the class to this group
1648  i = m.begin ();
1649  while (i != m.end ())
1650  {
1651  octave_value val = map.contents (i);
1652 
1653  bool retval2 = add_hdf5_data (data_hid, val, m.key (i), "", false,
1654  save_as_floats);
1655 
1656  if (! retval2)
1657  break;
1658 
1659  i++;
1660  }
1661 
1662 error_cleanup:
1663 
1664  if (data_hid > 0)
1665  H5Gclose (data_hid);
1666 
1667  if (class_hid > 0)
1668  H5Dclose (class_hid);
1669 
1670  if (space_hid > 0)
1671  H5Sclose (space_hid);
1672 
1673  if (type_hid > 0)
1674  H5Tclose (type_hid);
1675 
1676  if (group_hid > 0)
1677  H5Gclose (group_hid);
1678 
1679  return true;
1680 }
1681 
1682 bool
1683 octave_class::load_hdf5 (hid_t loc_id, const char *name)
1684 {
1685  bool retval = false;
1686 
1687  hid_t group_hid = -1;
1688  hid_t data_hid = -1;
1689  hid_t type_hid = -1;
1690  hid_t type_class_hid = -1;
1691  hid_t space_hid = -1;
1692  hid_t subgroup_hid = -1;
1693  hid_t st_id = -1;
1694 
1695  hdf5_callback_data dsub;
1696 
1697  herr_t retval2 = 0;
1698  octave_map m (dim_vector (1, 1));
1699  int current_item = 0;
1700  hsize_t num_obj = 0;
1701  int slen = 0;
1702  hsize_t rank = 0;
1703 
1704 #if HAVE_HDF5_18
1705  group_hid = H5Gopen (loc_id, name, H5P_DEFAULT);
1706 #else
1707  group_hid = H5Gopen (loc_id, name);
1708 #endif
1709  if (group_hid < 0)
1710  goto error_cleanup;
1711 
1712 #if HAVE_HDF5_18
1713  data_hid = H5Dopen (group_hid, "classname", H5P_DEFAULT);
1714 #else
1715  data_hid = H5Dopen (group_hid, "classname");
1716 #endif
1717 
1718  if (data_hid < 0)
1719  goto error_cleanup;
1720 
1721  type_hid = H5Dget_type (data_hid);
1722 
1723  type_class_hid = H5Tget_class (type_hid);
1724 
1725  if (type_class_hid != H5T_STRING)
1726  goto error_cleanup;
1727 
1728  space_hid = H5Dget_space (data_hid);
1729  rank = H5Sget_simple_extent_ndims (space_hid);
1730 
1731  if (rank != 0)
1732  goto error_cleanup;
1733 
1734  slen = H5Tget_size (type_hid);
1735  if (slen < 0)
1736  goto error_cleanup;
1737 
1738  // do-while loop here to prevent goto crossing initialization of classname
1739  do
1740  {
1741  OCTAVE_LOCAL_BUFFER (char, classname, slen);
1742 
1743  // create datatype for (null-terminated) string to read into:
1744  st_id = H5Tcopy (H5T_C_S1);
1745  H5Tset_size (st_id, slen);
1746 
1747  if (H5Dread (data_hid, st_id, H5S_ALL, H5S_ALL, H5P_DEFAULT,
1748  classname) < 0)
1749  {
1750  H5Tclose (st_id);
1751  H5Dclose (data_hid);
1752  H5Gclose (group_hid);
1753  return false;
1754  }
1755 
1756  H5Tclose (st_id);
1757  H5Dclose (data_hid);
1758  data_hid = -1;
1759 
1760  c_name = classname;
1761  }
1762  while (0);
1764 
1765 #if HAVE_HDF5_18
1766  subgroup_hid = H5Gopen (group_hid, name, H5P_DEFAULT);
1767 #else
1768  subgroup_hid = H5Gopen (group_hid, name);
1769 #endif
1770  H5Gget_num_objs (subgroup_hid, &num_obj);
1771  H5Gclose (subgroup_hid);
1772 
1773  while (current_item < static_cast<int> (num_obj)
1774  && (retval2 = H5Giterate (group_hid, name, &current_item,
1775  hdf5_read_next_data, &dsub)) > 0)
1776  {
1777  octave_value t2 = dsub.tc;
1778 
1779  Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
1780 
1781  if (error_state)
1782  {
1783  error ("load: internal error loading class elements");
1784  return false;
1785  }
1786 
1787  m.assign (dsub.name, tcell);
1788 
1789  }
1790 
1791  if (retval2 >= 0)
1792  {
1793  map = m;
1794 
1795  if (!reconstruct_parents ())
1796  warning ("load: unable to reconstruct object inheritance");
1797 
1798  if (load_path::find_method (c_name, "loadobj") != std::string ())
1799  {
1800  octave_value in = new octave_class (*this);
1801  octave_value_list tmp = feval ("loadobj", in, 1);
1802 
1803  if (! error_state)
1804  {
1805  map = tmp(0).map_value ();
1806  retval = true;
1807  }
1808  else
1809  retval = false;
1810  }
1811  }
1812 
1813 error_cleanup:
1814  if (data_hid > 0)
1815  H5Dclose (data_hid);
1816 
1817  if (data_hid > 0)
1818  H5Gclose (group_hid);
1819 
1820  return retval;
1821 }
1822 
1823 #endif
1824 
1825 mxArray *
1827 {
1828  gripe_wrong_type_arg ("octave_class::as_mxArray ()", type_name ());
1829 
1830  return 0;
1831 }
1832 
1833 bool
1835 {
1837 
1838  return (fcn
1839  && (fcn->is_class_method ()
1840  || fcn->is_class_constructor ()
1843  && find_parent_class (fcn->dispatch_class ()));
1844 }
1845 
1847  : field_names (), parent_class_names ()
1848 {
1849  if (obj.is_object ())
1850  {
1851  octave_map m = obj.map_value ();
1852  field_names = m.keys ();
1853 
1855  }
1856  else
1857  error ("invalid call to exemplar_info constructor");
1858 }
1859 
1860 
1861 // A map from class names to lists of fields.
1862 std::map<std::string, octave_class::exemplar_info> octave_class::exemplar_map;
1863 
1864 bool
1866 {
1867  bool retval = true;
1868 
1869  if (obj.is_object ())
1870  {
1871  if (nfields () == obj.nfields ())
1872  {
1873  octave_map obj_map = obj.map_value ();
1874  string_vector obj_fnames = obj_map.keys ();
1875  string_vector fnames = fields ();
1876 
1877  for (octave_idx_type i = 0; i < nfields (); i++)
1878  {
1879  if (obj_fnames[i] != fnames[i])
1880  {
1881  retval = false;
1882  error ("mismatch in field names");
1883  break;
1884  }
1885  }
1886 
1887  if (nparents () == obj.nparents ())
1888  {
1889  std::list<std::string> obj_parents
1890  = obj.parent_class_name_list ();
1891  std::list<std::string> pnames = parents ();
1892 
1893  std::list<std::string>::const_iterator p = obj_parents.begin ();
1894  std::list<std::string>::const_iterator q = pnames.begin ();
1895 
1896  while (p != obj_parents.end ())
1897  {
1898  if (*p++ != *q++)
1899  {
1900  retval = false;
1901  error ("mismatch in parent classes");
1902  break;
1903  }
1904  }
1905  }
1906  else
1907  {
1908  retval = false;
1909  error ("mismatch in number of parent classes");
1910  }
1911  }
1912  else
1913  {
1914  retval = false;
1915  error ("mismatch in number of fields");
1916  }
1917  }
1918  else
1919  {
1920  retval = false;
1921  error ("invalid comparison of class exemplar to non-class object");
1922  }
1923 
1924  return retval;
1925 }
1926 
1927 DEFUN (class, args, ,
1928  "-*- texinfo -*-\n\
1929 @deftypefn {Function File} {@var{classname} =} class (@var{obj})\n\
1930 @deftypefnx {Function File} {} class (@var{s}, @var{id})\n\
1931 @deftypefnx {Function File} {} class (@var{s}, @var{id}, @var{p}, @dots{})\n\
1932 Return the class of the object @var{obj} or create a class with\n\
1933 fields from structure @var{s} and name (string) @var{id}. Additional\n\
1934 arguments name a list of parent classes from which the new class is\n\
1935 derived.\n\
1936 @seealso{typeinfo, isa}\n\
1937 @end deftypefn")
1938 {
1939  octave_value retval;
1940 
1941  int nargin = args.length ();
1942 
1943  if (nargin == 0)
1944  print_usage ();
1945  else if (nargin == 1)
1946  // Called for class of object
1947  retval = args(0).class_name ();
1948  else
1949  {
1950  // Called as class constructor
1952 
1953  std::string id = args(1).string_value ();
1954 
1955  if (! error_state)
1956  {
1957  if (fcn)
1958  {
1959  if (fcn->is_class_constructor (id) || fcn->is_class_method (id))
1960  {
1961  octave_map m = args(0).map_value ();
1962 
1963  if (! error_state)
1964  {
1965  if (nargin == 2)
1966  retval
1967  = octave_value (new octave_class
1968  (m, id, std::list<std::string> ()));
1969  else
1970  {
1971  octave_value_list parents = args.slice (2, nargin-2);
1972 
1973  retval
1974  = octave_value (new octave_class (m, id, parents));
1975  }
1976 
1977  if (! error_state)
1978  {
1980  = octave_class::exemplar_map.find (id);
1981 
1982  if (it == octave_class::exemplar_map.end ())
1983  octave_class::exemplar_map[id]
1984  = octave_class::exemplar_info (retval);
1985  else if (! it->second.compare (retval))
1986  error ("class: object of class '%s' does not match previously constructed objects",
1987  id.c_str ());
1988  }
1989  }
1990  else
1991  error ("class: expecting structure S as first argument");
1992  }
1993  else
1994  error ("class: '%s' is invalid as a class name in this context",
1995  id.c_str ());
1996  }
1997  else
1998  error ("class: invalid call from outside class constructor or method");
1999  }
2000  else
2001  error ("class: ID (class name) must be a character string");
2002  }
2003 
2004  return retval;
2005 }
2006 
2007 /*
2008 %!assert (class (1.1), "double");
2009 %!assert (class (single (1.1)), "single");
2010 %!assert (class (uint8 (1)), "uint8");
2011 %!testif HAVE_JAVA
2012 %! jobj = javaObject ("java.lang.StringBuffer");
2013 %! assert (class (jobj), "java.lang.StringBuffer");
2014 
2015 %% Test Input Validation
2016 %!error class ()
2017 */
2018 
2019 DEFUN (__isa_parent__, args, ,
2020  "-*- texinfo -*-\n\
2021 @deftypefn {Built-in Function} {} __isa_parent__ (@var{class}, @var{name})\n\
2022 Undocumented internal function.\n\
2023 @end deftypefn")
2024 {
2025  octave_value retval = false;
2026 
2027  if (args.length () == 2)
2028  {
2029  octave_value cls = args(0);
2030  octave_value nm = args(1);
2031 
2032  if (! error_state)
2033  {
2034  if (cls.find_parent_class (nm.string_value ()))
2035  retval = true;
2036  }
2037  else
2038  error ("__isa_parent__: expecting arguments to be character strings");
2039  }
2040  else
2041  print_usage ();
2042 
2043  return retval;
2044 }
2045 
2046 DEFUN (__parent_classes__, args, ,
2047  "-*- texinfo -*-\n\
2048 @deftypefn {Built-in Function} {} __parent_classes__ (@var{x})\n\
2049 Undocumented internal function.\n\
2050 @end deftypefn")
2051 {
2052  octave_value retval = Cell ();
2053 
2054  if (args.length () == 1)
2055  {
2056  octave_value arg = args(0);
2057 
2058  if (arg.is_object ())
2059  retval = Cell (arg.parent_class_names ());
2060  }
2061  else
2062  print_usage ();
2063 
2064  return retval;
2065 }
2066 
2067 DEFUN (isobject, args, ,
2068  "-*- texinfo -*-\n\
2069 @deftypefn {Built-in Function} {} isobject (@var{x})\n\
2070 Return true if @var{x} is a class object.\n\
2071 @seealso{class, typeinfo, isa, ismethod}\n\
2072 @end deftypefn")
2073 {
2074  octave_value retval;
2075 
2076  if (args.length () == 1)
2077  retval = args(0).is_object ();
2078  else
2079  print_usage ();
2080 
2081  return retval;
2082 }
2083 
2084 DEFUN (ismethod, args, ,
2085  "-*- texinfo -*-\n\
2086 @deftypefn {Built-in Function} {} ismethod (@var{x}, @var{method})\n\
2087 Return true if @var{x} is a class object and the string @var{method}\n\
2088 is a method of this class.\n\
2089 @seealso{isobject}\n\
2090 @end deftypefn")
2091 {
2092  octave_value retval;
2093 
2094  if (args.length () == 2)
2095  {
2096  octave_value arg = args(0);
2097 
2098  std::string class_name;
2099 
2100  if (arg.is_object ())
2101  class_name = arg.class_name ();
2102  else if (arg.is_string ())
2103  class_name = arg.string_value ();
2104  else
2105  error ("ismethod: expecting object or class name as first argument");
2106 
2107  if (! error_state)
2108  {
2109  std::string method = args(1).string_value ();
2110 
2111  if (! error_state)
2112  {
2113  if (load_path::find_method (class_name, method) != std::string ())
2114  retval = true;
2115  else
2116  retval = false;
2117  }
2118  }
2119  }
2120  else
2121  print_usage ();
2122 
2123  return retval;
2124 }
2125 
2126 DEFUN (__methods__, args, ,
2127  "-*- texinfo -*-\n\
2128 @deftypefn {Built-in Function} {} __methods__ (@var{x})\n\
2129 @deftypefnx {Built-in Function} {} __methods__ (\"classname\")\n\
2130 Internal function.\n\
2131 \n\
2132 Implements @code{methods} for Octave class objects and classnames.\n\
2133 @seealso{methods}\n\
2134 @end deftypefn")
2135 {
2136  octave_value retval;
2137 
2138  // Input validation has already been done in methods.m.
2139  octave_value arg = args(0);
2140 
2141  std::string class_name;
2142 
2143  if (arg.is_object ())
2144  class_name = arg.class_name ();
2145  else if (arg.is_string ())
2146  class_name = arg.string_value ();
2147 
2148  if (! error_state)
2149  {
2150  string_vector sv = load_path::methods (class_name);
2151  retval = Cell (sv);
2152  }
2153 
2154  return retval;
2155 }
2156 
2157 static bool
2158 is_built_in_class (const std::string& cn)
2159 {
2160  static std::set<std::string> built_in_class_names;
2161 
2162  if (built_in_class_names.empty ())
2163  {
2164  built_in_class_names.insert ("double");
2165  built_in_class_names.insert ("single");
2166  built_in_class_names.insert ("cell");
2167  built_in_class_names.insert ("struct");
2168  built_in_class_names.insert ("logical");
2169  built_in_class_names.insert ("char");
2170  built_in_class_names.insert ("function handle");
2171  built_in_class_names.insert ("int8");
2172  built_in_class_names.insert ("uint8");
2173  built_in_class_names.insert ("int16");
2174  built_in_class_names.insert ("uint16");
2175  built_in_class_names.insert ("int32");
2176  built_in_class_names.insert ("uint32");
2177  built_in_class_names.insert ("int64");
2178  built_in_class_names.insert ("uint64");
2179  }
2180 
2181  return built_in_class_names.find (cn) != built_in_class_names.end ();
2182 }
2183 
2184 DEFUN (superiorto, args, ,
2185  "-*- texinfo -*-\n\
2186 @deftypefn {Built-in Function} {} superiorto (@var{class_name}, @dots{})\n\
2187 When called from a class constructor, mark the object currently\n\
2188 constructed as having a higher precedence than @var{class_name}.\n\
2189 More that one such class can be specified in a single call.\n\
2190 This function may only be called from a class constructor.\n\
2191 @end deftypefn")
2192 {
2193  octave_value retval;
2194 
2196  if ((! fcn) || (! fcn->is_class_constructor ()))
2197  {
2198  error ("superiorto: invalid call from outside class constructor");
2199  return retval;
2200  }
2201 
2202  for (int i = 0; i < args.length (); i++)
2203  {
2204  std::string inf_class = args(i).string_value ();
2205  if (error_state)
2206  {
2207  error ("superiorto: expecting argument to be class name");
2208  break;
2209  }
2210 
2211  // User defined classes always have higher precedence
2212  // than built-in classes
2213  if (is_built_in_class (inf_class))
2214  break;
2215 
2216  std::string sup_class = fcn->name ();
2217  if (! symbol_table::set_class_relationship (sup_class, inf_class))
2218  {
2219  error ("superiorto: opposite precedence already set for %s and %s",
2220  sup_class.c_str (), inf_class.c_str ());
2221  break;
2222  }
2223  }
2224 
2225  return retval;
2226 }
2227 
2228 DEFUN (inferiorto, args, ,
2229  "-*- texinfo -*-\n\
2230 @deftypefn {Built-in Function} {} inferiorto (@var{class_name}, @dots{})\n\
2231 When called from a class constructor, mark the object currently\n\
2232 constructed as having a lower precedence than @var{class_name}.\n\
2233 More that one such class can be specified in a single call.\n\
2234 This function may only be called from a class constructor.\n\
2235 @end deftypefn")
2236 {
2237  octave_value retval;
2238 
2240  if ((! fcn) || (! fcn->is_class_constructor ()))
2241  {
2242  error ("inferiorto: invalid call from outside class constructor");
2243  return retval;
2244  }
2245 
2246  for (int i = 0; i < args.length (); i++)
2247  {
2248  std::string sup_class = args(i).string_value ();
2249  if (error_state)
2250  {
2251  error ("inferiorto: expecting argument to be class name");
2252  break;
2253  }
2254 
2255  if (is_built_in_class (sup_class))
2256  {
2257  error ("inferiorto: cannot give user-defined class lower "
2258  "precedence than built-in class");
2259  break;
2260  }
2261 
2262  std::string inf_class = fcn->name ();
2263  if (! symbol_table::set_class_relationship (sup_class, inf_class))
2264  {
2265  error ("inferiorto: opposite precedence already set for %s and %s",
2266  inf_class.c_str (), sup_class.c_str ());
2267  break;
2268  }
2269  }
2270 
2271  return retval;
2272 }