Mercurial > octave-nkf
comparison src/ov-classdef.cc @ 15036:aa1f9e479c6e classdef
octave_value classdef objects
* ov-classdef.h, ov-classdef.cc: New files.
* octave.cc: Include ov-classdef.h.
(octave_main): Call install_classdef.
* src/Makefile.am (OV_SRC): Add ov-classdef.cc to the list.
(OV_INCLUDES): Add ov-classdef.h to the list.
author | Michael Goffioul <michael.goffioul@gmail.com> |
---|---|
date | Fri, 27 Jul 2012 16:02:01 -0400 |
parents | |
children | 56b8eb7c9c04 |
comparison
equal
deleted
inserted
replaced
15035:a820a990968e | 15036:aa1f9e479c6e |
---|---|
1 /* | |
2 | |
3 Copyright (C) 2012 Michael Goffioul | |
4 | |
5 This file is part of Octave. | |
6 | |
7 Octave is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 3 of the License, or (at your | |
10 option) any later version. | |
11 | |
12 Octave is distributed in the hope that it will be useful, but WITHOUT | |
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 for more details. | |
16 | |
17 You should have received a copy of the GNU General Public License | |
18 along with Octave; see the file COPYING. If not, see | |
19 <http://www.gnu.org/licenses/>. | |
20 | |
21 */ | |
22 | |
23 #ifdef HAVE_CONFIG_H | |
24 #include <config.h> | |
25 #endif | |
26 | |
27 #include <map> | |
28 | |
29 #include "defun.h" | |
30 #include "ov-builtin.h" | |
31 #include "ov-classdef.h" | |
32 #include "ov-fcn-handle.h" | |
33 #include "ov-typeinfo.h" | |
34 | |
35 static std::map<std::string, cdef_class> all_classes; | |
36 static std::map<std::string, cdef_package> all_packages; | |
37 | |
38 static void | |
39 gripe_method_access (const std::string& from, const cdef_method& meth) | |
40 { | |
41 error ("%s: method `%s' has %s access and cannot be run in this context", | |
42 from.c_str (), meth.get_name ().c_str (), | |
43 meth.get_access ().c_str ()); | |
44 } | |
45 | |
46 static void | |
47 gripe_property_access (const std::string& from, const cdef_property& prop, | |
48 bool is_set = false) | |
49 { | |
50 if (is_set) | |
51 error ("%s: property `%s' has %s access and cannot be set in this context", | |
52 from.c_str (), prop.get_name ().c_str (), | |
53 prop.get_set_access ().c_str ()); | |
54 else | |
55 error ("%s: property `%s' has %s access and cannot be obtained in this context", | |
56 from.c_str (), prop.get_name ().c_str (), | |
57 prop.get_get_access ().c_str ()); | |
58 } | |
59 | |
60 static octave_value | |
61 make_fcn_handle (octave_builtin::fcn ff, const std::string& nm) | |
62 { | |
63 octave_value fcn (new octave_builtin (ff, nm)); | |
64 | |
65 octave_value fcn_handle (new octave_fcn_handle (fcn, nm)); | |
66 | |
67 return fcn_handle; | |
68 } | |
69 | |
70 inline octave_value_list | |
71 execute_ov (octave_value val, const octave_value_list& args, int nargout) | |
72 { | |
73 std::list<octave_value_list> idx (1, args); | |
74 | |
75 std::string type ("("); | |
76 | |
77 return val.subsref (type, idx, nargout); | |
78 } | |
79 | |
80 static bool | |
81 check_access (const std::string& req, const std::string& acc) | |
82 { | |
83 if (req == "private") | |
84 return true; | |
85 else if (req == "protected") | |
86 return (acc != "private"); | |
87 else | |
88 return (acc == "public"); | |
89 } | |
90 | |
91 static std::string | |
92 get_base_name (const std::string& nm) | |
93 { | |
94 std::string::size_type pos = nm.find_last_of ('.'); | |
95 | |
96 if (pos != std::string::npos) | |
97 return nm.substr (pos + 1); | |
98 | |
99 return nm; | |
100 } | |
101 | |
102 static std::string | |
103 superclass_access (const std::string& acc) | |
104 { | |
105 if (acc == "public") | |
106 return acc; | |
107 else | |
108 return "protected"; | |
109 } | |
110 | |
111 static cdef_class | |
112 lookup_class (const std::string& name, bool error_if_not_found = true) | |
113 { | |
114 std::map<std::string, cdef_class>::iterator it = all_classes.find (name); | |
115 | |
116 if (it == all_classes.end ()) | |
117 { | |
118 // FIXME: should look into load-path | |
119 if (error_if_not_found) | |
120 error ("class not found: %s", name.c_str ()); | |
121 } | |
122 else | |
123 { | |
124 cdef_class& cls = it->second; | |
125 | |
126 if (! cls.is_builtin ()) | |
127 { | |
128 // FIXME: check whether a class reload is needed | |
129 } | |
130 | |
131 if (cls.ok ()) | |
132 return cls; | |
133 else | |
134 all_classes.erase (it); | |
135 } | |
136 | |
137 return cdef_class (); | |
138 } | |
139 | |
140 static Cell | |
141 lookup_classes (const Cell& cls_names) | |
142 { | |
143 Cell cls (cls_names.numel (), 1); | |
144 | |
145 for (int i = 0; i < cls_names.numel (); i++) | |
146 { | |
147 cdef_class c = lookup_class (cls_names(i).string_value ()); | |
148 | |
149 if (! error_state) | |
150 cls(i) = to_ov (c); | |
151 else | |
152 return Cell (); | |
153 } | |
154 | |
155 return cls; | |
156 } | |
157 | |
158 static bool | |
159 is_superclass (const cdef_class& clsa, const cdef_class& clsb, | |
160 bool allow_equal = true) | |
161 { | |
162 if (allow_equal && clsa == clsb) | |
163 return true; | |
164 else | |
165 { | |
166 Cell c = clsb.get ("SuperClasses").cell_value (); | |
167 | |
168 bool retval = false; | |
169 | |
170 for (int i = 0; ! retval && i < c.numel (); i++) | |
171 { | |
172 cdef_class cls = lookup_class (c(i).string_value ()); | |
173 | |
174 if (! error_state) | |
175 retval = is_superclass (clsa, cls, true); | |
176 } | |
177 | |
178 return retval; | |
179 } | |
180 } | |
181 | |
182 inline bool | |
183 is_strict_superclass (const cdef_class& clsa, const cdef_class& clsb) | |
184 { return is_superclass (clsa, clsb, false); } | |
185 | |
186 static octave_value_list | |
187 class_get_properties (const octave_value_list& args, int /* nargout */) | |
188 { | |
189 octave_value_list retval; | |
190 | |
191 if (args.length () == 1 && args(0).type_name () == "object") | |
192 { | |
193 cdef_class cls (to_cdef (args(0))); | |
194 | |
195 retval(0) = cls.get_properties (); | |
196 } | |
197 | |
198 return retval; | |
199 } | |
200 | |
201 static octave_value_list | |
202 class_get_methods (const octave_value_list& args, int /* nargout */) | |
203 { | |
204 octave_value_list retval; | |
205 | |
206 if (args.length () == 1 && args(0).type_name () == "object") | |
207 { | |
208 cdef_class cls (to_cdef (args(0))); | |
209 | |
210 retval(0) = cls.get_methods (); | |
211 } | |
212 | |
213 return retval; | |
214 } | |
215 | |
216 static octave_value_list | |
217 class_get_superclasses (const octave_value_list& args, int /* nargout */) | |
218 { | |
219 octave_value_list retval; | |
220 | |
221 if (args.length () == 1 && args(0).type_name () == "object" | |
222 && args(0).class_name () == "meta.class") | |
223 { | |
224 cdef_class cls (to_cdef (args(0))); | |
225 | |
226 Cell classes = cls.get ("SuperClasses").cell_value (); | |
227 | |
228 retval(0) = lookup_classes (classes); | |
229 } | |
230 | |
231 return retval; | |
232 } | |
233 | |
234 static octave_value_list | |
235 class_get_inferiorclasses (const octave_value_list& args, int /* nargout */) | |
236 { | |
237 octave_value_list retval; | |
238 | |
239 if (args.length () == 1 && args(0).type_name () == "object" | |
240 && args(0).class_name () == "meta.class") | |
241 { | |
242 cdef_class cls (to_cdef (args(0))); | |
243 | |
244 Cell classes = cls.get ("InferiorClasses").cell_value (); | |
245 | |
246 retval(0) = lookup_classes (classes); | |
247 } | |
248 | |
249 return retval; | |
250 } | |
251 | |
252 static octave_value_list | |
253 class_fromName (const octave_value_list& args, int /* nargout */) | |
254 { | |
255 octave_value_list retval; | |
256 | |
257 if (args.length () == 1) | |
258 { | |
259 std::string name = args(0).string_value (); | |
260 | |
261 if (! error_state) | |
262 retval(0) = to_ov (lookup_class (name)); | |
263 else | |
264 error ("fromName: invalid class name, expected a string value"); | |
265 } | |
266 else | |
267 error ("fromName: invalid number of parameters"); | |
268 | |
269 return retval; | |
270 } | |
271 | |
272 static octave_value_list | |
273 class_fevalStatic (const octave_value_list& args, int nargout) | |
274 { | |
275 octave_value_list retval; | |
276 | |
277 if (args.length () > 1 && args(0).type_name () == "object") | |
278 { | |
279 cdef_class cls (to_cdef (args(0))); | |
280 | |
281 if (! error_state) | |
282 { | |
283 std::string meth_name = args(1).string_value (); | |
284 | |
285 if (! error_state) | |
286 { | |
287 cdef_method meth = cls.find_method (meth_name); | |
288 | |
289 if (meth.ok ()) | |
290 { | |
291 // FIXME: can the context be something else? | |
292 if (meth.check_access ("public")) | |
293 { | |
294 if (meth.is_static ()) | |
295 retval = meth.execute (args.splice (0, 2), nargout); | |
296 else | |
297 error ("fevalStatic: method `%s' is not static", | |
298 meth_name.c_str ()); | |
299 } | |
300 else | |
301 gripe_method_access ("fevalStatic", meth); | |
302 } | |
303 else | |
304 error ("fevalStatic: method not found: %s", | |
305 meth_name.c_str ()); | |
306 } | |
307 else | |
308 error ("fevalStatic: invalid method name, expected a string value"); | |
309 } | |
310 error ("fevalStatic: invalid object, expected a meta.class object"); | |
311 } | |
312 else | |
313 error ("fevalStatic: invalid arguments"); | |
314 | |
315 return retval; | |
316 } | |
317 | |
318 static octave_value_list | |
319 class_getConstant (const octave_value_list& args, int /* nargout */) | |
320 { | |
321 octave_value_list retval; | |
322 | |
323 if (args.length () == 2 && args(0).type_name () == "object" | |
324 && args(0).class_name () == "meta.class") | |
325 { | |
326 cdef_class cls = to_cdef (args(0)); | |
327 | |
328 if (! error_state) | |
329 { | |
330 std::string prop_name = args(1).string_value (); | |
331 | |
332 if (! error_state) | |
333 { | |
334 cdef_property prop = cls.find_property (prop_name); | |
335 | |
336 if (prop.ok ()) | |
337 { | |
338 // FIXME: can the context be something else? | |
339 if (prop.check_get_access ("public")) | |
340 { | |
341 if (prop.is_constant ()) | |
342 retval(0) = prop.get_value (); | |
343 else | |
344 error ("getConstant: property `%s' is not constant", | |
345 prop_name.c_str ()); | |
346 } | |
347 else | |
348 gripe_property_access ("getConstant", prop); | |
349 } | |
350 else | |
351 error ("getConstant: property not found: %s", | |
352 prop_name.c_str ()); | |
353 } | |
354 else | |
355 error ("getConstant: invalid property name, expected a string value"); | |
356 } | |
357 else | |
358 error ("getConstant: invalid object, expected a meta.class object"); | |
359 } | |
360 else | |
361 error ("getConstant: invalid arguments"); | |
362 | |
363 return retval; | |
364 } | |
365 | |
366 #define META_CLASS_CMP(OP, CLSA, CLSB, FUN) \ | |
367 static octave_value_list \ | |
368 class_ ## OP (const octave_value_list& args, int /* nargout */) \ | |
369 { \ | |
370 octave_value_list retval; \ | |
371 \ | |
372 if (args.length () == 2 \ | |
373 && args(0).type_name () == "object" && args(1).type_name () == "object" \ | |
374 && args(0).class_name () == "meta.class" && args(1).class_name () == "meta.class") \ | |
375 { \ | |
376 cdef_class clsa = to_cdef (args(0)); \ | |
377 \ | |
378 cdef_class clsb = to_cdef (args(1)); \ | |
379 \ | |
380 if (! error_state) \ | |
381 retval(0) = FUN (CLSA, CLSB); \ | |
382 else \ | |
383 error (#OP ": invalid objects, expected meta.class objects"); \ | |
384 } \ | |
385 else \ | |
386 error (#OP ": invalid arguments"); \ | |
387 \ | |
388 return retval; \ | |
389 } | |
390 | |
391 META_CLASS_CMP (lt, clsb, clsa, is_strict_superclass) | |
392 META_CLASS_CMP (le, clsb, clsa, is_superclass) | |
393 META_CLASS_CMP (gt, clsa, clsb, is_strict_superclass) | |
394 META_CLASS_CMP (ge, clsa, clsb, is_superclass) | |
395 META_CLASS_CMP (eq, clsa, clsb, operator==) | |
396 META_CLASS_CMP (ne, clsa, clsb, operator!=) | |
397 | |
398 static octave_value_list | |
399 handle_delete (const octave_value_list& /* args */, int /* nargout */) | |
400 { | |
401 octave_value_list retval; | |
402 | |
403 // FIXME: implement this | |
404 | |
405 return retval; | |
406 } | |
407 | |
408 static cdef_class | |
409 make_class (const std::string& name, const std::string& super = std::string()) | |
410 { | |
411 cdef_class cls ("meta.class"); | |
412 | |
413 all_classes[name] = cls; | |
414 cls.put ("ConstructOnLoad", false); | |
415 cls.put ("ContainingPackage", Matrix ()); | |
416 cls.put ("Description", std::string ()); | |
417 cls.put ("DetailedDescription", std::string ()); | |
418 cls.put ("Events", Cell ()); | |
419 cls.put ("Hidden", false); | |
420 cls.put ("InferiorClasses", Cell ()); | |
421 cls.put ("Methods", Cell ()); | |
422 cls.put ("Name", name); | |
423 cls.put ("Properties", Cell ()); | |
424 cls.put ("Sealed", true); | |
425 if (super.empty ()) | |
426 cls.put ("SuperClasses", Cell ()); | |
427 else | |
428 cls.put ("SuperClasses", Cell (octave_value (super))); | |
429 | |
430 return cls; | |
431 } | |
432 | |
433 static cdef_property | |
434 make_property (const cdef_object& cls, const std::string& name, | |
435 const octave_value& get_method = Matrix (), | |
436 const std::string& get_access = "public", | |
437 const octave_value& set_method = Matrix (), | |
438 const std::string& set_access = "public") | |
439 { | |
440 // FIXME: what about default value? | |
441 | |
442 cdef_property prop ("meta.property"); | |
443 | |
444 prop.put ("Name", name); | |
445 prop.put ("Description", std::string ()); | |
446 prop.put ("DetailedDescription", std::string ()); | |
447 prop.put ("Abstract", false); | |
448 prop.put ("Constant", false); | |
449 prop.put ("GetAccess", get_access); | |
450 prop.put ("SetAccess", set_access); | |
451 prop.put ("Dependent", false); | |
452 prop.put ("Transient", false); | |
453 prop.put ("Hidden", false); | |
454 prop.put ("GetObservable", false); | |
455 prop.put ("SetObservable", false); | |
456 prop.put ("GetMethod", get_method); | |
457 prop.put ("SetMethod", set_method); | |
458 prop.put ("DefiningClass", to_ov (cls)); | |
459 | |
460 return prop; | |
461 } | |
462 | |
463 inline cdef_property | |
464 make_attribute (const cdef_object& cls, const std::string& name) | |
465 { | |
466 return make_property (cls, name, Matrix (), "public", Matrix (), "private"); | |
467 } | |
468 | |
469 static cdef_method | |
470 make_method (const cdef_object& cls, const std::string& name, const octave_value& fcn, | |
471 const std::string& m_access = "public", bool is_static = false) | |
472 { | |
473 cdef_method meth ("meta.method"); | |
474 | |
475 meth.put ("Abstract", false); | |
476 meth.put ("Access", m_access); | |
477 meth.put ("DefiningClass", to_ov (cls)); | |
478 meth.put ("Description", std::string ()); | |
479 meth.put ("DetailedDescription", std::string ()); | |
480 meth.put ("Hidden", false); | |
481 meth.put ("Name", name); | |
482 meth.put ("Sealed", true); | |
483 meth.put ("Static", is_static); | |
484 | |
485 meth.set_function (fcn); | |
486 | |
487 return meth; | |
488 } | |
489 | |
490 inline cdef_method | |
491 make_method (const cdef_object& cls, const std::string& name, octave_builtin::fcn ff, | |
492 const std::string& m_access = "public", bool is_static = false) | |
493 { | |
494 octave_value fcn (new octave_builtin (ff, name)); | |
495 | |
496 octave_value fcn_handle (new octave_fcn_handle (fcn, name)); | |
497 | |
498 return make_method (cls, name, fcn_handle, m_access, is_static); | |
499 } | |
500 | |
501 static cdef_package | |
502 make_package (const std::string& nm, | |
503 const std::string& parent = std::string ()) | |
504 { | |
505 cdef_package pack ("meta.package"); | |
506 | |
507 all_packages[nm] = pack; | |
508 pack.put ("Name", nm); | |
509 pack.put ("ContainingPackage", to_ov (all_packages[parent])); | |
510 | |
511 return pack; | |
512 } | |
513 | |
514 DEFINE_OCTAVE_ALLOCATOR (octave_classdef); | |
515 | |
516 int octave_classdef::t_id (-1); | |
517 | |
518 const std::string octave_classdef::t_name ("object"); | |
519 | |
520 void | |
521 octave_classdef::register_type (void) | |
522 { | |
523 t_id = octave_value_typeinfo::register_type | |
524 (octave_classdef::t_name, "<unknown>", octave_value (new octave_classdef ())); | |
525 } | |
526 | |
527 cdef_class | |
528 cdef_object_rep::get_class (void) const | |
529 { | |
530 cdef_class cls = lookup_class (class_name ()); | |
531 | |
532 return cls; | |
533 } | |
534 | |
535 string_vector | |
536 cdef_object_rep::map_keys (void) const | |
537 { | |
538 cdef_class cls = get_class (); | |
539 | |
540 if (cls.ok ()) | |
541 return cls.get_names (); | |
542 | |
543 return string_vector (); | |
544 } | |
545 | |
546 octave_value_list | |
547 handle_cdef_object::subsref (const std::string& type, | |
548 const std::list<octave_value_list>& idx, | |
549 int nargout, int& skip) | |
550 { | |
551 skip = 0; | |
552 | |
553 cdef_class cls = get_class (); | |
554 | |
555 octave_value_list retval; | |
556 | |
557 if (! cls.ok ()) | |
558 return retval; | |
559 | |
560 switch (type[0]) | |
561 { | |
562 case '.': | |
563 { | |
564 std::string name = (idx.front ())(0).string_value (); | |
565 | |
566 // FIXME: get the right context; context should also | |
567 // be linked to the current executing class (if any) | |
568 // such that protected/private methods found in inherited | |
569 // classes are correctly resolved. | |
570 std::string context = "public"; | |
571 | |
572 cdef_method meth = cls.find_method (name); | |
573 | |
574 if (meth.ok ()) | |
575 { | |
576 if (meth.check_access (context)) | |
577 { | |
578 int _nargout = (type.length () > 2 ? 1 : nargout); | |
579 | |
580 octave_value_list args; | |
581 | |
582 skip = 1; | |
583 | |
584 if (type.length () > 1 && type[1] == '(') | |
585 { | |
586 std::list<octave_value_list>::const_iterator it = idx.begin (); | |
587 | |
588 args = *++it; | |
589 | |
590 skip++; | |
591 } | |
592 | |
593 if (meth.is_static ()) | |
594 retval = meth.execute (args, _nargout); | |
595 else | |
596 { | |
597 refcount++; | |
598 retval = meth.execute (cdef_object (this), args, _nargout); | |
599 } | |
600 } | |
601 else | |
602 gripe_method_access ("subsref", meth); | |
603 } | |
604 | |
605 if (skip == 0 && ! error_state) | |
606 { | |
607 cdef_property prop = cls.find_property (name); | |
608 | |
609 if (prop.ok ()) | |
610 { | |
611 if (prop.check_get_access (context)) | |
612 { | |
613 refcount++; | |
614 retval(0) = prop.get_value (cdef_object (this)); | |
615 | |
616 skip = 1; | |
617 } | |
618 else | |
619 gripe_property_access ("subsref", prop); | |
620 } | |
621 else | |
622 error ("subsref: unknown method or property: %s", name.c_str ()); | |
623 } | |
624 break; | |
625 } | |
626 default: | |
627 error ("object cannot be indexed with `%c'", type[0]); | |
628 break; | |
629 } | |
630 | |
631 return retval; | |
632 } | |
633 | |
634 cdef_method | |
635 cdef_class::cdef_class_rep::find_method (const std::string& nm) | |
636 { | |
637 method_iterator it = method_map.find (nm); | |
638 | |
639 if (it == method_map.end ()) | |
640 { | |
641 // FIXME: look into class directory | |
642 } | |
643 else | |
644 { | |
645 cdef_method& meth = it->second; | |
646 | |
647 // FIXME: check if method reload needed | |
648 | |
649 if (meth.ok ()) | |
650 return meth; | |
651 } | |
652 | |
653 // Look into superclasses | |
654 | |
655 Cell super_classes = get ("SuperClasses").cell_value (); | |
656 | |
657 for (int i = 0; i < super_classes.numel (); i++) | |
658 { | |
659 cdef_class cls = lookup_class (super_classes(i).string_value ()); | |
660 | |
661 if (! error_state) | |
662 { | |
663 cdef_method meth = cls.find_method (nm); | |
664 | |
665 if (meth.ok ()) | |
666 return meth; | |
667 } | |
668 } | |
669 | |
670 return cdef_method (); | |
671 } | |
672 | |
673 void | |
674 cdef_class::cdef_class_rep::install_method (const cdef_method& meth) | |
675 { | |
676 method_map[meth.get_name ()] = meth; | |
677 } | |
678 | |
679 void | |
680 cdef_class::cdef_class_rep::load_all_methods (void) | |
681 { | |
682 // FIXME: re-scan class directory | |
683 } | |
684 | |
685 Cell | |
686 cdef_class::cdef_class_rep::get_methods (void) | |
687 { | |
688 std::map<std::string,cdef_method> meths; | |
689 | |
690 std::map<std::string,int> count; | |
691 | |
692 count["public"] = count["protected"] = count["private"] = 0; | |
693 | |
694 find_methods (meths, count); | |
695 | |
696 if (! error_state) | |
697 { | |
698 Cell c (count["public"] + count["protected"], 1); | |
699 | |
700 int idx = 0; | |
701 | |
702 for (std::map<std::string,cdef_method>::const_iterator it = meths.begin (); | |
703 it != meths.end (); ++it) | |
704 if (::check_access ("protected", it->second.get_access ())) | |
705 c (idx++, 0) = to_ov (it->second); | |
706 | |
707 return c; | |
708 } | |
709 | |
710 return Cell (); | |
711 } | |
712 | |
713 void | |
714 cdef_class::cdef_class_rep::find_methods (std::map<std::string,cdef_method>& meths, | |
715 std::map<std::string,int>& count) | |
716 { | |
717 load_all_methods (); | |
718 | |
719 method_const_iterator it; | |
720 | |
721 for (it = method_map.begin (); it != method_map.end (); ++it) | |
722 { | |
723 std::string nm = it->second.get_name (); | |
724 | |
725 if (meths.find (nm) == meths.end ()) | |
726 { | |
727 std::string acc = it->second.get_access (); | |
728 | |
729 meths[nm] = it->second; | |
730 count[acc]++; | |
731 } | |
732 } | |
733 | |
734 // Look into superclasses | |
735 | |
736 Cell super_classes = get ("SuperClasses").cell_value (); | |
737 | |
738 for (int i = 0; i < super_classes.numel (); i++) | |
739 { | |
740 cdef_class cls = lookup_class (super_classes(i).string_value ()); | |
741 | |
742 if (! error_state) | |
743 cls.get_rep ()->find_methods (meths, count); | |
744 else | |
745 break; | |
746 } | |
747 } | |
748 | |
749 cdef_property | |
750 cdef_class::cdef_class_rep::find_property (const std::string& nm) | |
751 { | |
752 property_iterator it = property_map.find (nm); | |
753 | |
754 if (it != property_map.end ()) | |
755 { | |
756 cdef_property& prop = it->second; | |
757 | |
758 if (prop.ok ()) | |
759 return prop; | |
760 } | |
761 | |
762 // Look into superclasses | |
763 | |
764 Cell super_classes = get ("SuperClasses").cell_value (); | |
765 | |
766 for (int i = 0; i < super_classes.numel (); i++) | |
767 { | |
768 cdef_class cls = lookup_class (super_classes(i).string_value ()); | |
769 | |
770 if (! error_state) | |
771 { | |
772 cdef_property prop = cls.find_property (nm); | |
773 | |
774 if (prop.ok ()) | |
775 return prop; | |
776 } | |
777 } | |
778 | |
779 return cdef_property (); | |
780 } | |
781 | |
782 void | |
783 cdef_class::cdef_class_rep::install_property (const cdef_property& prop) | |
784 { | |
785 property_map[prop.get_name ()] = prop; | |
786 } | |
787 | |
788 Cell | |
789 cdef_class::cdef_class_rep::get_properties (void) | |
790 { | |
791 std::map<std::string,cdef_property> props; | |
792 | |
793 std::map<std::string,int> count; | |
794 | |
795 count["public"] = count["protected"] = count["private"] = 0; | |
796 | |
797 find_properties (props, count); | |
798 | |
799 if (! error_state) | |
800 { | |
801 Cell c (count["public"] + count["protected"], 1); | |
802 | |
803 int idx = 0; | |
804 | |
805 for (std::map<std::string,cdef_property>::const_iterator it = props.begin (); | |
806 it != props.end (); ++it) | |
807 if (::check_access ("protected", it->second.get_get_access ())) | |
808 c (idx++, 0) = to_ov (it->second); | |
809 | |
810 return c; | |
811 } | |
812 | |
813 return Cell (); | |
814 } | |
815 | |
816 void | |
817 cdef_class::cdef_class_rep::find_properties (std::map<std::string,cdef_property>& props, | |
818 std::map<std::string,int>& count) | |
819 { | |
820 property_const_iterator it; | |
821 | |
822 for (it = property_map.begin (); it != property_map.end (); ++it) | |
823 { | |
824 std::string nm = it->second.get_name (); | |
825 | |
826 if (props.find (nm) == props.end ()) | |
827 { | |
828 std::string acc = it->second.get_get_access (); | |
829 | |
830 props[nm] = it->second; | |
831 count[acc]++; | |
832 } | |
833 } | |
834 | |
835 // Look into superclasses | |
836 | |
837 Cell super_classes = get ("SuperClasses").cell_value (); | |
838 | |
839 for (int i = 0; i < super_classes.numel (); i++) | |
840 { | |
841 cdef_class cls = lookup_class (super_classes(i).string_value ()); | |
842 | |
843 if (! error_state) | |
844 cls.get_rep ()->find_properties (props, count); | |
845 else | |
846 break; | |
847 } | |
848 } | |
849 | |
850 void | |
851 cdef_class::cdef_class_rep::find_names (std::map<std::string,std::string>& names, | |
852 std::map<std::string,int>& count) | |
853 { | |
854 load_all_methods (); | |
855 | |
856 for (method_const_iterator it = method_map.begin (); | |
857 it != method_map.end(); ++it) | |
858 { | |
859 std::string nm = it->second.get_name (); | |
860 | |
861 if (names.find (nm) == names.end ()) | |
862 { | |
863 std::string acc = it->second.get_access (); | |
864 | |
865 names[nm] = acc; | |
866 count[acc]++; | |
867 } | |
868 } | |
869 | |
870 for (property_const_iterator it = property_map.begin (); | |
871 it != property_map.end (); ++it) | |
872 { | |
873 std::string nm = it->second.get_name (); | |
874 | |
875 if (names.find (nm) == names.end ()) | |
876 { | |
877 std::string acc = it->second.get_get_access (); | |
878 | |
879 names[nm] = acc; | |
880 count[acc]++; | |
881 } | |
882 } | |
883 | |
884 // Look into superclasses | |
885 | |
886 Cell super_classes = get ("SuperClasses").cell_value (); | |
887 | |
888 for (int i = 0; i < super_classes.numel (); i++) | |
889 { | |
890 cdef_class cls = lookup_class (super_classes(i).string_value ()); | |
891 | |
892 if (! error_state) | |
893 cls.get_rep ()->find_names (names, count); | |
894 else | |
895 break; | |
896 } | |
897 } | |
898 | |
899 string_vector | |
900 cdef_class::cdef_class_rep::get_names (void) | |
901 { | |
902 std::map<std::string,std::string> names; | |
903 | |
904 std::map<std::string,int> count; | |
905 | |
906 count["public"] = count["protected"] = count["private"] = 0; | |
907 | |
908 find_names (names, count); | |
909 | |
910 if (! error_state) | |
911 { | |
912 string_vector v (count["public"]); | |
913 | |
914 int idx = 0; | |
915 for (std::map<std::string,std::string>::const_iterator it = names.begin (); | |
916 it != names.end (); ++it) | |
917 { | |
918 if (it->second == "public") | |
919 v[idx++] = it->first; | |
920 } | |
921 | |
922 return v.sort (true); | |
923 } | |
924 | |
925 return string_vector (); | |
926 } | |
927 | |
928 void | |
929 cdef_class::cdef_class_rep::delete_object (cdef_object obj) | |
930 { | |
931 method_iterator it = method_map.find ("delete"); | |
932 | |
933 if (it != method_map.end ()) | |
934 { | |
935 std::string cls_name = obj.class_name (); | |
936 | |
937 obj.set_class_name (get ("Name").string_value ()); | |
938 | |
939 it->second.execute (obj, octave_value_list (), 0); | |
940 | |
941 obj.set_class_name (cls_name); | |
942 } | |
943 | |
944 // FIXME: should we destroy corresponding properties here? | |
945 | |
946 // Call "delete" in super classes | |
947 | |
948 Cell super_classes = get ("SuperClasses").cell_value (); | |
949 | |
950 for (int i = 0; i < super_classes.numel (); i++) | |
951 { | |
952 cdef_class cls = lookup_class (super_classes(i).string_value ()); | |
953 | |
954 if (!error_state) | |
955 cls.delete_object (obj); | |
956 } | |
957 } | |
958 | |
959 octave_value | |
960 cdef_property::cdef_property_rep::get_value (const cdef_object& obj) | |
961 { | |
962 // FIXME: should check whether we're already in get accessor method | |
963 | |
964 octave_value retval; | |
965 | |
966 octave_value get_fcn = get ("GetMethod"); | |
967 | |
968 std::string get_access = get ("GetAccess").string_value (); | |
969 | |
970 if (get_access != "public") | |
971 { | |
972 // FIXME: should check the current call stack | |
973 } | |
974 | |
975 if (get_fcn.is_empty ()) | |
976 retval = obj.get (get ("Name").string_value ()); | |
977 else | |
978 { | |
979 octave_value_list args; | |
980 | |
981 args(0) = to_ov (obj); | |
982 | |
983 args = execute_ov (get_fcn, args, 1); | |
984 | |
985 if (! error_state) | |
986 retval = args(0); | |
987 } | |
988 | |
989 return retval; | |
990 } | |
991 | |
992 bool | |
993 cdef_property::check_get_access (const std::string& req) const | |
994 { | |
995 return ::check_access (req, get_get_access ()); | |
996 } | |
997 | |
998 bool | |
999 cdef_property::check_set_access (const std::string& req) const | |
1000 { | |
1001 return ::check_access (req, get_set_access ()); | |
1002 } | |
1003 | |
1004 void | |
1005 cdef_method::cdef_method_rep::check_method (void) | |
1006 { | |
1007 // FIXME: check whether re-load is needed | |
1008 } | |
1009 | |
1010 octave_value_list | |
1011 cdef_method::cdef_method_rep::execute (const octave_value_list& args, | |
1012 int nargout) | |
1013 { | |
1014 octave_value_list retval; | |
1015 | |
1016 if (! get ("Abstract").bool_value ()) | |
1017 { | |
1018 check_method (); | |
1019 | |
1020 if (function.is_defined ()) | |
1021 { | |
1022 retval = execute_ov (function, args, nargout); | |
1023 } | |
1024 } | |
1025 else | |
1026 error ("%s: cannot execute abstract method", | |
1027 get ("Name").string_value ().c_str ()); | |
1028 | |
1029 return retval; | |
1030 } | |
1031 | |
1032 octave_value_list | |
1033 cdef_method::cdef_method_rep::execute (const cdef_object& obj, | |
1034 const octave_value_list& args, | |
1035 int nargout) | |
1036 { | |
1037 octave_value_list retval; | |
1038 | |
1039 if (! get ("Abstract").bool_value ()) | |
1040 { | |
1041 check_method (); | |
1042 | |
1043 octave_value_list new_args; | |
1044 | |
1045 if (function.is_defined ()) | |
1046 { | |
1047 new_args.resize (args.length () + 1); | |
1048 | |
1049 new_args(0) = to_ov (obj); | |
1050 for (int i = 0; i < args.length (); i++) | |
1051 new_args(i+1) = args(i); | |
1052 | |
1053 retval = execute_ov (function, new_args, nargout); | |
1054 } | |
1055 } | |
1056 else | |
1057 error ("%s: cannot execute abstract method", | |
1058 get ("Name").string_value ().c_str ()); | |
1059 | |
1060 return retval; | |
1061 } | |
1062 | |
1063 bool | |
1064 cdef_method::check_access (const std::string& req) const | |
1065 { | |
1066 return ::check_access (req, get_access ()); | |
1067 } | |
1068 | |
1069 static cdef_package | |
1070 lookup_package (const std::string& name) | |
1071 { | |
1072 std::map<std::string, cdef_package>::const_iterator it = all_packages.find (name); | |
1073 | |
1074 if (it != all_packages.end ()) | |
1075 { | |
1076 cdef_package pack = it->second; | |
1077 | |
1078 if (pack.ok ()) | |
1079 return pack; | |
1080 else | |
1081 error ("invalid package: %s", name.c_str ()); | |
1082 } | |
1083 else | |
1084 error ("package not found: %s", name.c_str ()); | |
1085 | |
1086 return cdef_package (); | |
1087 } | |
1088 | |
1089 static octave_value_list | |
1090 package_fromName (const octave_value_list& args, int /* nargout */) | |
1091 { | |
1092 octave_value_list retval; | |
1093 | |
1094 if (args.length () == 1) | |
1095 { | |
1096 std::string name = args(0).string_value (); | |
1097 | |
1098 if (! error_state) | |
1099 retval(0) = to_ov (lookup_package (name)); | |
1100 else | |
1101 error ("fromName: invalid package name, expected a string value"); | |
1102 } | |
1103 else | |
1104 error ("fromName: invalid number of parameters"); | |
1105 | |
1106 return retval; | |
1107 } | |
1108 | |
1109 static octave_value_list | |
1110 package_get_classes (const octave_value_list& args, int /* nargout */) | |
1111 { | |
1112 octave_value_list retval (1, Matrix ()); | |
1113 | |
1114 if (args.length () == 1 && args(0).type_name () == "object" | |
1115 && args(0).class_name () == "meta.package") | |
1116 { | |
1117 cdef_package pack (to_cdef (args(0))); | |
1118 | |
1119 retval(0) = pack.get_classes (); | |
1120 } | |
1121 | |
1122 return retval; | |
1123 } | |
1124 | |
1125 static octave_value_list | |
1126 package_get_functions (const octave_value_list& args, int /* nargout */) | |
1127 { | |
1128 octave_value_list retval (1, Matrix ()); | |
1129 | |
1130 if (args.length () == 0 && args(0).type_name () == "object" | |
1131 && args(0).class_name () == "meta.package") | |
1132 { | |
1133 cdef_package pack (to_cdef (args(0))); | |
1134 | |
1135 retval(0) = pack.get_functions (); | |
1136 } | |
1137 | |
1138 return retval; | |
1139 } | |
1140 | |
1141 static octave_value_list | |
1142 package_get_packages (const octave_value_list& args, int /* nargout */) | |
1143 { | |
1144 octave_value_list retval (1, Matrix ()); | |
1145 | |
1146 if (args.length () == 0 && args(0).type_name () == "object" | |
1147 && args(0).class_name () == "meta.package") | |
1148 { | |
1149 cdef_package pack (to_cdef (args(0))); | |
1150 | |
1151 retval(0) = pack.get_packages (); | |
1152 } | |
1153 | |
1154 return retval; | |
1155 } | |
1156 | |
1157 void | |
1158 cdef_package::cdef_package_rep::install_class (const cdef_class& cls, | |
1159 const std::string& nm) | |
1160 { | |
1161 class_map[nm] = cls; | |
1162 } | |
1163 | |
1164 void | |
1165 cdef_package::cdef_package_rep::install_function (const octave_value& fcn, | |
1166 const std::string& nm) | |
1167 { | |
1168 function_map[nm] = fcn; | |
1169 } | |
1170 | |
1171 void | |
1172 cdef_package::cdef_package_rep::install_package (const cdef_package& pack, | |
1173 const std::string& nm) | |
1174 { | |
1175 package_map[nm] = pack; | |
1176 } | |
1177 | |
1178 octave_value_list | |
1179 cdef_package::cdef_package_rep::subsref (const std::string& type, | |
1180 const std::list<octave_value_list>& idx, | |
1181 int nargout, int& skip) | |
1182 { | |
1183 return handle_cdef_object::subsref (type, idx, nargout, skip); | |
1184 } | |
1185 | |
1186 template<class T1, class T2> | |
1187 Cell | |
1188 map2Cell (const std::map<T1, T2>& m) | |
1189 { | |
1190 Cell retval (1, m.size ()); | |
1191 int i = 0; | |
1192 | |
1193 for (typename std::map<T1, T2>::const_iterator it = m.begin (); | |
1194 it != m.end (); ++it, ++i) | |
1195 { | |
1196 retval(i) = to_ov (it->second); | |
1197 } | |
1198 | |
1199 return retval; | |
1200 } | |
1201 | |
1202 Cell | |
1203 cdef_package::cdef_package_rep::get_classes (void) const | |
1204 { return map2Cell (class_map); } | |
1205 | |
1206 Cell | |
1207 cdef_package::cdef_package_rep::get_functions (void) const | |
1208 { return map2Cell (function_map); } | |
1209 | |
1210 Cell | |
1211 cdef_package::cdef_package_rep::get_packages (void) const | |
1212 { return map2Cell (package_map); } | |
1213 | |
1214 octave_value_list | |
1215 octave_classdef::subsref (const std::string& type, | |
1216 const std::list<octave_value_list>& idx, | |
1217 int nargout) | |
1218 { | |
1219 int skip = 0; | |
1220 octave_value_list retval; | |
1221 | |
1222 // FIXME: should check "subsref" method first | |
1223 | |
1224 retval = object.subsref (type, idx, nargout, skip); | |
1225 | |
1226 if (! error_state) | |
1227 { | |
1228 if (type.length () > skip && idx.size () > skip) | |
1229 retval = retval(0).next_subsref (nargout, type, idx, skip); | |
1230 } | |
1231 | |
1232 return retval; | |
1233 } | |
1234 | |
1235 void | |
1236 install_classdef (void) | |
1237 { | |
1238 octave_classdef::register_type (); | |
1239 | |
1240 /* meta classes */ | |
1241 cdef_class handle = make_class ("handle"); | |
1242 cdef_class meta_class = make_class ("meta.class", "handle"); | |
1243 cdef_class meta_property = make_class ("meta.property", "handle"); | |
1244 cdef_class meta_method = make_class ("meta.method", "handle"); | |
1245 cdef_class meta_event = make_class ("meta.event", "handle"); | |
1246 cdef_class meta_package = make_class ("meta.package", "handle"); | |
1247 cdef_class meta_dynproperty = make_class ("meta.dynamicproperty", "handle"); | |
1248 | |
1249 /* meta.class properties */ | |
1250 meta_class.install_property (make_attribute (meta_class, "ConstructOnLoad")); | |
1251 meta_class.install_property (make_property (meta_class, "ContainingPackage")); | |
1252 meta_class.install_property (make_property (meta_class, "Description")); | |
1253 meta_class.install_property (make_property (meta_class, "DetailedDescription")); | |
1254 meta_class.install_property (make_property (meta_class, "Events")); | |
1255 meta_class.install_property (make_attribute (meta_class, "Hidden")); | |
1256 meta_class.install_property | |
1257 (make_property (meta_class, "InferiorClasses", | |
1258 make_fcn_handle (class_get_inferiorclasses, "meta.class>get.InferiorClasses"), | |
1259 "public", Matrix (), "private")); | |
1260 meta_class.install_property | |
1261 (make_property (meta_class, "Methods", | |
1262 make_fcn_handle (class_get_methods, "meta.class>get.Methods"), | |
1263 "public", Matrix (), "private")); | |
1264 meta_class.install_property | |
1265 (make_property (meta_class, "MethodList", | |
1266 make_fcn_handle (class_get_methods, "meta.class>get.MethodList"), | |
1267 "public", Matrix (), "private")); | |
1268 meta_class.install_property (make_attribute (meta_class, "Name")); | |
1269 meta_class.install_property | |
1270 (make_property (meta_class, "Properties", | |
1271 make_fcn_handle (class_get_properties, "meta.class>get.Properties"), | |
1272 "public", Matrix (), "private")); | |
1273 meta_class.install_property | |
1274 (make_property (meta_class, "PropertyList", | |
1275 make_fcn_handle (class_get_properties, "meta.class>get.PropertyList"), | |
1276 "public", Matrix (), "private")); | |
1277 meta_class.install_property (make_attribute (meta_class, "Sealed")); | |
1278 meta_class.install_property | |
1279 (make_property (meta_class, "SuperClasses", | |
1280 make_fcn_handle (class_get_superclasses, "meta.class>get.SuperClasses"), | |
1281 "public", Matrix (), "private")); | |
1282 meta_class.install_property | |
1283 (make_property (meta_class, "SuperClassList", | |
1284 make_fcn_handle (class_get_superclasses, "meta.class>get.SuperClassList"), | |
1285 "public", Matrix (), "private")); | |
1286 /* meta.class methods */ | |
1287 meta_class.install_method (make_method (meta_class, "fromName", class_fromName, | |
1288 "public", true)); | |
1289 meta_class.install_method (make_method (meta_class, "fevalStatic", class_fevalStatic, | |
1290 "public", false)); | |
1291 meta_class.install_method (make_method (meta_class, "getConstant", class_getConstant, | |
1292 "public", false)); | |
1293 meta_class.install_method (make_method (meta_class, "eq", class_eq)); | |
1294 meta_class.install_method (make_method (meta_class, "ne", class_ne)); | |
1295 meta_class.install_method (make_method (meta_class, "lt", class_lt)); | |
1296 meta_class.install_method (make_method (meta_class, "le", class_le)); | |
1297 meta_class.install_method (make_method (meta_class, "gt", class_gt)); | |
1298 meta_class.install_method (make_method (meta_class, "ge", class_ge)); | |
1299 | |
1300 /* meta.method properties */ | |
1301 meta_method.install_property (make_attribute (meta_method, "Abstract")); | |
1302 meta_method.install_property (make_attribute (meta_method, "Access")); | |
1303 meta_method.install_property (make_attribute (meta_method, "DefiningClass")); | |
1304 meta_method.install_property (make_attribute (meta_method, "Description")); | |
1305 meta_method.install_property (make_attribute (meta_method, "DetailedDescription")); | |
1306 meta_method.install_property (make_attribute (meta_method, "Hidden")); | |
1307 meta_method.install_property (make_attribute (meta_method, "Name")); | |
1308 meta_method.install_property (make_attribute (meta_method, "Sealed")); | |
1309 meta_method.install_property (make_attribute (meta_method, "Static")); | |
1310 | |
1311 /* meta.property properties */ | |
1312 meta_property.install_property (make_attribute (meta_property, "Name")); | |
1313 meta_property.install_property (make_attribute (meta_property, "Description")); | |
1314 meta_property.install_property (make_attribute (meta_property, "DetailedDescription")); | |
1315 meta_property.install_property (make_attribute (meta_property, "Abstract")); | |
1316 meta_property.install_property (make_attribute (meta_property, "Constant")); | |
1317 meta_property.install_property (make_attribute (meta_property, "GetAccess")); | |
1318 meta_property.install_property (make_attribute (meta_property, "SetAccess")); | |
1319 meta_property.install_property (make_attribute (meta_property, "Dependent")); | |
1320 meta_property.install_property (make_attribute (meta_property, "Transient")); | |
1321 meta_property.install_property (make_attribute (meta_property, "Hidden")); | |
1322 meta_property.install_property (make_attribute (meta_property, "GetObservable")); | |
1323 meta_property.install_property (make_attribute (meta_property, "SetObservable")); | |
1324 meta_property.install_property (make_attribute (meta_property, "GetMethod")); | |
1325 meta_property.install_property (make_attribute (meta_property, "SetMethod")); | |
1326 meta_property.install_property (make_attribute (meta_property, "DefiningClass")); | |
1327 /* meta.property events */ | |
1328 // FIXME: add events | |
1329 | |
1330 /* handle methods */ | |
1331 handle.install_method (make_method (handle, "delete", handle_delete)); | |
1332 | |
1333 /* meta.package properties */ | |
1334 meta_package.install_property (make_attribute (meta_package, "Name")); | |
1335 meta_package.install_property (make_property (meta_package, "ContainingPackage")); | |
1336 meta_package.install_property | |
1337 (make_property (meta_package, "ClassList", | |
1338 make_fcn_handle (package_get_classes, "meta.package>get.ClassList"), | |
1339 "public", Matrix (), "private")); | |
1340 meta_package.install_property | |
1341 (make_property (meta_package, "Classes", | |
1342 make_fcn_handle (package_get_classes, "meta.package>get.Classes"), | |
1343 "public", Matrix (), "private")); | |
1344 meta_package.install_property | |
1345 (make_property (meta_package, "FunctionList", | |
1346 make_fcn_handle (package_get_functions, "meta.package>get.FunctionList"), | |
1347 "public", Matrix (), "private")); | |
1348 meta_package.install_property | |
1349 (make_property (meta_package, "Functions", | |
1350 make_fcn_handle (package_get_functions, "meta.package>get.Functions"), | |
1351 "public", Matrix (), "private")); | |
1352 meta_package.install_property | |
1353 (make_property (meta_package, "PackageList", | |
1354 make_fcn_handle (package_get_packages, "meta.package>get.PackageList"), | |
1355 "public", Matrix (), "private")); | |
1356 meta_package.install_property | |
1357 (make_property (meta_package, "Packages", | |
1358 make_fcn_handle (package_get_packages, "meta.package>get.Packages"), | |
1359 "public", Matrix (), "private")); | |
1360 meta_package.install_method (make_method (meta_package, "fromName", package_fromName, | |
1361 "public", true)); | |
1362 | |
1363 /* create "meta" package */ | |
1364 cdef_package package_meta = make_package ("meta"); | |
1365 package_meta.install_class (meta_class, "class"); | |
1366 package_meta.install_class (meta_property, "property"); | |
1367 package_meta.install_class (meta_method, "method"); | |
1368 package_meta.install_class (meta_package, "package"); | |
1369 package_meta.install_class (meta_event, "event"); | |
1370 package_meta.install_class (meta_dynproperty, "dynproperty"); | |
1371 } | |
1372 | |
1373 DEFUN (__meta_get_class__, args, , "") | |
1374 { | |
1375 octave_value retval; | |
1376 | |
1377 if (args.length () == 1) | |
1378 { | |
1379 std::string cname = args(0).string_value (); | |
1380 | |
1381 if (! error_state) | |
1382 retval = to_ov (lookup_class (cname)); | |
1383 else | |
1384 error ("invalid class name, expected a string value"); | |
1385 } | |
1386 else | |
1387 print_usage (); | |
1388 | |
1389 return retval; | |
1390 } | |
1391 | |
1392 DEFUN (__meta_get_package__, args, , "") | |
1393 { | |
1394 octave_value retval; | |
1395 | |
1396 if (args.length () == 1) | |
1397 { | |
1398 std::string cname = args(0).string_value (); | |
1399 | |
1400 if (! error_state) | |
1401 retval = to_ov (lookup_package (cname)); | |
1402 else | |
1403 error ("invalid package name, expected a string value"); | |
1404 } | |
1405 else | |
1406 print_usage (); | |
1407 | |
1408 return retval; | |
1409 } | |
1410 | |
1411 /* | |
1412 ;;; Local Variables: *** | |
1413 ;;; mode: C++ *** | |
1414 ;;; End: *** | |
1415 */ |