]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/libcdl/interface.cxx
Initial revision
[karo-tx-redboot.git] / tools / src / libcdl / interface.cxx
1 //{{{  Banner                           
2
3 //============================================================================
4 //
5 //     interface.cxx
6 //
7 //     Implementation of the CdlInterface class
8 //
9 //============================================================================
10 //####COPYRIGHTBEGIN####
11 //                                                                          
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1999, 2000 Red Hat, Inc.
15 //
16 // This file is part of the eCos host tools.
17 //
18 // This program is free software; you can redistribute it and/or modify it 
19 // under the terms of the GNU General Public License as published by the Free 
20 // Software Foundation; either version 2 of the License, or (at your option) 
21 // any later version.
22 // 
23 // This program is distributed in the hope that it will be useful, but WITHOUT 
24 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
25 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
26 // more details.
27 // 
28 // You should have received a copy of the GNU General Public License along with
29 // this program; if not, write to the Free Software Foundation, Inc., 
30 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31 //
32 // ----------------------------------------------------------------------------
33 //                                                                          
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
37 //
38 // Author(s):   bartv
39 // Contact(s):  bartv
40 // Date:        1999/03/01
41 // Version:     0.02
42 //
43 //####DESCRIPTIONEND####
44 //============================================================================
45
46 //}}}
47 //{{{  #include's                       
48
49 // ----------------------------------------------------------------------------
50 #include "cdlconfig.h"
51
52 // Get the infrastructure types, assertions, tracing and similar
53 // facilities.
54 #include <cyg/infra/cyg_ass.h>
55 #include <cyg/infra/cyg_trac.h>
56
57 // <cdlcore.hxx> defines everything implemented in this module.
58 // It implicitly supplies <string>, <vector> and <map> because
59 // the class definitions rely on these headers.
60 #include <cdlcore.hxx>
61
62 //}}}
63
64 //{{{  Statics                          
65
66 // ----------------------------------------------------------------------------
67 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlInterfaceBody);
68
69 //}}}
70 //{{{  Constructor                      
71
72 // ----------------------------------------------------------------------------
73 CdlInterfaceBody::CdlInterfaceBody(std::string name_arg, bool generated_arg)
74     : CdlNodeBody(name_arg),
75       CdlUserVisibleBody(),
76       CdlValuableBody(CdlValueFlavor_Data),
77       CdlParentableBody(),
78       CdlBuildableBody(),
79       CdlDefinableBody()
80 {
81     CYG_REPORT_FUNCNAME("CdlInterfaceBody:: constructor");
82     CYG_REPORT_FUNCARG1XV(this);
83
84     generated = generated_arg;
85     cdlinterfacebody_cookie = CdlInterfaceBody_Magic;
86     CYGDBG_MEMLEAK_CONSTRUCTOR();
87
88     CYG_POSTCONDITION_THISC();
89     CYG_REPORT_RETURN();
90 }
91
92 //}}}
93 //{{{  Destructor                       
94
95 // ----------------------------------------------------------------------------
96 CdlInterfaceBody::~CdlInterfaceBody()
97 {
98     CYG_REPORT_FUNCNAME("CdlInterfaceBody:: destructor");
99     CYG_REPORT_FUNCARG1XV(this);
100     CYG_PRECONDITION_THISC();
101
102     cdlinterfacebody_cookie = CdlInterfaceBody_Invalid;
103     CYGDBG_MEMLEAK_DESTRUCTOR();
104     
105     CYG_REPORT_RETURN();
106 }
107
108 //}}}
109 //{{{  parse_interface()                
110
111 // ----------------------------------------------------------------------------
112 // Parsing an interface definition. This is basically the same as parsing
113 // an option, component, or package.
114
115 int
116 CdlInterfaceBody::parse_interface(CdlInterpreter interp, int argc, const char* argv[])
117 {
118     CYG_REPORT_FUNCNAMETYPE("CdlInterface::parse_interface", "result %d");
119     CYG_REPORT_FUNCARG1("argc %d", argc);
120     CYG_PRECONDITION_CLASSC(interp);
121     
122     std::string  diag_argv0      = CdlParse::get_tcl_cmd_name(argv[0]);
123
124     CdlLoadable  loadable       = interp->get_loadable();
125     CdlContainer parent         = interp->get_container();       
126     CdlToplevel  toplevel       = interp->get_toplevel();
127     CYG_ASSERT_CLASSC(loadable);        // There should always be a loadable during parsing
128     CYG_ASSERT_CLASSC(parent);
129     CYG_ASSERT_CLASSC(toplevel);
130
131     // The new interface should be created and added to the loadable.
132     // early on. If there is a parsing error it will get cleaned up
133     // automatically as a consequence of the loadable destructor.
134     // However it is necessary to validate the name first. Errors
135     // should be reported via CdlParse::report_error(), which
136     // may result in an exception.
137     CdlInterface new_interface  = 0;
138     bool         ok             = true;
139     int          result         = TCL_OK;
140     try {
141     
142         // Currently there are no command-line options. This may change in future.
143         if (3 != argc) {
144             CdlParse::report_error(interp, "", std::string("Incorrect number of arguments to `") + diag_argv0 +
145                                    "'\nExpecting name and properties list.");
146             ok = false;
147         } else if (!Tcl_CommandComplete(CDL_TCL_CONST_CAST(char*, argv[2]))) {
148             CdlParse::report_error(interp, "", std::string("Invalid property list for cdl_interface `") + argv[1] + "'.");
149             ok = false;
150         } else if (0 != toplevel->lookup(argv[1])) {
151             // FIXME: interfaces can be generated implicitly because of an
152             // unresolved implements property. This code should look for
153             // an existing auto-generated interface object and replace it
154             // if necessary.
155             CdlParse::report_error(interp, "", std::string("Interface `") + argv[1] +
156                                    "' cannot be loaded.\nThe name is already in use.");
157             ok = false;
158         } else {
159             new_interface = new CdlInterfaceBody(argv[1], false);
160             toplevel->add_node(loadable, parent, new_interface);
161         }
162
163         if (!ok) {
164             // Just because this component cannot be created, that is no
165             // reason to abort the whole parsing process.
166             CYG_REPORT_RETVAL(TCL_OK);
167             return TCL_OK;
168         }
169     } catch(std::bad_alloc e) {
170         interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Out of memory"));
171         result = TCL_ERROR;
172     } catch(CdlParseException e) {
173         interp->set_result(e.get_message());
174         result = TCL_ERROR;
175     } catch(...) {
176         interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Unexpected C++ exception"));
177         result = TCL_ERROR;
178     }
179     if (TCL_OK != result) {
180         CYG_REPORT_RETVAL(result);
181         return result;
182     }
183
184     // At this stage new_interface has been created and added to the
185     // hierarchy. The main work now is to add the properties.
186     
187     // Push the option as the current node early on. This aids
188     // diagnostics.
189     CdlNode old_node = interp->push_node(new_interface);
190
191     // Declare these outside the scope of the try statement, to allow
192     // goto calls for the error handling.
193     std::string tcl_result;
194     std::vector<CdlInterpreterCommandEntry>  new_commands;
195     std::vector<CdlInterpreterCommandEntry>* old_commands = 0;
196     static CdlInterpreterCommandEntry   commands[] =
197     {
198         CdlInterpreterCommandEntry("", 0)
199     };
200     int i;
201     
202     // All parsing errors may result in an exception, under the control of
203     // application code. This exception must not pass through the Tcl interpreter.
204     try {
205
206         for (i = 0; 0 != commands[i].command; i++) {
207             new_commands.push_back(commands[i]);
208         }
209         CdlDefinableBody::add_property_parsers(new_commands);
210         CdlBuildableBody::add_property_parsers(new_commands);
211         CdlParentableBody::add_property_parsers(new_commands);
212         CdlValuableBody::add_property_parsers(new_commands);
213         CdlUserVisibleBody::add_property_parsers(new_commands);
214         CdlNodeBody::add_property_parsers(new_commands);
215     
216         // Now evaluate the body. If an error occurs then typically
217         // this will be reported via CdlParse::report_error(),
218         // but any exceptions will have been intercepted and
219         // turned into a Tcl error.
220         old_commands = interp->push_commands(new_commands);
221         result = interp->eval(argv[2], tcl_result);
222         interp->pop_commands(old_commands);
223         
224         if (TCL_OK == result) {
225             // Even if there were errors, they were not fatal. There may
226             // now be a number of properties for this option, and some
227             // validation should take place. Start with the base classes.
228             new_interface->CdlNodeBody::check_properties(interp);
229             new_interface->CdlUserVisibleBody::check_properties(interp);
230             new_interface->CdlValuableBody::check_properties(interp);
231             new_interface->CdlParentableBody::check_properties(interp);
232             new_interface->CdlBuildableBody::check_properties(interp);
233             new_interface->CdlDefinableBody::check_properties(interp);
234
235             // The flavor "none" makes no sense for interfaces.
236             // The flavor "bool" makes very little sense, but may be useful
237             // in weird cases. Both booldata and data make sense.
238             // The default flavor is "data", because interfaces are
239             // essentially just counters.
240             if (new_interface->has_property(CdlPropertyId_Flavor)) {
241                 if (CdlValueFlavor_None == new_interface->get_flavor()) {
242                     CdlParse::report_error(interp, "", "An interface should not have the `none' flavor.");
243                 }
244             }
245             
246             // Interfaces cannot be modified directly by the user, so
247             // there is no point in entry_proc, check_proc, dialog or
248             // wizard
249             if (new_interface->has_property(CdlPropertyId_EntryProc)) {
250                 CdlParse::report_error(interp, "", "An interface should not have an `entry_proc' property.");
251             }
252             if (new_interface->has_property(CdlPropertyId_CheckProc)) {
253                 CdlParse::report_error(interp, "", "An interface should not have a `check_proc' property.");
254             }
255             if (new_interface->has_property(CdlPropertyId_Dialog)) {
256                 CdlParse::report_error(interp, "", "An interface should not have a `dialog' property.");
257             }
258             if (new_interface->has_property(CdlPropertyId_Wizard)) {
259                 CdlParse::report_error(interp, "", "An interface should not have a `wizard' property.");
260             }
261             // Calculated does not make sense, an interface is implicitly calculated
262             // Nor does default_value.
263             if (new_interface->has_property(CdlPropertyId_Calculated)) {
264                 CdlParse::report_error(interp, "", "An interface should not have a `calculated' property.");
265             }
266             if (new_interface->has_property(CdlPropertyId_DefaultValue)) {
267                 CdlParse::report_error(interp, "", "An interface should not have a `default_value' property.");
268             }
269             // active_if might make sense, as a way of controlling
270             // whether or not a #define will be generated.
271             
272             // legal_values, requires and implements are all sensible
273             // properties for an interface.
274
275             // group may or may not make sense, allow it for now.
276
277             // For the uservisible base class, allow all of display,
278             // doc and description. At worst these are harmless
279
280             // For the parentable base class, allow parent. Again this
281             // is harmless.
282
283             // Also allow all of the definable and buildable
284             // properties.
285         }
286     } catch (std::bad_alloc e) {
287         // Errors at this stage should be reported via Tcl, not via C++.
288         // However there is no point in continuing with the parsing operation,
289         // just give up.
290         interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Out of memory"));
291         result = TCL_ERROR;
292     } catch (CdlParseException e) {
293         interp->set_result(e.get_message());
294         result = TCL_ERROR;
295     } catch(...) {
296         interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Unexpected C++ exception"));
297         result = TCL_ERROR;
298     }
299
300     // Restore the interpreter to its prior state.
301     interp->pop_node(old_node);
302     if (0 != old_commands) {
303         interp->pop_commands(old_commands);
304     }
305     
306     CYG_REPORT_RETVAL(result);
307     return result;
308 }
309
310 //}}}
311 //{{{  Persistence support              
312
313 // ----------------------------------------------------------------------------
314
315 void
316 CdlInterfaceBody::initialize_savefile_support(CdlToplevel toplevel)
317 {
318     CYG_REPORT_FUNCNAME("CdlInterface::initialize_savefile_support");
319
320     toplevel->add_savefile_command("cdl_interface", 0, &savefile_interface_command);
321     CdlValuableBody::initialize_savefile_support(toplevel, "cdl_interface");
322
323     CYG_REPORT_RETURN();
324 }
325
326 void
327 CdlInterfaceBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
328 {
329     CYG_REPORT_FUNCNAME("CdlInterface::save");
330     CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
331     CYG_PRECONDITION_THISC();
332     CYG_PRECONDITION_CLASSC(interp);
333
334     // Interfaces contain only calculated data, so for a minimal save
335     // there is no point in storing any of the data.
336     if (!minimal || this->has_additional_savefile_information()) {
337         // Start with the UserVisible data, which will result in a suitable set
338         // of comments before the package definition itself.
339         this->CdlUserVisibleBody::save(interp, chan, indentation, minimal);
340
341         // Now output the line "cdl_interface <name> {"
342         // The name is guaranteed to be a valid C preprocessor symbol, so it
343         // is not going to need any quoting.
344         std::string data = std::string(indentation, ' ') + "cdl_interface " + get_name() + " {\n";
345
346         // Start with details of everything that implements this interface.
347         if (!minimal) {
348             const std::vector<CdlReferrer>& referrers = this->get_referrers();
349             std::vector<CdlReferrer>::const_iterator ref_i;
350             int real_referrers = 0;
351             for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) {
352                 CdlNode     node = ref_i->get_source();
353                 CdlProperty prop = ref_i->get_source_property();
354
355                 CdlValuable valuable = dynamic_cast<CdlValuable>(node);
356                 if ((0 != valuable) && (CdlPropertyId_Implements == prop->get_property_name())) {
357                     real_referrers++;
358                     data += std::string(indentation, ' ') + "    # Implemented by " + valuable->get_name() + ", " +
359                         (valuable->is_active()  ? "active"  : "inactive") + ", " +
360                         (valuable->is_enabled() ? "enabled" : "disabled") + '\n';
361                 }
362             }
363             if (0 == real_referrers) {
364                 data += std::string(indentation, ' ') + "    # No options implement this inferface\n";
365             }
366         }
367         interp->write_data(chan, data);
368
369         // Deal with the value
370         this->CdlValuableBody::save(interp, chan, indentation + 4, false, minimal);
371
372         // Close the cdl_interface body. A blank line is added here.
373         data = "};\n\n";
374         interp->write_data(chan, data);
375     }
376     
377     CYG_REPORT_RETURN();
378 }
379
380 int
381 CdlInterfaceBody::savefile_interface_command(CdlInterpreter interp, int argc, const char* argv[])
382 {
383     CYG_REPORT_FUNCNAMETYPE("CdlInterface::savefile_interface_command", "result %d");
384     CYG_PRECONDITION_CLASSC(interp);
385
386     int result = TCL_OK;
387     CdlToplevel toplevel = interp->get_toplevel();
388     CYG_ASSERT_CLASSC(toplevel);
389
390     std::vector<CdlInterpreterCommandEntry> subcommands;
391     std::vector<CdlInterpreterCommandEntry>* toplevel_commands = 0;
392     CdlNode old_node = 0;
393     
394     try {
395         
396         if (3 != argc) {
397             CdlParse::report_error(interp, "", "Invalid cdl_interface command in savefile, expecting two arguments.");
398         } else {
399
400             CdlNode current_node = toplevel->lookup(argv[1]);
401             if (0 == current_node) {
402                 // FIXME: save value in limbo
403                 CdlParse::report_error(interp, "",
404                                        std::string("The savefile contains a cdl_interface command for an unknown interface `")
405                                        + argv[1] + "'.");
406             } else {
407                 toplevel->get_savefile_subcommands("cdl_interface", subcommands);
408                 toplevel_commands = interp->push_commands(subcommands);
409                 old_node = interp->push_node(current_node);
410                 
411                 std::string tcl_result;
412                 result = interp->eval(argv[2], tcl_result);
413             
414                 interp->pop_commands(toplevel_commands);
415                 toplevel_commands = 0;
416                 interp->pop_node(old_node);
417                 old_node = 0;
418             }
419         }
420     } catch(...) {
421         if (0 != old_node) {
422             interp->pop_node(old_node);
423         }
424         if (0 != toplevel_commands) {
425             interp->pop_commands(toplevel_commands);
426         }
427         throw;
428     }
429
430     CYG_REPORT_RETVAL(result);
431     return result;
432 }
433
434 //}}}
435 //{{{  check_this()                     
436
437 // ----------------------------------------------------------------------------
438
439 bool
440 CdlInterfaceBody::check_this(cyg_assert_class_zeal zeal) const
441 {
442     if (CdlInterfaceBody_Magic != cdlinterfacebody_cookie) {
443         return false;
444     }
445     CYGDBG_MEMLEAK_CHECKTHIS();
446     return CdlNodeBody::check_this(zeal)        &&
447            CdlUserVisibleBody::check_this(zeal) &&
448            CdlValuableBody::check_this(zeal)    &&
449            CdlParentableBody::check_this(zeal)  &&
450            CdlBuildableBody::check_this(zeal)   &&
451            CdlDefinableBody::check_this(zeal);
452 }
453
454 //}}}
455 //{{{  recalculate()                    
456
457 // ----------------------------------------------------------------------------
458 // There has been a change in the configuration which may affect the value
459 // of an interface. This can happen for a variety of reasons. For simplicity
460 // the entire value is just recalculated, with no attempt at optimisation.
461 // This may have to change in future.
462
463 void
464 CdlInterfaceBody::recalculate(CdlTransaction transaction)
465 {
466     CYG_REPORT_FUNCNAME("CdlInterface::recalculate");
467
468     const CdlValue& old_value = transaction->get_whole_value(this);
469
470     cdl_int count = 0;
471     std::vector<CdlValuable> implementers;
472     std::vector<CdlValuable>::const_iterator valuable_i;
473     this->get_implementers(implementers);
474     for (valuable_i = implementers.begin(); valuable_i != implementers.end(); valuable_i++) {
475         if (transaction->is_active(*valuable_i)) {
476             const CdlValue& implementer_value = transaction->get_whole_value(*valuable_i);
477             if (implementer_value.is_enabled()) {
478                 count++;
479             }
480         }
481     }
482
483     // What to do with the count depends on the flavor.
484     switch(this->get_flavor()) {
485       case CdlValueFlavor_Bool :
486         {
487             bool new_bool = (count > 0);
488             if (new_bool != old_value.is_enabled()) {
489                 CdlValue new_value = old_value;
490                 new_value.set_enabled(new_bool, CdlValueSource_Default);
491                 transaction->set_whole_value(this, old_value, new_value);
492             }
493             break;
494         }
495       case CdlValueFlavor_BoolData:
496         {
497             // The only thing that actually needs checking is the count value.
498             // Iff that has changed then the boolean part may need changing as well.
499             if (count != old_value.get_integer_value()) {
500                 CdlValue new_value = old_value;
501                 new_value.set_enabled_and_value(count > 0, count, CdlValueSource_Default);
502                 transaction->set_whole_value(this, old_value, new_value);
503             }
504
505             break;
506         }
507       case CdlValueFlavor_Data:
508         {
509             if (count != old_value.get_integer_value()) {
510                 CdlValue new_value = old_value;
511                 new_value.set_integer_value(count, CdlValueSource_Default);
512                 transaction->set_whole_value(this, old_value, new_value);
513             }
514             break;
515         }
516           
517       default:
518         break;
519     }
520     
521     CYG_REPORT_RETURN();
522 }
523
524 //}}}
525 //{{{  misc                             
526
527 // ----------------------------------------------------------------------------
528
529 bool
530 CdlInterfaceBody::is_modifiable() const
531 {
532     CYG_REPORT_FUNCNAME("CdlInterface::is_modifiable (false)");
533     CYG_PRECONDITION_THISC();
534     CYG_REPORT_RETURN();
535     return false;
536 }
537
538 std::string
539 CdlInterfaceBody::get_class_name() const
540 {
541     CYG_REPORT_FUNCNAME("CdlInterface::get_class_name");
542     CYG_PRECONDITION_THISC();
543     CYG_REPORT_RETURN();
544     return "interface";
545 }
546
547 void
548 CdlInterfaceBody::get_implementers(std::vector<CdlValuable>& implementers) const
549 {
550     CYG_REPORT_FUNCNAME("CdlInterface::get_implementers");
551     CYG_PRECONDITION_THISC();
552
553     const std::vector<CdlReferrer>& referrers = this->get_referrers();
554     std::vector<CdlReferrer>::const_iterator ref_i;
555     for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) {
556         CdlNode     node = ref_i->get_source();
557         CdlProperty prop = ref_i->get_source_property();
558
559         CdlValuable valuable = dynamic_cast<CdlValuable>(node);
560         if ((0 != valuable) && (CdlPropertyId_Implements == prop->get_property_name())) {
561             implementers.push_back(valuable);
562         }
563     }
564     
565     CYG_REPORT_RETURN();
566 }
567
568 // Interfaces are somewhat peculiar, in that they can be defined implicitly
569 // simply by occurring in an "implements" property, or explicitly inside
570 // a CDL script. Worse, they can switch between these two states when
571 // loadables are added or removed.
572
573 bool
574 CdlInterfaceBody::was_generated() const
575 {
576     CYG_REPORT_FUNCNAMETYPE("CdlInterface::was_generated", "result %d");
577     CYG_REPORT_FUNCARG1XV(this);
578     CYG_PRECONDITION_THISC();
579
580     bool result = this->generated;
581     CYG_REPORT_RETVAL(result);
582     return result;
583 }
584
585 //}}}