OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [host/] [libcdl/] [base.cxx] - Blame information for rev 810

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 786 skrzyp
//{{{  Banner                           
2
 
3
//============================================================================
4
//
5
//     base.cxx
6
//
7
//     Implementations of the various base classes
8
//
9
//============================================================================
10
// ####ECOSHOSTGPLCOPYRIGHTBEGIN####                                        
11
// -------------------------------------------                              
12
// This file is part of the eCos host tools.                                
13
// Copyright (C) 1999, 2000, 2002, 2003 Free Software Foundation, Inc.      
14
//
15
// This program is free software; you can redistribute it and/or modify     
16
// it under the terms of the GNU General Public License as published by     
17
// the Free Software Foundation; either version 2 or (at your option) any   
18
// later version.                                                           
19
//
20
// This program is distributed in the hope that it will be useful, but      
21
// WITHOUT ANY WARRANTY; without even the implied warranty of               
22
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        
23
// General Public License for more details.                                 
24
//
25
// You should have received a copy of the GNU General Public License        
26
// along with this program; if not, write to the                            
27
// Free Software Foundation, Inc., 51 Franklin Street,                      
28
// Fifth Floor, Boston, MA  02110-1301, USA.                                
29
// -------------------------------------------                              
30
// ####ECOSHOSTGPLCOPYRIGHTEND####                                          
31
//============================================================================
32
//#####DESCRIPTIONBEGIN####
33
//
34
// Author(s):   bartv
35
// Contact(s):  bartv
36
// Date:        1999/02/18
37
// Version:     0.02
38
// Description: libcdl defines a hierarchy of base classes, used for
39
//              constructing higher-level entities such as options
40
//              and packages.
41
//
42
//####DESCRIPTIONEND####
43
//============================================================================
44
 
45
//}}}
46
//{{{  #include's                       
47
 
48
// ----------------------------------------------------------------------------
49
#include "cdlconfig.h"
50
 
51
// Get the infrastructure types, assertions, tracing and similar
52
// facilities.
53
#include <cyg/infra/cyg_ass.h>
54
#include <cyg/infra/cyg_trac.h>
55
 
56
// <cdl.hxx> defines everything implemented in this module.
57
// It implicitly supplies <string>, <vector> and <map> because
58
// the class definitions rely on these headers.
59
#include <cdlcore.hxx>
60
 
61
//}}}
62
 
63
//{{{  Statics                          
64
 
65
// ----------------------------------------------------------------------------
66
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlNodeBody);
67
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlContainerBody);
68
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlLoadableBody);
69
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlToplevelBody);
70
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlUserVisibleBody);
71
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlParentableBody);
72
 
73
//}}}
74
//{{{  CdlNodeBody                      
75
 
76
//{{{  Construction                             
77
 
78
// ----------------------------------------------------------------------------
79
// The real constructor takes a string argument and should get invoked first.
80
// Because of the way virtual inheritance is used it is also necessary to have
81
// a default constructor, but that need not do anything. A newly constructed
82
// object does not yet live in the hierarchy.
83
 
84
CdlNodeBody::CdlNodeBody(std::string name_arg)
85
{
86
    CYG_REPORT_FUNCNAME("CdlNode:: constructor");
87
    CYG_REPORT_FUNCARG1XV(this);
88
    CYG_PRECONDITIONC("" != name_arg);
89
 
90
    name        = name_arg;
91
    parent      = 0;
92
    owner       = 0;
93
    toplevel    = 0;
94
    active      = false;
95
    remove_node_container_position = -1;
96
 
97
    // The STL containers will take care of themselves.
98
 
99
    cdlnodebody_cookie = CdlNodeBody_Magic;
100
    CYGDBG_MEMLEAK_CONSTRUCTOR();
101
 
102
    CYG_POSTCONDITION_THISC();
103
    CYG_REPORT_RETURN();
104
}
105
 
106
CdlNodeBody::CdlNodeBody()
107
{
108
    CYG_PRECONDITION_THISC();
109
}
110
 
111
//}}}
112
//{{{  Destructor                               
113
 
114
// ----------------------------------------------------------------------------
115
// By the time the destructor gets invoked the node should already
116
// have been unbound and removed from the hierarchy.
117
 
118
CdlNodeBody::~CdlNodeBody()
119
{
120
    CYG_REPORT_FUNCNAME("CdlNode:: destructor");
121
    CYG_REPORT_FUNCARG1XV(this);
122
    CYG_PRECONDITION_THISC();
123
 
124
    // Make sure that the node is unbound: all references to and from
125
    // this node should have been destroyed already inside a
126
    // transaction.
127
    CYG_PRECONDITIONC(0 == referrers.size());
128
 
129
    // Make sure that the node has been removed from the hierarchy
130
    CYG_PRECONDITIONC(0 == toplevel);
131
    CYG_PRECONDITIONC(0 == owner);
132
    CYG_PRECONDITIONC(0 == parent);
133
 
134
    // Destroy all properties associated with this object.
135
    std::vector<CdlProperty>::iterator prop_i;
136
    for (prop_i= properties.begin(); prop_i != properties.end(); prop_i++) {
137
        delete *prop_i;
138
        *prop_i = 0;
139
    }
140
    properties.clear();
141
 
142
    cdlnodebody_cookie  = CdlNodeBody_Invalid;
143
    name   = "";
144
    active = false;
145
    unsupported_savefile_strings.clear();
146
 
147
    CYGDBG_MEMLEAK_DESTRUCTOR();
148
 
149
    CYG_REPORT_RETURN();
150
}
151
 
152
//}}}
153
//{{{  Trivial data access                      
154
 
155
// ----------------------------------------------------------------------------
156
 
157
std::string
158
CdlNodeBody::get_name() const
159
{
160
    CYG_REPORT_FUNCNAME("CdlNode::get_name");
161
    CYG_REPORT_FUNCARG1XV(this);
162
    CYG_PRECONDITION_THISC();
163
 
164
    CYG_REPORT_RETURN();
165
    return name;
166
}
167
 
168
void
169
CdlNodeBody::set_name(std::string name_arg)
170
{
171
    CYG_REPORT_FUNCNAME("CdlNode::set_name");
172
    CYG_REPORT_FUNCARG1XV(this);
173
    CYG_PRECONDITION_THISC();
174
 
175
    name = name_arg;
176
 
177
    CYG_REPORT_RETURN();
178
}
179
 
180
CdlContainer
181
CdlNodeBody::get_parent() const
182
{
183
    CYG_REPORT_FUNCNAMETYPE("CdlNode::get_parent", "parent %p");
184
    CYG_REPORT_FUNCARG1XV(this);
185
    CYG_PRECONDITION_THISC();
186
 
187
    CdlContainer result = parent;
188
    CYG_REPORT_RETVAL(result);
189
    return result;
190
}
191
 
192
CdlLoadable
193
CdlNodeBody::get_owner() const
194
{
195
    CYG_REPORT_FUNCNAMETYPE("CdlNode::get_owner", "owner %p");
196
    CYG_REPORT_FUNCARG1XV(this);
197
    CYG_PRECONDITION_THISC();
198
 
199
    CdlLoadable result = owner;
200
    CYG_REPORT_RETVAL(result);
201
    return result;
202
}
203
 
204
CdlToplevel
205
CdlNodeBody::get_toplevel() const
206
{
207
    CYG_REPORT_FUNCNAMETYPE("CdlNode::get_toplevel", "toplevel %p");
208
    CYG_REPORT_FUNCARG1XV(this);
209
    CYG_PRECONDITION_THISC();
210
 
211
    CdlToplevel result = toplevel;
212
    CYG_REPORT_RETVAL(result);
213
    return result;
214
}
215
 
216
std::string
217
CdlNodeBody::get_class_name() const
218
{
219
    CYG_REPORT_FUNCNAME("CdlNode::get_class_name");
220
    CYG_PRECONDITION_THISC();
221
    CYG_REPORT_RETURN();
222
    return "node";
223
}
224
 
225
//}}}
226
//{{{  The properties vector                    
227
 
228
// ----------------------------------------------------------------------------
229
// Trivial manipulation of the properties vector.
230
 
231
const std::vector<CdlProperty>&
232
CdlNodeBody::get_properties() const
233
{
234
    CYG_REPORT_FUNCNAME("CdlNode::get_properties");
235
    CYG_REPORT_FUNCARG1XV(this);
236
    CYG_PRECONDITION_THISC();
237
 
238
    CYG_REPORT_RETURN();
239
    return properties;
240
}
241
 
242
CdlProperty
243
CdlNodeBody::get_property(std::string id) const
244
{
245
    CYG_REPORT_FUNCNAMETYPE("CdlNode::get_property", "result %p");
246
    CYG_REPORT_FUNCARG1XV(this);
247
    CYG_PRECONDITION_THISC();
248
 
249
    CdlProperty result = 0;
250
    std::vector<CdlProperty>::const_iterator prop_i;
251
    for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
252
        if ((*prop_i)->get_property_name() == id) {
253
            result = *prop_i;
254
            break;
255
        }
256
    }
257
 
258
    CYG_REPORT_RETVAL(result);
259
    return result;
260
}
261
 
262
void
263
CdlNodeBody::get_properties(std::string id, std::vector<CdlProperty>& result) const
264
{
265
    CYG_REPORT_FUNCNAME("CdlNode::get_properties");
266
    CYG_REPORT_FUNCARG1XV(this);
267
    CYG_PRECONDITION_THISC();
268
 
269
    std::vector<CdlProperty>::const_iterator prop_i;
270
    for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
271
        if ((*prop_i)->get_property_name() == id) {
272
            result.push_back(*prop_i);
273
        }
274
    }
275
 
276
    CYG_REPORT_RETURN();
277
}
278
 
279
std::vector<CdlProperty>
280
CdlNodeBody::get_properties(std::string id) const
281
{
282
    CYG_REPORT_FUNCNAME("CdlNode::get_properties");
283
    CYG_REPORT_FUNCARG1XV(this);
284
    CYG_PRECONDITION_THISC();
285
 
286
    std::vector<CdlProperty> result;
287
    std::vector<CdlProperty>::const_iterator prop_i;
288
    for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
289
        if ((*prop_i)->get_property_name() == id) {
290
            result.push_back(*prop_i);
291
        }
292
    }
293
 
294
    CYG_REPORT_RETURN();
295
    return result;
296
}
297
 
298
bool
299
CdlNodeBody::has_property(std::string id) const
300
{
301
    CYG_REPORT_FUNCNAMETYPE("CdlNode::has_property", "result %d");
302
    CYG_REPORT_FUNCARG1XV(this);
303
    CYG_PRECONDITION_THISC();
304
 
305
    bool result = false;
306
    std::vector<CdlProperty>::const_iterator prop_i;
307
    for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
308
        if ((*prop_i)->get_property_name() == id) {
309
            result = true;
310
            break;
311
        }
312
    }
313
 
314
    CYG_REPORT_RETVAL(result);
315
    return result;
316
}
317
 
318
int
319
CdlNodeBody::count_properties(std::string id) const
320
{
321
    CYG_REPORT_FUNCNAMETYPE("CdlNode::count_properties", "result %d");
322
    CYG_REPORT_FUNCARG1XV(this);
323
    CYG_PRECONDITION_THISC();
324
 
325
    int result = 0;
326
    std::vector<CdlProperty>::const_iterator prop_i;
327
    for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
328
        if ((*prop_i)->get_property_name() == id) {
329
            result++;
330
        }
331
    }
332
 
333
    CYG_REPORT_RETVAL(result);
334
    return result;
335
}
336
 
337
//}}}
338
//{{{  Conflicts                                
339
 
340
// ----------------------------------------------------------------------------
341
// Provide access to the current set of conflicts. This operates on the global
342
// state, more commonly these changes happen in the context of a transaction.
343
 
344
void
345
CdlNodeBody::get_conflicts(std::vector<CdlConflict>& result) const
346
{
347
    CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
348
    CYG_REPORT_FUNCARG1XV(this);
349
    CYG_PRECONDITION_THISC();
350
 
351
    const std::list<CdlConflict>& conflicts = toplevel->get_all_conflicts();
352
    std::list<CdlConflict>::const_iterator conf_i;
353
    for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
354
        if ((*conf_i)->get_node() == this) {
355
            result.push_back(*conf_i);
356
        }
357
    }
358
 
359
    CYG_REPORT_RETURN();
360
}
361
 
362
void
363
CdlNodeBody::get_conflicts(bool (*fn)(CdlConflict), std::vector<CdlConflict>& result) const
364
{
365
    CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
366
    CYG_REPORT_FUNCARG2XV(this, fn);
367
    CYG_PRECONDITION_THISC();
368
    CYG_CHECK_FUNC_PTRC(fn);
369
 
370
    const std::list<CdlConflict>& conflicts = toplevel->get_all_conflicts();
371
    std::list<CdlConflict>::const_iterator conf_i;
372
    for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
373
        if (((*conf_i)->get_node() == this) && ((*fn)(*conf_i))) {
374
            result.push_back(*conf_i);
375
        }
376
    }
377
 
378
    CYG_REPORT_RETURN();
379
}
380
 
381
void
382
CdlNodeBody::get_structural_conflicts(std::vector<CdlConflict>& result) const
383
{
384
    CYG_REPORT_FUNCNAME("CdlNode::get_structural_conflicts");
385
    CYG_REPORT_FUNCARG1XV(this);
386
    CYG_PRECONDITION_THISC();
387
 
388
    const std::list<CdlConflict>& conflicts = toplevel->get_all_structural_conflicts();
389
    std::list<CdlConflict>::const_iterator conf_i;
390
    for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
391
        if ((*conf_i)->get_node() == this) {
392
            result.push_back(*conf_i);
393
        }
394
    }
395
 
396
    CYG_REPORT_RETURN();
397
}
398
 
399
void
400
CdlNodeBody::get_structural_conflicts(bool (*fn)(CdlConflict), std::vector<CdlConflict>& result) const
401
{
402
    CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
403
    CYG_REPORT_FUNCARG2XV(this, fn);
404
    CYG_PRECONDITION_THISC();
405
    CYG_CHECK_FUNC_PTRC(fn);
406
 
407
    const std::list<CdlConflict>& conflicts = toplevel->get_all_structural_conflicts();
408
    std::list<CdlConflict>::const_iterator conf_i;
409
    for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
410
        if (((*conf_i)->get_node() == this) && ((*fn)(*conf_i))) {
411
            result.push_back(*conf_i);
412
        }
413
    }
414
 
415
    CYG_REPORT_RETURN();
416
}
417
 
418
//}}}
419
//{{{  Referrers                                
420
 
421
// ----------------------------------------------------------------------------
422
// And access to the referrers vector.
423
const std::vector<CdlReferrer>&
424
CdlNodeBody::get_referrers() const
425
{
426
    CYG_REPORT_FUNCNAME("CdlNode::get_referrers");
427
    CYG_REPORT_FUNCARG1XV(this);
428
    CYG_PRECONDITION_THISC();
429
 
430
    CYG_REPORT_RETURN();
431
    return referrers;
432
}
433
 
434
//}}}
435
//{{{  Property parsers                         
436
 
437
// ----------------------------------------------------------------------------
438
// Property parsing. For now there are now properties guaranteed to be
439
// associated with every node. This may change in future, e.g.
440
// internal debugging-related properties.
441
void
442
CdlNodeBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
443
{
444
    CYG_REPORT_FUNCNAME("CdlNode::add_property_parsers");
445
    CYG_REPORT_RETURN();
446
}
447
 
448
void
449
CdlNodeBody::check_properties(CdlInterpreter interp)
450
{
451
    CYG_REPORT_FUNCNAME("CdlNode::check_properties");
452
    CYG_REPORT_FUNCARG2XV(this, interp);
453
    CYG_PRECONDITION_THISC();
454
    CYG_PRECONDITION_CLASSC(interp);
455
 
456
    CYG_REPORT_RETURN();
457
}
458
 
459
//}}}
460
//{{{  is_active() etc.                         
461
 
462
// ----------------------------------------------------------------------------
463
// Associated with every node is a boolean that holds the current
464
// "active" state. Changes to this happen only at transaction
465
// commit time.
466
 
467
bool
468
CdlNodeBody::is_active() const
469
{
470
    CYG_REPORT_FUNCNAMETYPE("CdlNode::is_active", "result %d");
471
    CYG_REPORT_FUNCARG1XV(this);
472
    CYG_PRECONDITION_THISC();
473
 
474
    bool result = active;
475
    CYG_REPORT_RETVAL(result);
476
    return result;
477
}
478
 
479
bool
480
CdlNodeBody::is_active(CdlTransaction transaction)
481
{
482
    CYG_REPORT_FUNCNAMETYPE("CdlNode::is_active", "result %d");
483
    CYG_REPORT_FUNCARG2XV(this, transaction);
484
    CYG_PRECONDITION_THISC();
485
    CYG_PRECONDITION_ZERO_OR_CLASSC(transaction);
486
 
487
    bool result;
488
    if (0 != transaction) {
489
        result = transaction->is_active(this);
490
    } else {
491
        result = active;
492
    }
493
    CYG_REPORT_RETVAL(result);
494
    return result;
495
}
496
 
497
// This virtual member function allows nodes to check whether or not
498
// they should be active. Derived classes may impose additional
499
// constraints.
500
bool
501
CdlNodeBody::test_active(CdlTransaction transaction)
502
{
503
    CYG_REPORT_FUNCNAMETYPE("CdlNode::test_active", "result %d");
504
    CYG_PRECONDITION_THISC();
505
    CYG_PRECONDITION_CLASSC(transaction);
506
 
507
    bool result = false;
508
    if ((0 != parent) && (transaction->is_active(parent))) {
509
        CdlValuable valuable = dynamic_cast<CdlValuable>(parent);
510
        if (0 == valuable) {
511
            result = true;
512
        } else if (valuable->is_enabled(transaction)) {
513
            result = true;
514
        }
515
    }
516
 
517
    CYG_REPORT_RETVAL(result);
518
    return result;
519
}
520
 
521
//}}}
522
//{{{  Propagation support                      
523
 
524
// ----------------------------------------------------------------------------
525
// In the base class nothing needs doing for propagation.
526
void
527
CdlNodeBody::update(CdlTransaction transaction, CdlUpdate change)
528
{
529
    CYG_REPORT_FUNCNAME("CdlNode::update");
530
    CYG_REPORT_FUNCARG1XV(this);
531
    CYG_PRECONDITION_THISC();
532
    CYG_PRECONDITION_CLASSC(transaction);
533
 
534
    CYG_REPORT_RETURN();
535
}
536
 
537
//}}}
538
//{{{  Persistence support                      
539
 
540
// ----------------------------------------------------------------------------
541
// The CdlNode::save() member should never get invoked directly, it should
542
// get invoked indirectly from e.g. CdlOption::save(). Normally there is
543
// no information associated with a node that ends up in a save file
544
// (the calling code will have take care of the name etc.). However there
545
// is support in the library for storing application-specific data in the
546
// save file, for example GUI information, and this information must be
547
// preserved even if it is not recognised. Savefiles are self-describing,
548
// they contain details of all the commands that are applicable. 
549
// CdlNode::save() is responsible for outputting the unrecognised strings
550
// to the save file.
551
 
552
void
553
CdlNodeBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
554
{
555
    CYG_REPORT_FUNCNAME("CdlNode::save");
556
    CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
557
 
558
    if (unsupported_savefile_strings.size() != 0) {
559
        // We should already be inside the body of a suitable command,
560
        // e.g. cdl_option xyz { ... }
561
        // CdlToplevel::savefile_handle_unsupported() is responsible for
562
        // putting suitably formatted strings into the
563
        // unsupported_savefile_strings vector, so all that is needed here
564
        // is to dump those strings to the channel.
565
        std::string data = "\n";
566
        std::vector<std::string>::const_iterator str_i;
567
        for (str_i = unsupported_savefile_strings.begin(); str_i != unsupported_savefile_strings.end(); str_i++) {
568
            data += std::string(indentation, ' ') + *str_i + " ;\n";
569
        }
570
        interp->write_data(chan, data);
571
    }
572
 
573
    CYG_UNUSED_PARAM(bool, minimal);
574
    CYG_REPORT_RETURN();
575
}
576
 
577
bool
578
CdlNodeBody::has_additional_savefile_information() const
579
{
580
    CYG_REPORT_FUNCNAMETYPE("CdlNode::has_additional_savefile_information", "result %d");
581
    CYG_REPORT_FUNCARG1XV(this);
582
    CYG_PRECONDITION_THISC();
583
 
584
    bool result = (0 != unsupported_savefile_strings.size());
585
    CYG_REPORT_RETVAL(result);
586
    return result;
587
}
588
 
589
//}}}
590
//{{{  check_this()                             
591
 
592
// ----------------------------------------------------------------------------
593
// Because of multiple and virtual inheritance, check_this() may
594
// get called rather a lot. Unfortunately all of the checks are
595
// useful.
596
 
597
bool
598
CdlNodeBody::check_this(cyg_assert_class_zeal zeal) const
599
{
600
    if (CdlNodeBody_Magic != cdlnodebody_cookie) {
601
        return false;
602
    }
603
    CYGDBG_MEMLEAK_CHECKTHIS();
604
 
605
    if ("" == name) {
606
        return false;
607
    }
608
 
609
    // It is hard to validate the toplevel, owner, and parent
610
    // fields.
611
    //
612
    // 1) when a node is newly created all three fields will
613
    //    be null.
614
    // 2) the toplevel may be null if the node is in the process
615
    //    of being removed, e.g. during an unload operation.
616
    //    The node should still have a valid owner, and will
617
    //    have a parent unless the node is also the loadable.
618
    // 3) some nodes are special, e.g. the orphans container,
619
    //    and do not have an owner.
620
    //
621
    // So the following combinations can occur:
622
    //  Toplevel   Owner    Parent
623
    //     0         0         0          Creation & toplevel
624
    //     0       Valid       0          Loadable being unloaded
625
    //     0       Valid     Valid        Node being unloaded
626
    //   Valid       0       Valid        Orphans container
627
    //   Valid     Valid     Valid        Any node
628
    if (0 != toplevel) {
629
        if (0 == parent) {
630
            return false;
631
        }
632
    }
633
 
634
    switch(zeal) {
635
      case cyg_system_test :
636
      case cyg_extreme     :
637
      {
638
        if ((0 != toplevel) && (toplevel != this)) {
639
            if (!toplevel->check_this(cyg_quick)) {
640
                return false;
641
            }
642
            if (toplevel->lookup_table.find(name) == toplevel->lookup_table.end()) {
643
                return false;
644
            }
645
        }
646
        if (0 != parent) {
647
            if (!parent->check_this(cyg_quick)) {
648
                return false;
649
            }
650
            if (std::find(parent->contents.begin(), parent->contents.end(), this) == parent->contents.end()) {
651
                return false;
652
            }
653
        }
654
        if (0 != owner) {
655
            if (!owner->check_this(cyg_quick)) {
656
                return false;
657
            }
658
            if (std::find(owner->owned.begin(), owner->owned.end(), this) == owner->owned.end()) {
659
                return false;
660
            }
661
        }
662
        std::vector<CdlProperty>::const_iterator prop_i;
663
        for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
664
            if (!(*prop_i)->check_this(cyg_quick)) {
665
                return false;
666
            }
667
        }
668
        std::vector<CdlReferrer>::const_iterator ref_i;
669
        for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) {
670
            if (!ref_i->check_this(cyg_quick)) {
671
                return false;
672
            }
673
        }
674
      }
675
      case cyg_thorough    :
676
      case cyg_quick       :
677
      case cyg_trivial     :
678
      case cyg_none        :
679
      default              :
680
          break;
681
    }
682
 
683
    return true;
684
}
685
 
686
//}}}
687
 
688
//}}}
689
//{{{  CdlContainerBody                 
690
 
691
//{{{  Constructors                     
692
 
693
// ----------------------------------------------------------------------------
694
// A container simply holds other nodes in a hierarchy. Most
695
// containers correspond to data in CDL scripts, but there are
696
// exceptions. For example, if an option is reparented below some
697
// package or component that is not yet known then it can instead be
698
// reparented into an "orphans" container immediately below the
699
// toplevel.
700
//
701
// Adding and removing entries to a container is done inside the
702
// CdlToplevel add_node() and remove_node() members. These will update
703
// all the fields needed to keep the hierarchy consistent. This
704
// means that the CdlContainer class itself provides only limited
705
// functionality.
706
 
707
CdlContainerBody::CdlContainerBody()
708
{
709
    CYG_REPORT_FUNCNAME("CdlContainer:: default constructor");
710
    CYG_REPORT_FUNCARG1XV(this);
711
 
712
    cdlcontainerbody_cookie = CdlContainerBody_Magic;
713
    CYGDBG_MEMLEAK_CONSTRUCTOR();
714
 
715
    CYG_POSTCONDITION_THISC();
716
    CYG_REPORT_RETURN();
717
}
718
 
719
// This variant is for internal use, to allow the library to create
720
// containers that do not correspond to CDL entities such as
721
// the orphans container.
722
CdlContainerBody::CdlContainerBody(std::string name_arg)
723
    : CdlNodeBody(name_arg)
724
{
725
    CYG_REPORT_FUNCNAME("CdlContainerBody:: constructor (name)");
726
    CYG_REPORT_FUNCARG1XV(this);
727
 
728
    cdlcontainerbody_cookie = CdlContainerBody_Magic;
729
    CYGDBG_MEMLEAK_CONSTRUCTOR();
730
 
731
    CYG_POSTCONDITION_THISC();
732
    CYG_REPORT_RETURN();
733
}
734
 
735
//}}}
736
//{{{  Destructor                       
737
 
738
// ----------------------------------------------------------------------------
739
 
740
CdlContainerBody::~CdlContainerBody()
741
{
742
    CYG_REPORT_FUNCNAME("CdlContainer:: destructor");
743
    CYG_REPORT_FUNCARG1XV(this);
744
    CYG_PRECONDITION_THISC();
745
 
746
    // Containers should always be empty by the time they
747
    // get deleted. The toplevel and loadable destructors should
748
    // guarantee this.
749
    CYG_ASSERTC(0 == contents.size());
750
 
751
    cdlcontainerbody_cookie = CdlContainerBody_Invalid;
752
    CYGDBG_MEMLEAK_DESTRUCTOR();
753
 
754
    CYG_REPORT_RETURN();
755
}
756
 
757
//}}}
758
//{{{  Accessing the contents           
759
 
760
// ----------------------------------------------------------------------------
761
// Simple contents access facilities, including searching. Note that
762
// the toplevel class maintains a <name,ptr> map, which will usually
763
// be more efficient.
764
 
765
const std::vector<CdlNode>&
766
CdlContainerBody::get_contents() const
767
{
768
    CYG_REPORT_FUNCNAME("CdlContainer::get_contents");
769
    CYG_REPORT_FUNCARG1XV(this);
770
    CYG_PRECONDITION_THISC();
771
 
772
    CYG_REPORT_RETURN();
773
    return contents;
774
}
775
 
776
bool
777
CdlContainerBody::contains(CdlConstNode node, bool recurse) const
778
{
779
    CYG_REPORT_FUNCNAMETYPE("CdlContainer::contains (node)", "result %d");
780
    CYG_REPORT_FUNCARG3XV(this, node, recurse);
781
    CYG_PRECONDITION_THISC();
782
    CYG_PRECONDITION_CLASSC(node);
783
 
784
    bool result = false;
785
    std::vector<CdlNode>::const_iterator node_i;
786
    for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
787
        if (node == *node_i) {
788
            result = true;
789
            break;
790
        }
791
        if (recurse) {
792
            CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
793
            if ((0 != child) && child->contains(node, true)) {
794
                result = true;
795
                break;
796
            }
797
        }
798
    }
799
 
800
    CYG_REPORT_RETVAL(result);
801
    return result;
802
}
803
 
804
bool
805
CdlContainerBody::contains(const std::string name, bool recurse) const
806
{
807
    CYG_REPORT_FUNCNAMETYPE("CdlContainer::contains (name)", "result %d");
808
    CYG_REPORT_FUNCARG2XV(this, recurse);
809
    CYG_PRECONDITION_THISC();
810
    CYG_PRECONDITIONC("" != name);
811
 
812
    bool result = false;
813
    std::vector<CdlNode>::const_iterator node_i;
814
    for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
815
        if ((*node_i)->get_name() == name) {
816
            result = true;
817
            break;
818
        }
819
        if (recurse) {
820
            CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
821
            if ((0 != child) && child->contains(name, true)) {
822
                result = true;
823
                break;
824
            }
825
        }
826
    }
827
 
828
    CYG_REPORT_RETVAL(result);
829
    return result;
830
}
831
 
832
CdlNode
833
CdlContainerBody::find_node(const std::string name, bool recurse) const
834
{
835
    CYG_REPORT_FUNCNAMETYPE("CdlContainer::find_node", "result %p");
836
    CYG_REPORT_FUNCARG2XV(this, recurse);
837
    CYG_PRECONDITION_THISC();
838
    CYG_PRECONDITIONC("" != name);
839
 
840
    CdlNode result = 0;
841
    std::vector<CdlNode>::const_iterator node_i;
842
    for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
843
        if ((*node_i)->get_name() == name) {
844
            result = *node_i;
845
            break;
846
        }
847
        if (recurse) {
848
            CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
849
            if (0 != child) {
850
                result = child->find_node(name, true);
851
                if (0 != result) {
852
                    break;
853
                }
854
            }
855
        }
856
    }
857
 
858
    CYG_REPORT_RETVAL(result);
859
    return result;
860
}
861
 
862
//}}}
863
//{{{  Misc                             
864
 
865
// ----------------------------------------------------------------------------
866
 
867
std::string
868
CdlContainerBody::get_class_name() const
869
{
870
    CYG_REPORT_FUNCNAME("CdlContainer::get_class_name");
871
    CYG_PRECONDITION_THISC();
872
    CYG_REPORT_RETURN();
873
    return "container";
874
}
875
 
876
//}}}
877
//{{{  Propagation                      
878
 
879
// ----------------------------------------------------------------------------
880
// If a container becomes active and is enabled then it is necessary
881
// to check all the children in case they want to become active as well.
882
 
883
void
884
CdlContainerBody::update(CdlTransaction transaction, CdlUpdate change)
885
{
886
    CYG_REPORT_FUNCNAME("CdlContainer::update");
887
    CYG_REPORT_FUNCARG1XV(this);
888
    CYG_PRECONDITION_THISC();
889
    CYG_PRECONDITION_CLASSC(transaction);
890
 
891
    if ((CdlUpdate_ActiveChange != change) && (CdlUpdate_ValueChange != change)) {
892
        CYG_REPORT_RETURN();
893
        return;
894
    }
895
 
896
    if (transaction->is_active(this)) {
897
        // The container has become active. It is necessary to check
898
        // all the children. If any of them should be active as well
899
        // but are not then this needs to change.
900
        std::vector<CdlNode>::iterator node_i;
901
 
902
        for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
903
            bool old_state = transaction->is_active(*node_i);
904
            bool new_state = (*node_i)->test_active(transaction);
905
            if (old_state != new_state) {
906
                transaction->set_active(*node_i, new_state);
907
            }
908
        }
909
    } else {
910
        // The container has become inactive. Any children that were
911
        // active should also become inactive.
912
        std::vector<CdlNode>::iterator node_i;
913
        for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
914
            if (transaction->is_active(*node_i)) {
915
                transaction->set_active(*node_i, false);
916
            }
917
        }
918
    }
919
 
920
    CYG_REPORT_RETURN();
921
}
922
 
923
//}}}
924
//{{{  Persistence                      
925
 
926
// ----------------------------------------------------------------------------
927
// This member function is invoked while traversing the hierarchy.
928
// The container itself will have been saved already, this member
929
// is responsible only for the contents. There are marker comments
930
// in the output file to indicate a new level in the hierarchy.
931
//
932
// Note that this member can also be invoked for the "orphans" container.
933
// That container will not appear in the save file, but its contents
934
// will.
935
 
936
void
937
CdlContainerBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
938
{
939
    CYG_REPORT_FUNCNAME("CdlContainer::save");
940
    CYG_REPORT_FUNCARG4XV(this, interp, chan, indentation);
941
    CYG_PRECONDITION_THISC();
942
    CYG_PRECONDITION_CLASSC(interp);
943
    CYG_PRECONDITIONC(0 == indentation);
944
 
945
    if (0 != contents.size()) {
946
        if (!minimal) {
947
            interp->write_data(chan, "# >\n");
948
        }
949
        std::vector<CdlNode>::const_iterator node_i;
950
        for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
951
            (*node_i)->save(interp, chan, indentation, minimal);
952
        }
953
        if (!minimal) {
954
            interp->write_data(chan, "# <\n");
955
        }
956
    }
957
 
958
    CYG_REPORT_RETURN();
959
}
960
 
961
//}}}
962
//{{{  check_this()                     
963
 
964
// ----------------------------------------------------------------------------
965
bool
966
CdlContainerBody::check_this(cyg_assert_class_zeal zeal) const
967
{
968
    if (CdlContainerBody_Magic != cdlcontainerbody_cookie) {
969
        return false;
970
    }
971
    CYGDBG_MEMLEAK_CHECKTHIS();
972
 
973
    if (cyg_extreme == zeal) {
974
        std::vector<CdlNode>::const_iterator node_i;
975
        for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
976
            if (!((*node_i)->check_this(cyg_quick))) {
977
                return false;
978
            }
979
        }
980
    }
981
    return CdlNodeBody::check_this(zeal);
982
}
983
 
984
//}}}
985
 
986
//}}}
987
//{{{  CdlLoadableBody                  
988
 
989
//{{{  Constructor                              
990
 
991
// ----------------------------------------------------------------------------
992
// A loadable object keeps track of all the nodes read in from a
993
// particular script, in an "owned" vector. Simply keeping things in a
994
// hierarchy is not enough because of possible re-parenting. Actual
995
// updates of the owned vector happen inside the CdlToplevel
996
// add_node() and remove_node() family.
997
 
998
CdlLoadableBody::CdlLoadableBody(CdlToplevel toplevel, std::string repo, std::string dir)
999
    : CdlContainerBody()
1000
{
1001
    CYG_REPORT_FUNCNAME("CdlLoadable:: constructor");
1002
    CYG_REPORT_FUNCARG1XV(this);
1003
    CYG_PRECONDITION_CLASSC(toplevel);
1004
 
1005
    // Initialize enough of the object to support check_this()
1006
    repository  = repo;
1007
    directory   = dir;
1008
    interp      = 0;
1009
    remove_node_loadables_position = -1;
1010
    cdlloadablebody_cookie = CdlLoadableBody_Magic;
1011
 
1012
    // The owned vector takes care of itself. It is necessary
1013
    // to create a new slave interpreter, using the master
1014
    // interpreter from the toplevel.
1015
    CdlInterpreter master = toplevel->get_interpreter();
1016
    CYG_ASSERTC(0 != master);
1017
    interp      = master->create_slave(this, false);
1018
    interp->push_context(this->get_name());
1019
    CYGDBG_MEMLEAK_CONSTRUCTOR();
1020
 
1021
    CYG_POSTCONDITION_THISC();
1022
    CYG_REPORT_RETURN();
1023
}
1024
 
1025
// Needed by derived classes, but should never actually be used.
1026
CdlLoadableBody::CdlLoadableBody()
1027
{
1028
    CYG_FAIL("CdlLoadable default constructor should never get invoked");
1029
}
1030
 
1031
//}}}
1032
//{{{  Destructor                               
1033
 
1034
// ----------------------------------------------------------------------------
1035
// The loadable destructor. This gets invoked from two places: after an
1036
// unsuccessful load operation, and from inside the transaction commit
1037
// code. Either way most of the clean-up will have happened already:
1038
// all the nodes will have been removed from the toplevel's hierarchy,
1039
// and all property references to and from this loadable will have been
1040
// unbound.
1041
//
1042
// Since all nodes belonging to the loadable are also present in
1043
// the owned vector, they must be destroyed before this destructor
1044
// completes. Hence clearing out the contents cannot be left to
1045
// the base CdlContainer destructor.
1046
 
1047
CdlLoadableBody::~CdlLoadableBody()
1048
{
1049
    CYG_REPORT_FUNCNAME("CdlLoadable:: destructor");
1050
    CYG_REPORT_FUNCARG1XV(this);
1051
    CYG_PRECONDITION_THISC();
1052
 
1053
    // Make sure that the loadable has already been removed from the
1054
    // hierarchy: it should not have a toplevel or a parent.
1055
    CYG_PRECONDITIONC(0 == toplevel);
1056
    CYG_PRECONDITIONC(0 == parent);
1057
 
1058
    // Containers must have been created before any of their contents.
1059
    // The only way to reverse this involves a parent property, but
1060
    // all such properties will have been unbound already such that
1061
    // the nodes can be safely deleted. The only worry is that
1062
    // loadables own themselves.
1063
    int i;
1064
    for (i = owned.size() - 1; i >= 0; i--) {
1065
        CdlNode node = owned[i];
1066
        CYG_LOOP_INVARIANT_CLASSC(node);
1067
 
1068
        if (node != this) {
1069
            CdlToplevelBody::remove_node(this, node->parent, node);
1070
            delete node;
1071
        }
1072
    }
1073
 
1074
    // Now there should be exactly one entry in the owned vector,
1075
    // the loadable itself. We already know that this is no longer
1076
    // part of the toplevel and it does not have a parent, so
1077
    // the only field we need to worry about is the owner.
1078
    CYG_ASSERTC(1 == owned.size());
1079
    CYG_ASSERTC(this == owned[0]);
1080
    this->owner = 0;
1081
 
1082
    // Strictly speaking the owned vector should be clear by now,
1083
    // but remove_node() does not actually bother to clear it.
1084
    owned.clear();
1085
 
1086
    // The loadable should now be empty. It remains to clean up
1087
    // a few odds and ends.
1088
    cdlloadablebody_cookie = CdlLoadableBody_Invalid;
1089
    CYG_ASSERTC(0 == owned.size());
1090
    delete interp;
1091
    interp      = 0;
1092
    repository  = "";
1093
    directory   = "";
1094
 
1095
    CYGDBG_MEMLEAK_DESTRUCTOR();
1096
 
1097
    CYG_REPORT_RETURN();
1098
}
1099
 
1100
//}}}
1101
//{{{  Simple information access                
1102
 
1103
// ----------------------------------------------------------------------------
1104
 
1105
const std::vector<CdlNode>&
1106
CdlLoadableBody::get_owned() const
1107
{
1108
    CYG_REPORT_FUNCNAME("CdlLoadable::get_owned");
1109
    CYG_REPORT_FUNCARG1XV(this);
1110
    CYG_PRECONDITION_THISC();
1111
 
1112
    CYG_REPORT_RETURN();
1113
    return owned;
1114
}
1115
 
1116
bool
1117
CdlLoadableBody::owns(CdlConstNode node) const
1118
{
1119
    CYG_REPORT_FUNCNAMETYPE("CdlLoadable::owns", "result %d");
1120
    CYG_REPORT_FUNCARG2XV(this, node);
1121
    CYG_PRECONDITION_THISC();
1122
    CYG_PRECONDITION_CLASSC(node);
1123
 
1124
    bool result = false;
1125
    std::vector<CdlNode>::const_iterator i = std::find(owned.begin(), owned.end(), node);
1126
    if (i != owned.end()) {
1127
        result = true;
1128
    }
1129
 
1130
    CYG_REPORT_RETVAL(result);
1131
    return result;
1132
}
1133
 
1134
CdlInterpreter
1135
CdlLoadableBody::get_interpreter() const
1136
{
1137
    CYG_REPORT_FUNCNAMETYPE("CdlLoadable::get_interpreter", "result %p");
1138
    CYG_REPORT_FUNCARG1XV(this);
1139
    CYG_PRECONDITION_THISC();
1140
 
1141
    CdlInterpreter result = interp;
1142
    CYG_REPORT_RETVAL(result);
1143
    return result;
1144
}
1145
 
1146
std::string
1147
CdlLoadableBody::get_repository() const
1148
{
1149
    CYG_REPORT_FUNCNAME("CdlLoadable::get_repository");
1150
    CYG_REPORT_FUNCARG1XV(this);
1151
    CYG_PRECONDITION_THISC();
1152
 
1153
    CYG_REPORT_RETURN();
1154
    return repository;
1155
}
1156
 
1157
std::string
1158
CdlLoadableBody::get_directory() const
1159
{
1160
    CYG_REPORT_FUNCNAME("CdlLoadable::get_directory");
1161
    CYG_REPORT_FUNCARG1XV(this);
1162
    CYG_PRECONDITION_THISC();
1163
 
1164
    CYG_REPORT_RETURN();
1165
    return directory;
1166
}
1167
 
1168
//}}}
1169
//{{{  Bind/unbind support                      
1170
 
1171
// ----------------------------------------------------------------------------
1172
// Binding a loadable. This involves checking every property of every node
1173
// in the loadable, which the properties do themselves by a suitable
1174
// update() virtual function. Next, there may be properties in the
1175
// existing configuration which could not previously be bound: there
1176
// will be structural conflicts for all of these. Once all the pointers
1177
// go to the right places it is possible to calculate the default values
1178
// and generally process the properties. Finally each node's active
1179
// state is checked - the default inactive state will be inappropriate
1180
// in many cases.
1181
//
1182
// FIXME: error recovery?
1183
 
1184
void
1185
CdlLoadableBody::bind(CdlTransaction transaction)
1186
{
1187
    CYG_REPORT_FUNCNAME("CdlLoadable::bind");
1188
    CYG_REPORT_FUNCARG2XV(this, transaction);
1189
    CYG_INVARIANT_THISC(CdlLoadableBody);
1190
    CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction);
1191
 
1192
    // The loadable must already be part of the hierarchy.
1193
    CdlToplevel toplevel = this->get_toplevel();
1194
    CYG_ASSERT_CLASSC(toplevel);
1195
 
1196
    // As a first step, bind all references in this loadable.
1197
    // This is achieved via a Loaded update.
1198
    const std::vector<CdlNode>& nodes = this->get_owned();
1199
    std::vector<CdlNode>::const_iterator node_i;
1200
    for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1201
        const std::vector<CdlProperty>& properties = (*node_i)->get_properties();
1202
        std::vector<CdlProperty>::const_iterator prop_i;
1203
        for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
1204
            (*prop_i)->update(transaction, *node_i, 0, CdlUpdate_Loaded);
1205
        }
1206
    }
1207
 
1208
    // Next, look for all structural conflicts which are unresolved
1209
    // references and which can now be resolved. It is necessary
1210
    // to check per-transaction structural conflicts, plus those
1211
    // in any parent transactions, plus the global ones.
1212
    std::list<CdlConflict>::const_iterator conf_i;
1213
    CdlTransaction current_transaction = transaction;
1214
    do {
1215
        CYG_ASSERT_CLASSC(current_transaction);
1216
        const std::list<CdlConflict>& new_structural_conflicts = current_transaction->get_new_structural_conflicts();
1217
 
1218
        for (conf_i = new_structural_conflicts.begin(); conf_i != new_structural_conflicts.end(); ) {
1219
 
1220
            CdlConflict conflict = *conf_i++;
1221
            CYG_LOOP_INVARIANT_CLASSC(conflict);
1222
 
1223
            CdlConflict_Unresolved unresolved_conflict = dynamic_cast<CdlConflict_Unresolved>(conflict);
1224
            if ((0 != unresolved_conflict) && !transaction->has_conflict_been_cleared(conflict)) {
1225
                CdlNode dest = toplevel->lookup(unresolved_conflict->get_target_name());
1226
                if (0 != dest) {
1227
                    CdlNode     node = unresolved_conflict->get_node();
1228
                    CdlProperty prop = unresolved_conflict->get_property();
1229
                    prop->update(transaction, node, dest, CdlUpdate_Created);
1230
                }
1231
            }
1232
        }
1233
        current_transaction = current_transaction->get_parent();
1234
    } while (0 != current_transaction);
1235
 
1236
    const std::list<CdlConflict>& structural_conflicts = toplevel->get_all_structural_conflicts();
1237
    for (conf_i = structural_conflicts.begin(); conf_i != structural_conflicts.end(); ) {
1238
 
1239
        CdlConflict conflict = *conf_i++;
1240
        CYG_LOOP_INVARIANT_CLASSC(conflict);
1241
 
1242
        CdlConflict_Unresolved this_conflict = dynamic_cast<CdlConflict_Unresolved>(conflict);
1243
        if ((0 != this_conflict) && !transaction->has_conflict_been_cleared(conflict)) {
1244
            CdlNode dest = toplevel->lookup(this_conflict->get_target_name());
1245
            if (0 != dest) {
1246
                CdlNode     node = this_conflict->get_node();
1247
                CdlProperty prop = this_conflict->get_property();
1248
                prop->update(transaction, node, dest, CdlUpdate_Created);
1249
            }
1250
        }
1251
    }
1252
 
1253
    // Conflict resolution has happened. Next it is time
1254
    // to evaluate default_value expressions and the like
1255
    // in the new loadable.
1256
    for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1257
        const std::vector<CdlProperty>& properties = (*node_i)->get_properties();
1258
        std::vector<CdlProperty>::const_iterator prop_i;
1259
        for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
1260
            (*prop_i)->update(transaction, *node_i, 0, CdlUpdate_Init);
1261
        }
1262
    }
1263
 
1264
    // Nodes start off inactive. Check each one whether or not it
1265
    // should be active.
1266
    // NOTE: possibly this should be done via a per-node init
1267
    // update instead.
1268
    for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1269
        bool current_state = transaction->is_active(*node_i);
1270
        bool new_state     = (*node_i)->test_active(transaction);
1271
        if (current_state != new_state) {
1272
            transaction->set_active(*node_i, new_state);
1273
        }
1274
    }
1275
 
1276
    CYG_REPORT_RETURN();
1277
}
1278
 
1279
// ----------------------------------------------------------------------------
1280
 
1281
void
1282
CdlLoadableBody::unbind(CdlTransaction transaction)
1283
{
1284
    CYG_REPORT_FUNCNAME("CdlLoadable::unbind");
1285
    CYG_REPORT_FUNCARG2XV(this, transaction);
1286
    CYG_PRECONDITION_THISC();
1287
    CYG_PRECONDITION_CLASSC(transaction);
1288
 
1289
    // First take care of all references to nodes in the loadable
1290
    // that is disappearing. This involves a Destroyed update.
1291
    const std::vector<CdlNode>& nodes = this->get_owned();
1292
    std::vector<CdlNode>::const_iterator node_i;
1293
    for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1294
        // The update will remove referrer objects, so it is best
1295
        // to work from the back.
1296
        std::vector<CdlReferrer>& referrers = (*node_i)->referrers;
1297
        std::vector<CdlReferrer>::reverse_iterator ref_i;
1298
        for (ref_i = referrers.rbegin(); ref_i != referrers.rend(); ref_i = referrers.rbegin()) {
1299
            ref_i->update(transaction, *node_i, CdlUpdate_Destroyed);
1300
            CYG_LOOP_INVARIANT(ref_i != referrers.rbegin(), "the vector should have shrunk");
1301
        }
1302
    }
1303
 
1304
    // Now repeat the loop, but unbind references from the unloaded objects
1305
    // to ones which are going to stay loaded. This will not cause
1306
    // the properties to disappear.
1307
    for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1308
        const std::vector<CdlProperty>& properties = (*node_i)->get_properties();
1309
        std::vector<CdlProperty>::const_iterator prop_i;
1310
        for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
1311
            (*prop_i)->update(transaction, *node_i, 0, CdlUpdate_Unloading);
1312
        }
1313
    }
1314
 
1315
    // Eliminate any conflicts that belong to this loadable.
1316
    // FIXME: why is his necessary? Should these conflicts not get
1317
    // eliminated by the above property iterations?
1318
    std::list<CdlConflict>::const_iterator conf_i;
1319
    const std::list<CdlConflict>& global_conflicts = toplevel->get_all_conflicts();
1320
    for (conf_i = global_conflicts.begin(); conf_i != global_conflicts.end(); ) {
1321
        CdlConflict conflict = *conf_i++;
1322
        CYG_LOOP_INVARIANT_CLASSC(conflict);
1323
        CdlNode     node     = conflict->get_node();
1324
        if ((node->get_owner() == this) && !transaction->has_conflict_been_cleared(conflict)) {
1325
            transaction->clear_conflict(conflict);
1326
        }
1327
    }
1328
    const std::list<CdlConflict>& global_structural_conflicts = toplevel->get_all_structural_conflicts();
1329
    for (conf_i = global_structural_conflicts.begin(); conf_i != global_structural_conflicts.end(); ) {
1330
        CdlConflict conflict = *conf_i++;
1331
        CYG_LOOP_INVARIANT_CLASSC(conflict);
1332
        CdlNode     node     = conflict->get_node();
1333
        if ((node->get_owner() == this) && !transaction->has_conflict_been_cleared(conflict)) {
1334
            transaction->clear_conflict(conflict);
1335
        }
1336
    }
1337
    const std::list<CdlConflict>& transaction_conflicts = transaction->get_new_conflicts();
1338
    for (conf_i = transaction_conflicts.begin(); conf_i != transaction_conflicts.end(); ) {
1339
        CdlConflict conflict = *conf_i++;
1340
        CYG_LOOP_INVARIANT_CLASSC(conflict);
1341
        CdlNode     node     = conflict->get_node();
1342
        if (node->get_owner() == this) {
1343
            transaction->clear_conflict(conflict);
1344
        }
1345
    }
1346
    const std::list<CdlConflict>& transaction_structural_conflicts = transaction->get_new_structural_conflicts();
1347
    for (conf_i = transaction_structural_conflicts.begin(); conf_i != transaction_structural_conflicts.end(); ) {
1348
        CdlConflict conflict = *conf_i++;
1349
        CYG_LOOP_INVARIANT_CLASSC(conflict);
1350
        CdlNode     node     = conflict->get_node();
1351
        if (node->get_owner() == this) {
1352
            transaction->clear_conflict(conflict);
1353
        }
1354
    }
1355
 
1356
    // FIXME: how about cleanup_orphans()
1357
 
1358
    CYG_REPORT_RETURN();
1359
}
1360
 
1361
// ----------------------------------------------------------------------------
1362
// These members are invoked for load and unload operations.
1363
//
1364
// Committing a load does not require anything, the loadable has
1365
// already been fully bound and all propagation has happened.
1366
 
1367
void
1368
CdlLoadableBody::transaction_commit_load(CdlTransaction transaction, CdlLoadable loadable)
1369
{
1370
    CYG_REPORT_FUNCNAME("CdlLoadable::transaction_commit_load");
1371
    CYG_REPORT_FUNCARG2XV(transaction, loadable);
1372
    CYG_PRECONDITION_CLASSC(transaction);
1373
    CYG_PRECONDITION_CLASSC(loadable);
1374
 
1375
    CYG_UNUSED_PARAM(CdlTransaction, transaction);
1376
    CYG_UNUSED_PARAM(CdlLoadable, loadable);
1377
 
1378
    CYG_REPORT_RETURN();
1379
}
1380
 
1381
// Cancelling a load is more difficult. The loadable has to be
1382
// unbound, removed from the toplevel, and deleted. If any of
1383
// this fails then we are in trouble, there is no easy way to
1384
// recover.
1385
void
1386
CdlLoadableBody::transaction_cancel_load(CdlTransaction transaction, CdlLoadable loadable)
1387
{
1388
    CYG_REPORT_FUNCNAME("CdlLoadable::transaction_cancel_load");
1389
    CYG_REPORT_FUNCARG2XV(transaction, loadable);
1390
    CYG_PRECONDITION_CLASSC(transaction);
1391
    CYG_PRECONDITION_CLASSC(loadable);
1392
 
1393
    CdlToplevel toplevel = transaction->get_toplevel();
1394
    CYG_PRECONDITION_CLASSC(toplevel);
1395
    CYG_ASSERTC(toplevel == loadable->get_toplevel());
1396
 
1397
    loadable->unbind(transaction);
1398
    toplevel->remove_loadable_from_toplevel(loadable);
1399
    delete loadable;
1400
 
1401
    CYG_REPORT_RETURN();
1402
}
1403
 
1404
// Committing an unload means that the loadable can now be deleted.
1405
// It should already be unbound and removed from the toplevel.
1406
void
1407
CdlLoadableBody::transaction_commit_unload(CdlTransaction transaction, CdlLoadable loadable)
1408
{
1409
    CYG_REPORT_FUNCNAME("CdlLoadable::transaction_commit_unload");
1410
    CYG_REPORT_FUNCARG2XV(transaction, loadable);
1411
    CYG_PRECONDITION_CLASSC(transaction);
1412
    CYG_PRECONDITION_CLASSC(loadable);
1413
 
1414
    CYG_UNUSED_PARAM(CdlTransaction, transaction);
1415
    delete loadable;
1416
 
1417
    CYG_REPORT_RETURN();
1418
}
1419
 
1420
// Cancelling an unload means that the loadable has to be re-added
1421
// to the hierarchy and then rebound. This implies that value
1422
// propagation needs to happen. However, since all value changes
1423
// since the very start of the transaction are held inside the
1424
// transaction and will be eliminated, the original state will
1425
// be restored anyway so the propagation is not actually required.
1426
void
1427
CdlLoadableBody::transaction_cancel_unload(CdlTransaction transaction, CdlLoadable loadable)
1428
{
1429
    CYG_REPORT_FUNCNAME("CdlLoadable::transaction_cancel_unload");
1430
    CYG_REPORT_FUNCARG2XV(transaction, loadable);
1431
    CYG_PRECONDITION_CLASSC(transaction);
1432
    CYG_PRECONDITION_CLASSC(loadable);
1433
 
1434
    CdlToplevel toplevel = transaction->get_toplevel();
1435
    CYG_PRECONDITION_CLASSC(toplevel);
1436
    toplevel->add_loadable_to_toplevel(loadable);
1437
    CYG_ASSERT_CLASSC(loadable);
1438
    loadable->bind(transaction);
1439
 
1440
    CYG_REPORT_RETURN();
1441
}
1442
 
1443
//}}}
1444
//{{{  File search facilities                   
1445
 
1446
// ----------------------------------------------------------------------------
1447
// File search facilities. Given a file name such as hello.cxx from a compile
1448
// property, or doc.html from a doc property, find the corresponding filename,
1449
// for example /usr/local/eCos/kernel/v1_3/doc/threads.html#create
1450
//
1451
// The second argument (default value "") indicates a preferred directory
1452
// where searching should begin. This would be src for a source file,
1453
// doc for a URL, etc.
1454
//
1455
// For some properties the data may refer to a URL rather than to a local
1456
// filename. This is controlled by the third argument, allow_urls.
1457
// If false then only local filenames will be considered. allow_urls
1458
// also controls whether or not anchor processing is performed.
1459
//
1460
// RFC1807: a URL consists of <scheme>:<rest>, where <scheme> can be
1461
// any sequence of lower-case letters, digits, plus, dot or hyphen. It
1462
// is recommended that upper-case letters should be accepted as well.
1463
//
1464
// RFC1807: an anchor is everything after the first # in the URL.
1465
 
1466
static char find_absolute_file_script[] = "                                     \n\
1467
set cdl_anchor \"\"                                                             \n\
1468
if {$::cdl_allow_urls} {                                                        \n\
1469
  if { [regexp -- {^[a-zA-Z+.-]*:.*$} $::cdl_target] } {                        \n\
1470
      return $::cdl_target                                                      \n\
1471
  }                                                                             \n\
1472
  set tmp \"\"                                                                  \n\
1473
  set non_anchor \"\"                                                           \n\
1474
  if { [regexp -- {^([^#])(#.*$)} $::cdl_target tmp non_anchor cdl_anchor] } {  \n\
1475
      set ::cdl_target $non_anchor                                              \n\
1476
  }                                                                             \n\
1477
}                                                                               \n\
1478
if {$::cdl_prefdir != \"\"} {                                                   \n\
1479
    set filename [file join $::cdl_topdir $::cdl_pkgdir $::cdl_prefdir $::cdl_target]         \n\
1480
    if {[file exists $filename]} {                                              \n\
1481
        return \"[set filename][set cdl_anchor]\"                               \n\
1482
    }                                                                           \n\
1483
}                                                                               \n\
1484
set filename [file join $::cdl_topdir $::cdl_pkgdir $::cdl_target]              \n\
1485
if {[file exists $filename]} {                                                  \n\
1486
    return \"[set filename][set cdl_anchor]\"                                   \n\
1487
}                                                                               \n\
1488
return \"\"                                                                     \n\
1489
";
1490
 
1491
std::string
1492
CdlLoadableBody::find_absolute_file(std::string filename, std::string dirname, bool allow_urls) const
1493
{
1494
    CYG_REPORT_FUNCNAME("CdlLoadable::find_absolute_file");
1495
    CYG_REPORT_FUNCARG1XV(this);
1496
    CYG_PRECONDITION_THISC();
1497
    CYG_PRECONDITIONC("" != filename);
1498
 
1499
    // These variable names should be kept in step with CdlBuildable::update_all_build_info()
1500
    interp->set_variable("::cdl_topdir",  repository);
1501
    interp->set_variable("::cdl_pkgdir",  directory);
1502
    interp->set_variable("::cdl_prefdir", dirname);
1503
    interp->set_variable("::cdl_target",  filename);
1504
    interp->set_variable("::cdl_allow_urls", allow_urls ? "1" : "0");
1505
 
1506
    std::string result;
1507
    int tmp = interp->eval(find_absolute_file_script, result);
1508
    if (tmp != TCL_OK) {
1509
        result = "";
1510
    }
1511
 
1512
    // Replace any backslashes in the repository with forward slashes.
1513
    // The latter are used throughout the library
1514
    // NOTE: this is not i18n-friendly.
1515
    for (unsigned int i = 0; i < result.size(); i++) {
1516
        if ('\\' == result[i]) {
1517
            result[i] = '/';
1518
        }
1519
    }
1520
 
1521
    CYG_REPORT_RETURN();
1522
    return result;
1523
}
1524
 
1525
static char find_relative_file_script[] = "                                     \n\
1526
if {$::cdl_prefdir != \"\"} {                                                   \n\
1527
    set filename [file join $::cdl_prefdir $::cdl_target]                       \n\
1528
    if {[file exists [file join $::cdl_topdir $::cdl_pkgdir $filename]]} {      \n\
1529
        return $filename                                                        \n\
1530
    }                                                                           \n\
1531
}                                                                               \n\
1532
set filename $::cdl_target                                                      \n\
1533
if {[file exists [file join $::cdl_topdir $::cdl_pkgdir $filename]]} {          \n\
1534
    return \"[set filename][set cdl_anchor]\"                                   \n\
1535
}                                                                               \n\
1536
return \"\"                                                                     \n\
1537
";
1538
 
1539
std::string
1540
CdlLoadableBody::find_relative_file(std::string filename, std::string dirname) const
1541
{
1542
    CYG_REPORT_FUNCNAME("CdlLoadable::find_relative_file");
1543
    CYG_REPORT_FUNCARG1XV(this);
1544
    CYG_PRECONDITION_THISC();
1545
    CYG_PRECONDITIONC("" != filename);
1546
 
1547
    // These variable names should be kept in step with CdlBuildable::update_all_build_info()
1548
    interp->set_variable("::cdl_topdir",  repository);
1549
    interp->set_variable("::cdl_pkgdir",  directory);
1550
    interp->set_variable("::cdl_prefdir", dirname);
1551
    interp->set_variable("::cdl_target",  filename);
1552
 
1553
    std::string result;
1554
    int tmp = interp->eval(find_relative_file_script, result);
1555
    if (tmp != TCL_OK) {
1556
        result = "";
1557
    }
1558
 
1559
    // Replace any backslashes in the repository with forward slashes.
1560
    // The latter are used throughout the library
1561
    // NOTE: this is not i18n-friendly.
1562
    for (unsigned int i = 0; i < result.size(); i++) {
1563
        if ('\\' == result[i]) {
1564
            result[i] = '/';
1565
        }
1566
    }
1567
 
1568
    CYG_REPORT_RETURN();
1569
    return result;
1570
}
1571
 
1572
static char has_subdirectory_script[] = "                               \n\
1573
set dirname [file join $::cdl_topdir $::cdl_pkgdir $::cdl_target]       \n\
1574
if {[file isdirectory $dirname] == 0} {                                 \n\
1575
    return 0                                                            \n\
1576
}                                                                       \n\
1577
return 1                                                                \n\
1578
";
1579
 
1580
bool
1581
CdlLoadableBody::has_subdirectory(std::string name) const
1582
{
1583
    CYG_REPORT_FUNCNAMETYPE("CdlLoadable::has_subdirectory", "result %d");
1584
    CYG_REPORT_FUNCARG1XV(this);
1585
    CYG_PRECONDITION_THISC();
1586
    CYG_PRECONDITIONC("" != name);
1587
 
1588
    bool        result = false;
1589
 
1590
    interp->set_variable("::cdl_topdir",  repository);
1591
    interp->set_variable("::cdl_pkgdir",  directory);
1592
    interp->set_variable("::cdl_target",  name);
1593
 
1594
    std::string tcl_result;
1595
    int tmp = interp->eval(has_subdirectory_script, tcl_result);
1596
    if ((TCL_OK == tmp) && ("1" == tcl_result)) {
1597
        result = true;
1598
    }
1599
 
1600
    CYG_REPORT_RETVAL(result);
1601
    return result;
1602
}
1603
 
1604
//}}}
1605
//{{{  Misc                                     
1606
 
1607
// ----------------------------------------------------------------------------
1608
 
1609
std::string
1610
CdlLoadableBody::get_class_name() const
1611
{
1612
    CYG_REPORT_FUNCNAME("CdlLoadable::get_class_name");
1613
    CYG_PRECONDITION_THISC();
1614
    CYG_REPORT_RETURN();
1615
    return "loadable";
1616
}
1617
 
1618
//}}}
1619
//{{{  check_this()                             
1620
 
1621
// ----------------------------------------------------------------------------
1622
bool
1623
CdlLoadableBody::check_this(cyg_assert_class_zeal zeal) const
1624
{
1625
    if (CdlLoadableBody_Magic != cdlloadablebody_cookie) {
1626
        return false;
1627
    }
1628
    CYGDBG_MEMLEAK_CHECKTHIS();
1629
 
1630
    if ((zeal == cyg_extreme) || (zeal == cyg_thorough)) {
1631
        std::vector<CdlNode>::const_iterator node_i;
1632
        for (node_i = owned.begin(); node_i != owned.end(); node_i++) {
1633
            if ((!(*node_i)->check_this(cyg_quick)) || ((*node_i)->get_owner() != this)) {
1634
                return false;
1635
            }
1636
        }
1637
    }
1638
    return CdlContainerBody::check_this(zeal);
1639
}
1640
 
1641
//}}}
1642
 
1643
//}}}
1644
//{{{  CdlToplevelBody                  
1645
 
1646
//{{{  Constructor                              
1647
 
1648
// ----------------------------------------------------------------------------
1649
// A toplevel is a container without a parent or owner. It keeps track
1650
// of all the names in the hierarchy, thus guaranteeing uniqueness and
1651
// providing a quick lookup facility.
1652
//
1653
// The member functions add_node() and remove_node() are the only
1654
// way of modifying the hierarchy. Adding a node with a zero parent
1655
// means adding it to a special container, Orphans.
1656
//
1657
// An interpreter object must be created explicitly, preventing
1658
// toplevel objects from being statically allocated (although
1659
// it is possible to play tricks with utility classes...)
1660
// There are too many possible error conditions when creating
1661
// an interpreter, so this should not happen until the world
1662
// is ready to deal with such errors.
1663
 
1664
CdlToplevelBody::CdlToplevelBody(CdlInterpreter interp_arg)
1665
    : CdlContainerBody()
1666
{
1667
    CYG_REPORT_FUNCNAME("CdlToplevel:: constructor");
1668
    CYG_REPORT_FUNCARG2XV(this, interp_arg);
1669
    CYG_PRECONDITION_CLASSC(interp_arg);
1670
 
1671
    // The STL containers will take care of themselves.
1672
    interp      = interp_arg;
1673
    transaction = 0;
1674
 
1675
    // A toplevel is always active, override the default setting for a node
1676
    active      = true;
1677
 
1678
    // Make the object valid before creating the orphans container.
1679
    orphans     = 0;
1680
    description = "";
1681
    cdltoplevelbody_cookie = CdlToplevelBody_Magic;
1682
 
1683
    // Arguably creating the orphans container should be left until
1684
    // it is actually needed. The advantage of creating it at the
1685
    // start is that it will appear in a fixed location in the contents,
1686
    // right at the start. Arguably the end would be better, but the
1687
    // end can move as loadables get added and removed.
1688
    //
1689
    // The GUI code will probably want to ignore any empty
1690
    // containers that are not valuables and not user-visible.
1691
    orphans = new CdlContainerBody("orphans");
1692
    add_node(0, this, orphans);
1693
 
1694
    // Let the interpreter know about its owning toplevel, as well as
1695
    // vice versa.
1696
    interp->set_toplevel(this);
1697
 
1698
    // The orphans container needs to be active as well.
1699
    orphans->active = true;
1700
 
1701
    CYGDBG_MEMLEAK_CONSTRUCTOR();
1702
 
1703
    CYG_POSTCONDITION_THISC();
1704
    CYG_REPORT_RETURN();
1705
}
1706
 
1707
//}}}
1708
//{{{  Destructor                               
1709
 
1710
// ----------------------------------------------------------------------------
1711
// The toplevel should have been mostly cleared already, by the
1712
// appropriate derived class. Without any loadables there should not
1713
// be any conflicts. It is necessary to clean up the orphans
1714
// container, since that was created by the CdlToplevel constructor.
1715
// If there are any other special nodes then these should have been
1716
// cleared by higher level code.
1717
 
1718
CdlToplevelBody::~CdlToplevelBody()
1719
{
1720
    CYG_REPORT_FUNCNAME("CdlToplevel:: destructor");
1721
    CYG_REPORT_FUNCARG1XV(this);
1722
    CYG_PRECONDITION_THISC();
1723
 
1724
    CYG_PRECONDITIONC(0 == loadables.size());
1725
    CYG_PRECONDITIONC(0 == conflicts.size());
1726
    CYG_PRECONDITIONC(0 == structural_conflicts.size());
1727
    CYG_PRECONDITIONC(0 == transaction);
1728
 
1729
    CYG_PRECONDITIONC(0 != orphans);
1730
    this->remove_node_from_toplevel(orphans);
1731
    CdlToplevelBody::remove_node(0, this, orphans);
1732
    delete orphans;
1733
    orphans = 0;
1734
 
1735
    CYG_PRECONDITIONC(0 == contents.size());
1736
 
1737
    cdltoplevelbody_cookie = CdlToplevelBody_Magic;
1738
    description = "";
1739
    limbo.clear();
1740
    unsupported_savefile_toplevel_strings.clear();
1741
    unsupported_savefile_commands.clear();
1742
    unsupported_savefile_subcommands.clear();
1743
 
1744
    // Since the interpreter is not created by the toplevel, it is
1745
    // not destroyed with the toplevel either. This leaves a potential
1746
    // big memory leak in application code.
1747
    interp = 0;
1748
 
1749
    CYGDBG_MEMLEAK_DESTRUCTOR();
1750
 
1751
    CYG_REPORT_RETURN();
1752
}
1753
 
1754
//}}}
1755
//{{{  Adding and removing nodes                
1756
 
1757
// ----------------------------------------------------------------------------
1758
// Adding and removing a node, and changing a parent.
1759
//
1760
// These routines allow the hierarchy to be manipulated. All nodes should
1761
// exist in a hierarchy below a toplevel, except for brief periods after
1762
// construction and during destruction.
1763
//
1764
// Most nodes will belong to a loadable. An owner of 0 is allowed, for
1765
// objects internal to the library such as the orphans container.
1766
// Everything else must have an owner, and specifically a loadable owns
1767
// itself.
1768
 
1769
void
1770
CdlToplevelBody::add_node(CdlLoadable owner, CdlContainer parent, CdlNode node)
1771
{
1772
    CYG_REPORT_FUNCNAME("CdlToplevel::add_node");
1773
    CYG_REPORT_FUNCARG4XV(this, owner, parent, node);
1774
    CYG_PRECONDITION_THISC();
1775
    CYG_PRECONDITION_ZERO_OR_CLASSC(owner);
1776
    CYG_PRECONDITION_CLASSC(parent);
1777
    CYG_PRECONDITION_CLASSC(node);
1778
 
1779
    // The node must not be in the hierarchy already.
1780
    CYG_ASSERTC(0 == node->toplevel);
1781
    CYG_ASSERTC(0 == node->owner);
1782
    CYG_ASSERTC(0 == node->parent);
1783
 
1784
    // The node's name should be unique. Checks for that should have happened
1785
    // in higher-level code.
1786
    CYG_ASSERTC(lookup_table.find(node->name) == lookup_table.end());
1787
    node->toplevel              = this;
1788
    lookup_table[node->name]    = node;
1789
 
1790
    node->owner                 = owner;
1791
    if (0 != owner) {
1792
        owner->owned.push_back(node);
1793
    }
1794
 
1795
    // If the node is in fact a loadable, it should own itself and
1796
    // in addition the toplevel class keeps track of its loadables
1797
    // in a separate vector.
1798
    if (0 != dynamic_cast<CdlLoadable>(node)) {
1799
        CYG_ASSERTC(owner == dynamic_cast<CdlLoadable>(node));
1800
        this->loadables.push_back(owner);
1801
    }
1802
 
1803
    if (0 == parent) {
1804
        parent = orphans;
1805
    }
1806
    node->parent = parent;
1807
    parent->contents.push_back(node);
1808
 
1809
    CYG_REPORT_RETURN();
1810
}
1811
 
1812
// Removing a node from a toplevel. This is the first step in deleting
1813
// a node: the step may be undone by a call to add_node_to_toplevel(),
1814
// or completed by a call to remove_node(). Removing a node from the
1815
// toplevel involves undoing the name->node mapping. In the case
1816
// of loadables, it also involves removing the node from the toplevel's
1817
// contents and loadables containers.
1818
 
1819
void
1820
CdlToplevelBody::remove_node_from_toplevel(CdlNode node)
1821
{
1822
    CYG_REPORT_FUNCNAME("CdlToplevel::remove_node_from_toplevel");
1823
    CYG_REPORT_FUNCARG2XV(this, node);
1824
    CYG_PRECONDITION_THISC();
1825
    CYG_PRECONDITION_CLASSC(node);
1826
    CYG_ASSERTC(this == node->toplevel);
1827
    CYG_ASSERTC(lookup_table[node->name] == node);
1828
 
1829
    node->toplevel = 0;
1830
    lookup_table.erase(node->name);
1831
 
1832
    CdlLoadable loadable = dynamic_cast<CdlLoadable>(node);
1833
    if (0 != loadable) {
1834
        CYG_ASSERTC(loadable == node->owner);
1835
        CYG_ASSERTC(this == node->parent);
1836
 
1837
        // Because remove_node_from_toplevel() is reversible, the
1838
        // loadable should reappear in its old position. Hence we
1839
        // had better keep track of that position. Note that
1840
        // this code assumed that the remove_node and add_node
1841
        // calls are exactly reversed.
1842
        int i;
1843
        for (i = 0; i < (int) this->contents.size(); i++) {
1844
            if (this->contents[i] == node) {
1845
                break;
1846
            }
1847
        }
1848
        CYG_ASSERTC(i < (int) this->contents.size());
1849
        node->remove_node_container_position = i;
1850
        this->contents.erase(this->contents.begin() + i);
1851
        node->parent = 0;
1852
 
1853
        // It is not clear that preserving the order of the loadables
1854
        // in the toplevel is useful, but it is harmless.
1855
        for (i = 0; i < (int) this->loadables.size(); i++) {
1856
            if (this->loadables[i] == loadable) {
1857
                break;
1858
            }
1859
        }
1860
        CYG_ASSERTC(i < (int) this->loadables.size());
1861
        loadable->remove_node_loadables_position = i;
1862
        this->loadables.erase(this->loadables.begin() + i);
1863
    }
1864
 
1865
    CYG_REPORT_RETURN();
1866
}
1867
 
1868
void
1869
CdlToplevelBody::remove_loadable_from_toplevel(CdlLoadable loadable)
1870
{
1871
    CYG_REPORT_FUNCNAME("CdlToplevel::remove_loadable_from_toplevel");
1872
    CYG_REPORT_FUNCARG2XV(this, loadable);
1873
    CYG_PRECONDITION_THISC();
1874
    CYG_PRECONDITION_CLASSC(loadable);
1875
 
1876
    const std::vector<CdlNode>& contents = loadable->get_owned();
1877
    for (int i = contents.size() - 1; i >= 0; i--) {
1878
        CdlToplevel toplevel = contents[i]->get_toplevel();
1879
        CYG_LOOP_INVARIANT_ZERO_OR_CLASSC(toplevel);
1880
        if (0 != toplevel) {
1881
            CYG_LOOP_INVARIANTC(this == toplevel);
1882
            this->remove_node_from_toplevel(contents[i]);
1883
        }
1884
    }
1885
 
1886
    CYG_REPORT_RETURN();
1887
}
1888
 
1889
// Re-adding a node to a toplevel. This needs to undo all of the changes
1890
// that may have been done by remove_node_from_toplevel() above.
1891
void
1892
CdlToplevelBody::add_node_to_toplevel(CdlNode node)
1893
{
1894
    CYG_REPORT_FUNCNAME("CdlToplevel::add_node_to_toplevel");
1895
    CYG_REPORT_FUNCARG2XV(this, node);
1896
    CYG_PRECONDITION_THISC();
1897
    CYG_PRECONDITION_CLASSC(node);
1898
    CYG_ASSERTC(0 == node->toplevel);
1899
    CYG_ASSERTC(0 != node->owner);
1900
 
1901
    CYG_ASSERTC(lookup_table.find(node->name) == lookup_table.end());
1902
    node->toplevel = this;
1903
    lookup_table[node->name] = node;
1904
 
1905
    CdlLoadable loadable = dynamic_cast<CdlLoadable>(node);
1906
    if (0 != loadable) {
1907
        CYG_ASSERTC(loadable == node->owner);
1908
        CYG_ASSERTC(0 == node->parent);
1909
        CYG_ASSERTC(-1 != node->remove_node_container_position);
1910
        CYG_ASSERTC(node->remove_node_container_position <= (int) this->contents.size());
1911
 
1912
        this->contents.insert(this->contents.begin() + node->remove_node_container_position, node);
1913
        node->remove_node_container_position = -1;
1914
        node->parent = this;
1915
 
1916
        CYG_ASSERTC(-1 != loadable->remove_node_loadables_position);
1917
        this->loadables.insert(this->loadables.begin() + loadable->remove_node_loadables_position, loadable);
1918
        loadable->remove_node_loadables_position = -1;
1919
    }
1920
 
1921
    CYG_REPORT_RETURN();
1922
}
1923
 
1924
void
1925
CdlToplevelBody::add_loadable_to_toplevel(CdlLoadable loadable)
1926
{
1927
    CYG_REPORT_FUNCNAME("CdlToplevel::add_loadable_to_toplevel");
1928
    CYG_REPORT_FUNCARG2XV(this, loadable);
1929
    CYG_PRECONDITION_THISC();
1930
    CYG_PRECONDITION_CLASSC(loadable);
1931
 
1932
    const std::vector<CdlNode>& contents = loadable->get_owned();
1933
    for (int i = 0; i < (int) contents.size(); i++) {
1934
        this->add_node_to_toplevel(contents[i]);
1935
    }
1936
 
1937
    CYG_REPORT_RETURN();
1938
}
1939
 
1940
// ----------------------------------------------------------------------------
1941
// The second stage remove operation. This cannot be undone, and
1942
// happens just before the node gets deleted and after a succesful
1943
// remove_node_from_toplevel().
1944
void
1945
CdlToplevelBody::remove_node(CdlLoadable owner, CdlContainer parent, CdlNode node)
1946
{
1947
    CYG_REPORT_FUNCNAME("CdlToplevel::remove_node");
1948
    CYG_REPORT_FUNCARG3XV(owner, parent, node);
1949
    CYG_PRECONDITION_CLASSC(node);
1950
    CYG_PRECONDITION_ZERO_OR_CLASSC(owner);
1951
    CYG_PRECONDITION_ZERO_OR_CLASSC(parent);
1952
    CYG_PRECONDITIONC(node->owner  == owner);
1953
    CYG_PRECONDITIONC(node->parent == parent);
1954
    CYG_PRECONDITIONC(0 == node->toplevel);
1955
 
1956
    if (0 != owner) {
1957
        node->owner = 0;
1958
        owner->owned.erase(std::find(owner->owned.begin(), owner->owned.end(), node));
1959
    }
1960
    if (0 != parent) {
1961
        node->parent = 0;
1962
        parent->contents.erase(std::find(parent->contents.begin(), parent->contents.end(), node));
1963
    }
1964
 
1965
    CYG_REPORT_RETURN();
1966
}
1967
 
1968
// Changing a parent does not affect the node's standing in terms of the
1969
// overall hierarchy or its owner, only the parent field.
1970
void
1971
CdlToplevelBody::change_parent(CdlLoadable owner, CdlContainer old_parent, CdlContainer new_parent, CdlNode node, int pos)
1972
{
1973
    CYG_REPORT_FUNCNAME("CdlToplevel::change_parent");
1974
    CYG_REPORT_FUNCARG6XV(this, owner, parent, new_parent, node, pos);
1975
    CYG_PRECONDITION_THISC();
1976
    CYG_PRECONDITION_CLASSC(old_parent);
1977
    CYG_PRECONDITION_ZERO_OR_CLASSC(new_parent);
1978
    CYG_PRECONDITION_CLASSC(node);
1979
    CYG_PRECONDITIONC(node->owner  == owner);
1980
    CYG_PRECONDITIONC(node->parent == old_parent);
1981
    CYG_PRECONDITIONC(this == node->toplevel);
1982
    CYG_PRECONDITIONC(lookup_table[node->name] == node);
1983
 
1984
    if (0 == new_parent) {
1985
        new_parent = orphans;
1986
    }
1987
    old_parent->contents.erase(std::find(old_parent->contents.begin(), old_parent->contents.end(), node));
1988
    node->parent = 0;
1989
 
1990
    if (-1 == pos) {
1991
        new_parent->contents.push_back(node);
1992
    } else {
1993
        CYG_ASSERTC(pos <= (int) new_parent->contents.size());
1994
        new_parent->contents.insert(new_parent->contents.begin() + pos, node);
1995
    }
1996
    node->parent = new_parent;
1997
 
1998
    CYG_REPORT_RETURN();
1999
}
2000
 
2001
// Cleaning up orphans.
2002
//
2003
// Right now this is only relevant for interfaces. Consider the case
2004
// where a loadable is being removed and that loadable defines an
2005
// interface. There may be other loadables which still have
2006
// "implements" properties affecting that interface, so instead of
2007
// deleting the cdl_interface object it is necessary to turn it into
2008
// an auto-generated orphan. At some stage there may no longer be
2009
// any references to an interface, in which case it can be removed
2010
// safely.
2011
//
2012
// In practice it is quite hard to do a clean-up purely on the basis
2013
// of implements properties, for example there may be an external
2014
// "requires" property as well which would need to have its references
2015
// cleaned up, then the expression needs to get re-evaluated, etc.
2016
// The transaction class does not currently provide a clean way
2017
// in which a single object can be destroyed. Instead the code below
2018
// checks for any references whose source is not the interface itself.
2019
 
2020
void
2021
CdlToplevelBody::cleanup_orphans()
2022
{
2023
    CYG_REPORT_FUNCNAME("CdlToplevel::cleanup_orphans");
2024
    CYG_REPORT_FUNCARG1XV(this);
2025
    CYG_PRECONDITION_THISC();
2026
 
2027
    // First figure out whether or not there are any interfaces along
2028
    // these lines.
2029
    std::vector<CdlInterface>   interfaces;
2030
    const std::vector<CdlNode>& contents = orphans->get_contents();
2031
    std::vector<CdlNode>::const_iterator node_i;
2032
 
2033
    for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
2034
        CdlInterface intface = dynamic_cast<CdlInterface>(*node_i);
2035
        if (0 == intface) {
2036
            continue;
2037
        }
2038
        const std::vector<CdlReferrer>& referrers = intface->get_referrers();
2039
        std::vector<CdlReferrer>::const_iterator ref_i;
2040
        for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) {
2041
            if (ref_i->get_source() != intface) {
2042
                break;
2043
            }
2044
        }
2045
        if (ref_i == referrers.end()) {
2046
            // None of the existing references involve an "implements" property, so
2047
            // this interface can be deleted.
2048
            interfaces.push_back(intface);
2049
        }
2050
    }
2051
 
2052
    if (0 != interfaces.size()) {
2053
        CYG_FAIL("Not yet implemented");
2054
    }
2055
}
2056
 
2057
//}}}
2058
//{{{  Basic information                        
2059
 
2060
// ----------------------------------------------------------------------------
2061
 
2062
const std::vector<CdlLoadable>&
2063
CdlToplevelBody::get_loadables() const
2064
{
2065
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_loadables", "result %p");
2066
    CYG_REPORT_FUNCARG1XV(this);
2067
    CYG_PRECONDITION_THISC();
2068
 
2069
    const std::vector<CdlLoadable>& result = loadables;
2070
    CYG_REPORT_RETVAL(&result);
2071
    return result;
2072
}
2073
 
2074
 
2075
CdlNode
2076
CdlToplevelBody::lookup(const std::string name) const
2077
{
2078
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::lookup", "result %p");
2079
    CYG_REPORT_FUNCARG1XV(this);
2080
    CYG_PRECONDITION_THISC();
2081
    CYG_PRECONDITIONC("" != name);
2082
 
2083
    CdlNode result = 0;
2084
    std::map<std::string,CdlNode>::const_iterator i = lookup_table.find(name);
2085
    if (i != lookup_table.end()) {
2086
        result = i->second;
2087
    }
2088
 
2089
    CYG_REPORT_RETVAL(result);
2090
    return result;
2091
}
2092
 
2093
CdlInterpreter
2094
CdlToplevelBody::get_interpreter() const
2095
{
2096
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_interpreter", "result %p");
2097
    CYG_REPORT_FUNCARG1XV(this);
2098
    CYG_PRECONDITION_THISC();
2099
 
2100
    CdlInterpreter result = interp;
2101
    CYG_REPORT_RETVAL(result);
2102
    return result;
2103
}
2104
 
2105
std::string
2106
CdlToplevelBody::get_description() const
2107
{
2108
    CYG_REPORT_FUNCNAME("CdlToplevel::get_description");
2109
    CYG_REPORT_FUNCARG1XV(this);
2110
    CYG_PRECONDITION_THISC();
2111
 
2112
    CYG_REPORT_RETURN();
2113
    return description;
2114
}
2115
 
2116
void
2117
CdlToplevelBody::set_description(std::string new_description)
2118
{
2119
    CYG_REPORT_FUNCNAME("CdlToplevel::set_description");
2120
    CYG_REPORT_FUNCARG1XV(this);
2121
    CYG_PRECONDITION_THISC();
2122
 
2123
    description = new_description;
2124
 
2125
    CYG_REPORT_RETURN();
2126
}
2127
 
2128
std::string
2129
CdlToplevelBody::get_class_name() const
2130
{
2131
    CYG_REPORT_FUNCNAME("CdlToplevel::get_class_name");
2132
    CYG_PRECONDITION_THISC();
2133
    CYG_REPORT_RETURN();
2134
    return "toplevel";
2135
}
2136
 
2137
CdlTransaction
2138
CdlToplevelBody::get_active_transaction() const
2139
{
2140
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_active_transaction", "result %p");
2141
    CYG_REPORT_FUNCARG1XV(this);
2142
    CYG_PRECONDITION_THISC();
2143
 
2144
    CdlTransaction result = transaction;
2145
    CYG_REPORT_RETVAL(result);
2146
    return result;
2147
}
2148
 
2149
//}}}
2150
//{{{  Conflict support                         
2151
 
2152
// ----------------------------------------------------------------------------
2153
const std::list<CdlConflict>&
2154
CdlToplevelBody::get_all_conflicts() const
2155
{
2156
    CYG_REPORT_FUNCNAME("CdlToplevel::get_all_conflicts");
2157
    CYG_REPORT_FUNCARG1XV(this);
2158
    CYG_PRECONDITION_THISC();
2159
 
2160
    const std::list<CdlConflict>& result = conflicts;
2161
 
2162
    CYG_REPORT_RETURN();
2163
    return result;
2164
}
2165
 
2166
const std::list<CdlConflict>&
2167
CdlToplevelBody::get_all_structural_conflicts() const
2168
{
2169
    CYG_REPORT_FUNCNAME("CdlToplevel::get_all_structural_conflicts");
2170
    CYG_REPORT_FUNCARG1XV(this);
2171
    CYG_PRECONDITION_THISC();
2172
 
2173
    const std::list<CdlConflict>& result = structural_conflicts;
2174
 
2175
    CYG_REPORT_RETURN();
2176
    return result;
2177
}
2178
 
2179
// ----------------------------------------------------------------------------
2180
// Resolve one or more conflicts. This involves creating a new transaction,
2181
// invoking the per-transaction resolve code, and then CdlTransaction::body()
2182
// takes care of everything else like propagation, further inference,
2183
// callbacks, committing, ...
2184
void
2185
CdlToplevelBody::resolve_conflicts(const std::vector<CdlConflict>& conflicts_arg)
2186
{
2187
    CYG_REPORT_FUNCNAME("CdlToplevel::resolve_conflicts");
2188
    CYG_REPORT_FUNCARG1XV(this);
2189
    CYG_PRECONDITION_THISC();
2190
 
2191
    CdlTransaction transact = CdlTransactionBody::make(this);
2192
 
2193
    std::vector<CdlConflict>::const_iterator conf_i;
2194
    for (conf_i = conflicts_arg.begin(); conf_i != conflicts_arg.end(); conf_i++) {
2195
        CYG_LOOP_INVARIANT_CLASSC(*conf_i);
2196
        CYG_LOOP_INVARIANTC(0 == (*conf_i)->get_transaction());
2197
 
2198
        if (((*conf_i)->resolution_implemented()) &&
2199
            !transact->has_conflict_been_cleared(*conf_i) &&
2200
            !(*conf_i)->has_known_solution() &&
2201
            !(*conf_i)->has_no_solution() ) {
2202
            transact->resolve(*conf_i);
2203
        }
2204
    }
2205
    transact->body();
2206
    delete transact;
2207
 
2208
    CYG_REPORT_RETURN();
2209
}
2210
 
2211
void
2212
CdlToplevelBody::resolve_all_conflicts()
2213
{
2214
    CYG_REPORT_FUNCNAME("CdlToplevel::resolve_all_conflicts");
2215
    CYG_REPORT_FUNCARG1XV(this);
2216
    CYG_PRECONDITION_THISC();
2217
 
2218
    CdlTransaction transact = CdlTransactionBody::make(this);
2219
    std::list<CdlConflict>::const_iterator conf_i;
2220
 
2221
    for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
2222
        CYG_LOOP_INVARIANT_CLASSC(*conf_i);
2223
        CYG_LOOP_INVARIANTC(0 == (*conf_i)->get_transaction());
2224
        if ((*conf_i)->resolution_implemented() &&
2225
            !transact->has_conflict_been_cleared(*conf_i) &&
2226
            !(*conf_i)->has_known_solution() &&
2227
            !(*conf_i)->has_no_solution() ) {
2228
            transact->resolve(*conf_i);
2229
        }
2230
    }
2231
    for (conf_i = structural_conflicts.begin(); conf_i != structural_conflicts.end(); conf_i++) {
2232
        CYG_LOOP_INVARIANT_CLASSC(*conf_i);
2233
        CYG_LOOP_INVARIANTC(0 == (*conf_i)->get_transaction());
2234
        if (((*conf_i)->resolution_implemented()) &&
2235
            !transact->has_conflict_been_cleared(*conf_i) &&
2236
            !(*conf_i)->has_known_solution() &&
2237
            !(*conf_i)->has_no_solution() ) {
2238
            transact->resolve(*conf_i);
2239
        }
2240
    }
2241
 
2242
    transact->body();
2243
    delete transact;
2244
 
2245
    CYG_REPORT_RETURN();
2246
}
2247
 
2248
//}}}
2249
//{{{  Limbo support                            
2250
 
2251
// ----------------------------------------------------------------------------
2252
// Limbo support. This is basically trivial, an STL map does all the
2253
// right things.
2254
void
2255
CdlToplevelBody::set_limbo_value(CdlValuable valuable)
2256
{
2257
    CYG_REPORT_FUNCNAME("CdlToplevel::set_limbo_value");
2258
    CYG_REPORT_FUNCARG2XV(this, valuable);
2259
    CYG_PRECONDITION_THISC();
2260
    CYG_PRECONDITION_CLASSC(valuable);
2261
 
2262
    limbo[valuable->get_name()] = valuable->get_whole_value();
2263
 
2264
    CYG_REPORT_RETURN();
2265
}
2266
 
2267
bool
2268
CdlToplevelBody::has_limbo_value(std::string name) const
2269
{
2270
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::has_limbo_value", "result %d");
2271
    CYG_REPORT_FUNCARG1XV(this);
2272
    CYG_PRECONDITION_THISC();
2273
    CYG_PRECONDITIONC("" != name);
2274
 
2275
    bool result = false;
2276
    if (limbo.find(name) != limbo.end()) {
2277
        result = true;
2278
    }
2279
 
2280
    CYG_REPORT_RETVAL(result);
2281
    return result;
2282
}
2283
 
2284
CdlValue
2285
CdlToplevelBody::get_limbo_value(std::string name) const
2286
{
2287
    CYG_REPORT_FUNCNAME("CdlToplevel::get_limbo_value");
2288
    CYG_REPORT_FUNCARG1XV(this);
2289
    CYG_PRECONDITION_THISC();
2290
    CYG_PRECONDITIONC("" != name);
2291
 
2292
    std::map<std::string,CdlValue>::const_iterator limbo_i = limbo.find(name);
2293
    CYG_ASSERTC(limbo_i != limbo.end());
2294
 
2295
    CYG_REPORT_RETURN();
2296
    return limbo_i->second;
2297
}
2298
 
2299
CdlValue
2300
CdlToplevelBody::get_and_remove_limbo_value(std::string name)
2301
{
2302
    CYG_REPORT_FUNCNAME("CdlToplevel::get_and_remove_limbo_value");
2303
    CYG_REPORT_FUNCARG1XV(this);
2304
    CYG_PRECONDITION_THISC();
2305
    CYG_PRECONDITIONC("" != name);
2306
 
2307
    std::map<std::string,CdlValue>::iterator limbo_i = limbo.find(name);
2308
    CYG_ASSERTC(limbo_i != limbo.end());
2309
 
2310
    CdlValue local_copy = limbo_i->second;
2311
    limbo.erase(limbo_i);
2312
 
2313
    CYG_REPORT_RETURN();
2314
    return local_copy;
2315
}
2316
 
2317
void
2318
CdlToplevelBody::clear_limbo()
2319
{
2320
    CYG_REPORT_FUNCNAME("CdlToplevel::clear_limbo");
2321
    CYG_REPORT_FUNCARG1XV(this);
2322
    CYG_PRECONDITION_THISC();
2323
 
2324
    limbo.clear();
2325
 
2326
    CYG_REPORT_RETURN();
2327
}
2328
 
2329
//}}}
2330
//{{{  Persistence support                      
2331
 
2332
//{{{  Description                      
2333
 
2334
// ----------------------------------------------------------------------------
2335
// Toplevels do not have any data specifically associated with them which
2336
// should go into savefiles (not quite true, there is a description field,
2337
// but that can be handled easily by the derived classes).
2338
//
2339
// However there is a need in the library for some generic savefile support:
2340
//
2341
// 1) it is an important goal that savefiles should be self-describing.
2342
//    This is handled by having a header section at the start of each
2343
//    savefile which describes what commands will appear in the savefile
2344
//    (note that savefiles are actually just Tcl scripts). In addition
2345
//    each savefile contains a version number so that individual commands
2346
//    can detect and adapt to older versions of the library.
2347
//
2348
// 2) savefiles should also be extensible, so for example a GUI tool should
2349
//    be able to add its own information. This can be toplevel information,
2350
//    i.e. a new command that gets executed at the savefile's toplevel,
2351
//    or it can be a subcommand extending an existing command such as
2352
//    cdl_option. Right now only one level of nesting is available, but
2353
//    this should suffice.
2354
//
2355
// 3) extensibility means that the application reading in a savefile may
2356
//    not support the same set of commands as the application that generated
2357
//    the savefile. Care is taken to avoid loss of data. However exact
2358
//    ordering is not guaranteed to be preserved, and neither is formatting.
2359
//
2360
// These needs are interrelated, and supported by the CdlToplevelBody
2361
// class. The functions of interest are:
2362
//
2363
// virtual void initialize_savefile_support()
2364
//    This should be called from higher-level code such as
2365
//    CdlConfiguration::initialize_savefile_support() at the start of
2366
//    any savefile-related operation.
2367
//
2368
//    The support operates on a per-application basis rather than a
2369
//    per-toplevel basis, in spite of being a virtual member function
2370
//    rather than a static. A virtual member function facilitates
2371
//    automatic initialization. This causes some problems if you need
2372
//    to load in toplevels with different application-specific
2373
//    extensions, but it makes life a lot simpler for the application.
2374
//
2375
// static bool savefile_support_initialized()
2376
//    Has there been a call to initialize_savefile_support() yet?
2377
//
2378
// virtual void add_savefile_command(std::string, CdlSaveCallback, CdlInterpreterCommand)
2379
//    Register a new savefile toplevel command. The string must be a
2380
//    valid command name. The callback function will be 0 for savedata
2381
//    supported directly by the library, non-zero for application-specific
2382
//    data, and is invoked during a save operation to allow application
2383
//    code to add extra data to the savefile. The command procedure must
2384
//    be provided and is registered with the Tcl interpreter.
2385
//
2386
// virtual void add_savefile_subcommand(std::string cmd, std::string subcommand, CdlSaveCallback, CdlInterpreterCommand)
2387
//    Typically savefile commands take the form <command> <name> <body>,
2388
//    where <body> contains a set of subcommands. This function is used
2389
//    to register a new subcommand.
2390
//
2391
// void save_command_details(CdlInterpreter, Tcl_Channel, int)
2392
//    This should be invoked early on when generating a savefile. Its
2393
//    purpose is to store information about the current set of savefile
2394
//    commands in the savefile, thus making savefiles self-describing.
2395
//    This command acts on a per-toplevel basis, since each toplevel
2396
//    may have been created via a load operation and hence may contain
2397
//    unrecognised commands.
2398
//
2399
// static void get_savefile_commands(std::vector<CdlInterpreterCommandEntry>&)
2400
//    Work out the set of commands that should be supported by the
2401
//    interpreter used to process a savefile. Note that this set gets
2402
//    updated magically by savefile_handle_command().
2403
//
2404
// static void get_savefile_subcommands(std::string, std::vector<CdlInterpreterCommandEntry>&)
2405
//    Ditto for subcommands.
2406
//
2407
// static int savefile_handle_command(CdlInterpreter, int, char**)
2408
//    This implements cdl_savefile_command, and makes sure that
2409
//    all of the commands that may be present in the savefile will
2410
//    be processed.
2411
//
2412
// static int savefile_handle_unsupported(CdlInterpreter, int, char**)
2413
//    This takes care of commands present in the savefile which are
2414
//    not supported by the current application.
2415
//
2416
// static int savefile_handle_unknown(CdlInterpreter, int, char**)
2417
//    This is an implementation of "unknown" suitable for savefiles.
2418
//    All commands that may get used in a savefile should be specified
2419
//    via cdl_savefile_command, so an unknown command is an error.
2420
//
2421
// cdl_int get_library_savefile_version()
2422
//    Savefiles contain a format version number. This function can be used
2423
//    to determine the current version used in the library.
2424
//
2425
// int savefile_handle_version(CdlInterpreter, int, char**)
2426
//    This is the implementation of the cdl_savefile_version command. It
2427
//    stores the version information in the interpreter, allowing it
2428
//    to be retrieved by other commands.
2429
//
2430
// cdl_int get_savefile_version(CdlInterpreter)
2431
//    This can be used to retrieve the version number that was present
2432
//    in the current savefile.
2433
//
2434
//    The version number should not be used for application-specific
2435
//    commands. It is possible for a savefile to be read in by a
2436
//    different program and then updated: the updated savefile will
2437
//    contain the unrecognised commands unchanged, but it will also
2438
//    have the version number corresponding to that program rather
2439
//    than to the original savefile.
2440
//
2441
// void save_conflicts(CdlInterpreter, Tcl_Channel, int)
2442
//    Output details of all the conflicts in the current configuration
2443
//
2444
// void save_separator(CdlInterpreter, Tcl_Channel, int)
2445
//    A utility to add a separator line to a savefile. This has to
2446
//    go somewhere....
2447
//
2448
// FIXME: add limbo support
2449
 
2450
//}}}
2451
//{{{  Statics and initialization       
2452
 
2453
// ----------------------------------------------------------------------------
2454
bool CdlToplevelBody::savefile_commands_initialized = false;
2455
std::vector<CdlSavefileCommand> CdlToplevelBody::savefile_commands;
2456
std::map<std::string,std::vector<CdlSavefileCommand> > CdlToplevelBody::savefile_subcommands;
2457
 
2458
void
2459
CdlToplevelBody::initialize_savefile_support()
2460
{
2461
    CYG_REPORT_FUNCNAME("CdlToplevel::initialize_savefile_support");
2462
    CYG_REPORT_FUNCARG1XV(this);
2463
    CYG_PRECONDITION_THISC();
2464
 
2465
    // This assignment avoids circular dependencies. It is not
2466
    // completely accurate but close enough - the full set of
2467
    // commands will be initialised shortly.
2468
    savefile_commands_initialized = true;
2469
 
2470
    // The commands cdl_savefile_version and cdl_command are a core
2471
    // part of the CDL savefile support.
2472
    add_savefile_command("cdl_savefile_version", 0, &savefile_handle_version);
2473
    add_savefile_command("cdl_savefile_command", 0, &savefile_handle_command);
2474
 
2475
    CYG_REPORT_RETURN();
2476
}
2477
 
2478
bool
2479
CdlToplevelBody::savefile_support_initialized()
2480
{
2481
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::check_savefile_support_initialized", "result %d");
2482
 
2483
    bool result = savefile_commands_initialized;
2484
    CYG_REPORT_RETVAL(result);
2485
    return result;
2486
}
2487
 
2488
//}}}
2489
//{{{  Command details                  
2490
 
2491
// ----------------------------------------------------------------------------
2492
// These routines are used to keep track of the savefile commands that
2493
// are understood by the current application. There may have been
2494
// additional per-toplevel commands when a savefile was read in, but
2495
// these are stored separately.
2496
//
2497
// Currently there is only support for toplevel savefile commands
2498
// and for one level of subcommands. Multiple levels can probably
2499
// be accommodated by using the equivalent of a directory separator
2500
// in the savefile_subcommands map key.
2501
 
2502
void
2503
CdlToplevelBody::add_savefile_command(std::string name, CdlSaveCallback save_callback, CdlInterpreterCommand load_command)
2504
{
2505
    CYG_REPORT_FUNCNAME("CdlToplevel::add_savefile_command");
2506
    CYG_REPORT_FUNCARG3XV(this, save_callback, load_command);
2507
    CYG_PRECONDITION_THISC();
2508
    CYG_PRECONDITIONC("" != name);
2509
 
2510
    if (!savefile_commands_initialized) {
2511
        this->initialize_savefile_support();
2512
    }
2513
 
2514
    std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2515
    for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2516
        if (cmd_i->name == name) {
2517
            if ((cmd_i->save_callback != save_callback) || (cmd_i->load_command != load_command)) {
2518
                CYG_FAIL("Internal error: attempt to define two toplevel savefile commands with the same name.");
2519
            }
2520
            break;
2521
        }
2522
    }
2523
    if (cmd_i == savefile_commands.end()) {
2524
        CdlSavefileCommand cmd;
2525
        cmd.name                = name;
2526
        cmd.save_callback       = save_callback;
2527
        cmd.load_command        = load_command;
2528
        savefile_commands.push_back(cmd);
2529
 
2530
        std::vector<CdlSavefileCommand> subcommands;
2531
        savefile_subcommands[name] = subcommands;
2532
    }
2533
 
2534
    CYG_REPORT_RETURN();
2535
}
2536
 
2537
// Add a new subcommand for a given command. The command should have been
2538
// defined already.
2539
void
2540
CdlToplevelBody::add_savefile_subcommand(std::string cmd, std::string subcommand, CdlSaveCallback save_callback,
2541
                                         CdlInterpreterCommand load_command)
2542
{
2543
    CYG_REPORT_FUNCNAME("CdlToplevel::add_savefile_subcommand");
2544
    CYG_REPORT_FUNCARG3XV(this, save_callback, load_command);
2545
    CYG_PRECONDITION_THISC();
2546
 
2547
    if (!savefile_commands_initialized) {
2548
        this->initialize_savefile_support();
2549
    }
2550
 
2551
    std::vector<CdlSavefileCommand>::iterator cmd_i;
2552
    for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2553
        if (cmd_i->name == cmd) {
2554
            break;
2555
        }
2556
    }
2557
    CYG_ASSERTC(cmd_i != savefile_commands.end());
2558
 
2559
    for (cmd_i = savefile_subcommands[cmd].begin(); cmd_i != savefile_subcommands[cmd].end(); cmd_i++) {
2560
        if (cmd_i->name == subcommand) {
2561
            if ((cmd_i->save_callback != save_callback) || (cmd_i->load_command != load_command)) {
2562
                CYG_FAIL("Internal error: attempt to define two subcommands with the same name.");
2563
            }
2564
        }
2565
    }
2566
    if (cmd_i == savefile_subcommands[cmd].end()) {
2567
        CdlSavefileCommand new_subcommand;
2568
        new_subcommand.name          = subcommand;
2569
        new_subcommand.save_callback = save_callback;
2570
        new_subcommand.load_command  = load_command;
2571
        savefile_subcommands[cmd].push_back(new_subcommand);
2572
    }
2573
 
2574
    CYG_REPORT_RETURN();
2575
}
2576
 
2577
// ----------------------------------------------------------------------------
2578
// This member function is invoked by e.g. CdlConfiguraton::save() to
2579
// take care of the generic savefile information, specifically the
2580
// savefile format version number and the various commands and subcommands
2581
// Note that it has to cope with per-toplevel commands from the original
2582
// savefile, as well as the global set.
2583
 
2584
void
2585
CdlToplevelBody::save_command_details(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
2586
{
2587
    CYG_REPORT_FUNCNAME("CdlToplevel::save_command_details");
2588
    CYG_REPORT_FUNCARG4XV(this, interp, chan, indentation);
2589
    CYG_PRECONDITION_THISC();
2590
    CYG_PRECONDITION_CLASSC(interp);
2591
    CYG_ASSERTC(0 == indentation);
2592
 
2593
    // The parent code should have provided the first couple of lines,
2594
    // identifying whether this is an eCos configuration or some other
2595
    // CDL-based entity.
2596
    //
2597
    // Immediately after these lines we want a nice big comment
2598
    // telling people that they can edit bits of this file, but
2599
    // that other bits are automatically generated and will
2600
    // be overwritten.
2601
 
2602
    if (!minimal) {
2603
        interp->write_data(chan,
2604
"# This section contains information about the savefile format.\n\
2605
# It should not be edited. Any modifications made to this section\n\
2606
# may make it impossible for the configuration tools to read\n\
2607
# the savefile.\n\
2608
\n");
2609
    }
2610
 
2611
    // Next output details of the savefile format version. This allows
2612
    // all other code to adapt to the version.
2613
    std::string savefile_data;
2614
    Cdl::integer_to_string(savefile_version, savefile_data);
2615
    savefile_data = "cdl_savefile_version " + savefile_data + ";\n";
2616
 
2617
    std::vector<CdlSavefileCommand>::const_iterator cmd_i, cmd_j;
2618
    std::vector<std::string>::const_iterator cmd_k, cmd_l;
2619
 
2620
    for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2621
        savefile_data += "cdl_savefile_command " + cmd_i->name + " ";
2622
 
2623
        if ((0 == savefile_subcommands[cmd_i->name].size()) &&
2624
            (0 == this->unsupported_savefile_subcommands[cmd_i->name].size())) {
2625
 
2626
            savefile_data += "{};\n";
2627
 
2628
        } else {
2629
 
2630
            savefile_data += "{";
2631
            for (cmd_j = savefile_subcommands[cmd_i->name].begin();
2632
                 cmd_j != savefile_subcommands[cmd_i->name].end();
2633
                 cmd_j++) {
2634
 
2635
                savefile_data += " " + cmd_j->name;
2636
            }
2637
            for (cmd_l = this->unsupported_savefile_subcommands[cmd_i->name].begin();
2638
                 cmd_l != this->unsupported_savefile_subcommands[cmd_i->name].end();
2639
                 cmd_l++) {
2640
 
2641
                savefile_data += " " + *cmd_l;
2642
            }
2643
            savefile_data += " };\n";
2644
        }
2645
    }
2646
    for (cmd_k = this->unsupported_savefile_commands.begin();
2647
         cmd_k != this->unsupported_savefile_commands.end();
2648
         cmd_k++) {
2649
        savefile_data += "cdl_savefile_command " + *cmd_k + " ";
2650
        if (0 == this->unsupported_savefile_subcommands[*cmd_k].size()) {
2651
 
2652
            savefile_data += "{};\n";
2653
 
2654
        } else {
2655
 
2656
            savefile_data += "{";
2657
            for (cmd_l = this->unsupported_savefile_subcommands[*cmd_k].begin();
2658
                 cmd_l != this->unsupported_savefile_subcommands[*cmd_k].end();
2659
                 cmd_l++) {
2660
 
2661
                savefile_data += " " + *cmd_l;
2662
            }
2663
            savefile_data += " };\n";
2664
        }
2665
    }
2666
    savefile_data += "\n";
2667
 
2668
    interp->write_data(chan, savefile_data);
2669
 
2670
    CYG_REPORT_RETURN();
2671
}
2672
 
2673
// ----------------------------------------------------------------------------
2674
// Get hold of the commands that should be added to the interpreter for
2675
// processing a savefile. Note that this will only deal with commands
2676
// supported by the library or the application, not any additional
2677
// unsupported commands specified in the savefile itself. The latter
2678
// will be taken care of magically by savefile_handle_command().
2679
 
2680
void
2681
CdlToplevelBody::get_savefile_commands(std::vector<CdlInterpreterCommandEntry>& cmds)
2682
{
2683
    CYG_REPORT_FUNCNAME("CdlToplevel::get_savefile_commands");
2684
    CYG_REPORT_FUNCARG1XV(this);
2685
    CYG_PRECONDITION_THISC();
2686
 
2687
    CdlInterpreterCommandEntry local_cmd;
2688
    std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2689
    for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2690
        // NOTE: this use of c_str() is somewhat dubious, but the string should not
2691
        // change so the c_str() array should remain ok as well.
2692
        local_cmd.name = cmd_i->name;
2693
        local_cmd.command = cmd_i->load_command;
2694
        cmds.push_back(local_cmd);
2695
    }
2696
 
2697
    // There is no point in iterating over this->unsupported_savefile_commands,
2698
    // that vector should be empty since we have not actually started
2699
    // processing the savefile yet.
2700
    CYG_ASSERTC(0 == this->unsupported_savefile_commands.size());
2701
 
2702
    // Add an implementation of the "unknown" command.
2703
    local_cmd.name = "unknown";
2704
    local_cmd.command = &CdlToplevelBody::savefile_handle_unknown;
2705
    cmds.push_back(local_cmd);
2706
 
2707
    CYG_REPORT_RETURN();
2708
}
2709
 
2710
// Having repeated calls of this for e.g. every cdl_option statement in
2711
// a savefile is expensive. Some sort of caching mechanism should be
2712
// used to avoid unnecessary overheads.
2713
void
2714
CdlToplevelBody::get_savefile_subcommands(std::string main_command, std::vector<CdlInterpreterCommandEntry>& cmds)
2715
{
2716
    CYG_REPORT_FUNCNAME("CdlToplevel::get_savefile_subcommands");
2717
    CYG_REPORT_FUNCARG1XV(this);
2718
    CYG_PRECONDITION_THISC();
2719
 
2720
    CdlInterpreterCommandEntry local_cmd;
2721
    std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2722
    for (cmd_i = savefile_subcommands[main_command].begin();
2723
         cmd_i != savefile_subcommands[main_command].end();
2724
         cmd_i++) {
2725
 
2726
        local_cmd.name = cmd_i->name.c_str();
2727
        local_cmd.command = cmd_i->load_command;
2728
        cmds.push_back(local_cmd);
2729
    }
2730
 
2731
    std::vector<std::string>::const_iterator cmd_j;
2732
    for (cmd_j = this->unsupported_savefile_subcommands[main_command].begin();
2733
         cmd_j != this->unsupported_savefile_subcommands[main_command].end();
2734
         cmd_j++) {
2735
 
2736
        local_cmd.name = cmd_j->c_str();
2737
        local_cmd.command = &savefile_handle_unsupported;
2738
        cmds.push_back(local_cmd);
2739
    }
2740
 
2741
    CYG_REPORT_RETURN();
2742
}
2743
 
2744
// ----------------------------------------------------------------------------
2745
// This implements cdl_savefile_command which should appear near the
2746
// start of savefiles. The command takes two arguments, a primary
2747
// command name and a set of subcommand names.
2748
int
2749
CdlToplevelBody::savefile_handle_command(CdlInterpreter interp, int argc, const char* argv[])
2750
{
2751
    CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_command");
2752
    CYG_REPORT_FUNCARG2XV(interp, argc);
2753
    CYG_PRECONDITION_CLASSC(interp);
2754
 
2755
    CdlToplevel toplevel = interp->get_toplevel();
2756
    CYG_ASSERT_CLASSC(toplevel);
2757
    CYG_ASSERTC(toplevel->savefile_commands_initialized);
2758
 
2759
    if (1 == argc) {
2760
        CdlParse::report_error(interp, "", "Expecting at least one argument to cdl_savefile_command");
2761
    } else if (2 == argc) {
2762
        CdlParse::report_warning(interp, "",
2763
                                 std::string("Missing third argument to `cdl_savefile_command ") + argv[1] +
2764
                                 "'\n.Expecting an additional list of subcommands.");
2765
    } else if (3 != argc) {
2766
        CdlParse::report_warning(interp, "", std::string("Unexpected additional arguments to `cdl_savefile_command ") +
2767
                                 argv[1] + " { " + argv[2] + " }");
2768
    }
2769
 
2770
    // Is the primary command one of the known ones?
2771
    bool known_command = false;
2772
    std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2773
    std::vector<std::string>::const_iterator cmd_j;
2774
 
2775
    if (1 != argc) {
2776
        // Make sure that the primary command is known.
2777
        for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2778
            if (cmd_i->name == argv[1]) {
2779
                known_command = true;
2780
                break;
2781
            }
2782
        }
2783
        if (!known_command) {
2784
            // Detect duplicate definitions, just in case.
2785
            for (cmd_j = toplevel->unsupported_savefile_commands.begin();
2786
                 cmd_j != toplevel->unsupported_savefile_commands.end();
2787
                 cmd_j++) {
2788
                if (*cmd_j == argv[1]) {
2789
                    break;
2790
                }
2791
            }
2792
            if (cmd_j == toplevel->unsupported_savefile_commands.end()) {
2793
                toplevel->unsupported_savefile_commands.push_back(argv[1]);
2794
            }
2795
        }
2796
    }
2797
 
2798
    // Now take care of all the subcommands.
2799
    if (2 != argc) {
2800
 
2801
        int          list_count = 0;
2802
        const char** list_entries = 0;
2803
 
2804
        try {
2805
            Tcl_Interp* tcl_interp = interp->get_tcl_interpreter();
2806
            if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, argv[2]), &list_count, CDL_TCL_CONST_CAST(char***, &list_entries))) {
2807
                CdlParse::report_error(interp, "", std::string("Invalid subcommand list for `cdl_command ") + argv[1] + "'.");
2808
            }
2809
 
2810
            for (int i = 0; i < list_count; i++) {
2811
                bool known_subcommand = false;
2812
                if (known_command) {
2813
                    for (cmd_i = savefile_subcommands[argv[1]].begin();
2814
                         cmd_i != savefile_subcommands[argv[1]].end();
2815
                         cmd_i++) {
2816
 
2817
                        if (cmd_i->name == list_entries[i]) {
2818
                            known_subcommand = true;
2819
                        }
2820
                    }
2821
                }
2822
                if (!known_subcommand) {
2823
                    for (cmd_j = toplevel->unsupported_savefile_subcommands[argv[1]].begin();
2824
                         cmd_j != toplevel->unsupported_savefile_subcommands[argv[1]].end();
2825
                         cmd_j++) {
2826
 
2827
                        if (*cmd_j == list_entries[i]) {
2828
                            known_subcommand = true;
2829
                            break;
2830
                        }
2831
                    }
2832
                }
2833
                if (!known_subcommand) {
2834
                    toplevel->unsupported_savefile_subcommands[argv[1]].push_back(list_entries[i]);
2835
                }
2836
 
2837
            }
2838
 
2839
            if (0 != list_entries) {
2840
                Tcl_Free((char *)list_entries);
2841
            }
2842
 
2843
        } catch(...) {
2844
            if (0 != list_entries) {
2845
                Tcl_Free((char *)list_entries);
2846
            }
2847
            throw;
2848
        }
2849
    }
2850
 
2851
    return TCL_OK;
2852
}
2853
 
2854
//}}}
2855
//{{{  handle_unsupported()             
2856
 
2857
// ----------------------------------------------------------------------------
2858
// This function is invoked when an unsupported command is detected in
2859
// a savefile. It turns the data back into a string which can go back
2860
// into the next savefile, thus avoiding loss of data.
2861
//
2862
// It is possible that the savefile contents involved variable or
2863
// command substitution. If so then this information will have been
2864
// lost, there is no simple way of retrieving this from the interpreter.
2865
// Care has to be taken when generating the new command string to
2866
// perform appropriate quoting.
2867
//
2868
// Ideally the original data could be extracted from the Tcl
2869
// interpreter somehow. Currently this data is not readily available,
2870
// and the resulting string may not match the original data exactly.
2871
int
2872
CdlToplevelBody::savefile_handle_unsupported(CdlInterpreter interp, int argc, const char* argv[])
2873
{
2874
    CYG_REPORT_FUNCNAME("CdlNode::savefile_handle_unsupported");
2875
    CYG_REPORT_FUNCARG2XV(interp, argc);
2876
    CYG_ASSERT_CLASSC(interp);
2877
 
2878
    CdlToplevel toplevel = interp->get_toplevel();
2879
    CYG_ASSERT_CLASSC(toplevel);
2880
    CdlNode node = interp->get_node();
2881
    CYG_ASSERT_ZERO_OR_CLASSC(node);
2882
 
2883
    std::string tmp = CdlInterpreterBody::quote(argv[0]);
2884
    for (int i = 1; i < argc; i++) {
2885
        tmp = tmp + " " + CdlInterpreterBody::quote(argv[i]);
2886
    }
2887
    // Unknown commands may occur at the toplevel or inside
2888
    // e.g. a cdl_option body. Toplevels are also nodes.
2889
    if (0 == node) {
2890
        toplevel->unsupported_savefile_toplevel_strings.push_back(tmp);
2891
    } else {
2892
        node->unsupported_savefile_strings.push_back(tmp);
2893
    }
2894
 
2895
    return TCL_OK;
2896
}
2897
 
2898
//}}}
2899
//{{{  save_unsupported()               
2900
 
2901
// ----------------------------------------------------------------------------
2902
// This code deals with any toplevel data present in the original save
2903
// file that was not recognised.
2904
void
2905
CdlToplevelBody::save_unsupported_commands(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
2906
{
2907
    CYG_REPORT_FUNCNAME("CdlToplevelBody::save_unsupported_commands");
2908
    CYG_REPORT_FUNCARG3XV(this, interp, chan);
2909
    CYG_PRECONDITION_THISC();
2910
    CYG_PRECONDITION_CLASSC(interp);
2911
    CYG_PRECONDITIONC(0 == indentation);
2912
 
2913
    std::string data = "\n";
2914
    std::vector<std::string>::const_iterator str_i;
2915
    for (str_i = unsupported_savefile_toplevel_strings.begin();
2916
         str_i != unsupported_savefile_toplevel_strings.end();
2917
         str_i++) {
2918
        data += *str_i + " ;\n";
2919
    }
2920
    interp->write_data(chan, data);
2921
 
2922
    CYG_UNUSED_PARAM(bool, minimal);
2923
    CYG_REPORT_RETURN();
2924
}
2925
 
2926
//}}}
2927
//{{{  handle_unknown()                 
2928
 
2929
// ----------------------------------------------------------------------------
2930
 
2931
int
2932
CdlToplevelBody::savefile_handle_unknown(CdlInterpreter interp, int argc, const char* argv[])
2933
{
2934
    CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_unknown");
2935
    CYG_REPORT_FUNCARG2XV(interp, argc);
2936
    CYG_PRECONDITION_CLASSC(interp);
2937
 
2938
    CdlParse::report_error(interp, "", std::string("Unknown command `") + argv[1] + "'.");
2939
 
2940
    CYG_UNUSED_PARAM(int, argc);
2941
    return TCL_OK;
2942
}
2943
 
2944
//}}}
2945
//{{{  versioning                       
2946
 
2947
// ----------------------------------------------------------------------------
2948
// Savefiles include a version number that can be used by library
2949
// commands to cope with old and incompatible savefiles. This
2950
// version number should be changed only very rarely, hopefully never.
2951
cdl_int CdlToplevelBody::savefile_version = 1;
2952
 
2953
cdl_int
2954
CdlToplevelBody::get_library_savefile_version()
2955
{
2956
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_library_savefile_version", "result %ld");
2957
 
2958
    cdl_int result = savefile_version;
2959
    CYG_REPORT_RETVAL((long) result);
2960
    return result;
2961
}
2962
 
2963
// This implements the cdl_savefile_version command. It stores the
2964
// version number with the interpreter, allowing it to be retrieved
2965
// by other commands.
2966
int
2967
CdlToplevelBody::savefile_handle_version(CdlInterpreter interp, int argc, const char* argv[])
2968
{
2969
    CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_version");
2970
    CYG_REPORT_FUNCARG2XV(interp, argc);
2971
    CYG_PRECONDITION_CLASSC(interp);
2972
 
2973
    if (1 == argc) {
2974
        CdlParse::report_warning(interp, "", "Expecting one argument to cdl_savefile_version");
2975
    } else {
2976
        if (2 != argc) {
2977
            CdlParse::report_warning(interp, "",
2978
                                     std::string("Unexpected number of arguments to cdl_savefile_version\n") +
2979
                                     "There should be exactly one argument, the savefile format version number.");
2980
        }
2981
        cdl_int tmp;
2982
        if (!Cdl::string_to_integer(argv[1], tmp)) {
2983
            CdlParse::report_error(interp, "",
2984
                                   std::string("Invalid version number `") + argv[1] + "' for cdl_savefile_version");
2985
        } else {
2986
            // Store the data in a Tcl variable. This is at least as convenient
2987
            // as assoc data.
2988
            interp->set_variable("cdl_savefile_version", argv[1]);
2989
        }
2990
    }
2991
 
2992
    return TCL_OK;
2993
}
2994
 
2995
cdl_int
2996
CdlToplevelBody::get_savefile_version(CdlInterpreter interp)
2997
{
2998
    CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_savefile_version", "result %ld");
2999
    CYG_REPORT_FUNCARG1XV(interp);
3000
    CYG_PRECONDITION_CLASSC(interp);
3001
 
3002
    cdl_int result = 0;
3003
    std::string version = interp->get_variable("cdl_savefile_version");
3004
    if ("" != version) {
3005
        if (!Cdl::string_to_integer(version, result)) {
3006
            CdlParse::report_error(interp, "", std::string("Invalid cdl_savefile_version number `") + version + "'");
3007
        }
3008
    }
3009
 
3010
    CYG_REPORT_RETVAL((long) result);
3011
    return result;
3012
}
3013
 
3014
//}}}
3015
//{{{  conflicts                        
3016
 
3017
// ----------------------------------------------------------------------------
3018
void
3019
CdlToplevelBody::save_conflicts(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
3020
{
3021
    CYG_REPORT_FUNCNAME("CdlToplevel::save_conflicts");
3022
    CYG_REPORT_FUNCARG4XV(this, interp, chan, indentation);
3023
    CYG_PRECONDITION_THISC();
3024
    CYG_PRECONDITION_CLASSC(interp);
3025
    CYG_PRECONDITIONC(0 == indentation);
3026
 
3027
    // For now only comments are generated here, so in a minimal save
3028
    // there is no need for any of this data
3029
    if (!minimal) {
3030
        std::string data = "";
3031
        if (0 == conflicts.size()) {
3032
            data += "# There are no conflicts.\n";
3033
        } else {
3034
            std::string tmp;
3035
            Cdl::integer_to_string((cdl_int) this->conflicts.size(), tmp);
3036
            data += "# There are " + tmp + " conflicts.\n";
3037
 
3038
            std::list<CdlConflict>::const_iterator conf_i;
3039
            for (conf_i = this->conflicts.begin(); conf_i != this->conflicts.end(); conf_i++) {
3040
                data += "#\n";
3041
 
3042
                CdlNode node = (*conf_i)->get_node();
3043
                CdlProperty prop = (*conf_i)->get_property();
3044
                std::string description = (*conf_i)->get_explanation();
3045
                data += "# " + node->get_class_name() + " "  + node->get_name() + "\n";
3046
                data += "#   Property " + prop->get_property_name() + "\n";
3047
                data += CdlInterpreterBody::multiline_comment(description, 0, 2) + "\n";
3048
            }
3049
            data += '\n';
3050
        }
3051
        data += '\n';
3052
 
3053
        interp->write_data(chan, data);
3054
    }
3055
 
3056
    CYG_REPORT_RETURN();
3057
}
3058
 
3059
//}}}
3060
//{{{  save_separator()                 
3061
 
3062
// ----------------------------------------------------------------------------
3063
void
3064
CdlToplevelBody::save_separator(CdlInterpreter interp, Tcl_Channel chan, std::string msg, bool minimal)
3065
{
3066
    CYG_REPORT_FUNCNAME("CdlToplevel::save_separator");
3067
    CYG_REPORT_FUNCARG1XV(interp);
3068
    CYG_PRECONDITION_CLASSC(interp);
3069
 
3070
    if (!minimal) {
3071
        std::string data = "# ---- " + msg + ' ';
3072
        if (72 > data.size()) {
3073
            data += std::string(72 - data.size(), '-');
3074
        }
3075
        data += '\n';
3076
        interp->write_data(chan, data);
3077
    }
3078
 
3079
    CYG_REPORT_RETURN();
3080
}
3081
 
3082
//}}}
3083
 
3084
//}}}
3085
//{{{  check_this()                             
3086
 
3087
// ----------------------------------------------------------------------------
3088
bool
3089
CdlToplevelBody::check_this(cyg_assert_class_zeal zeal) const
3090
{
3091
    if (CdlToplevelBody_Magic != cdltoplevelbody_cookie) {
3092
        return false;
3093
    }
3094
    CYGDBG_MEMLEAK_CHECKTHIS();
3095
 
3096
    if ((zeal == cyg_extreme) || (zeal == cyg_thorough)) {
3097
        if (!interp->check_this(cyg_quick)) {
3098
            return false;
3099
        }
3100
        if ((0 == orphans) || !orphans->check_this(cyg_quick)) {
3101
            return false;
3102
        }
3103
        if (orphans != *contents.begin()) {
3104
            return false;
3105
        }
3106
        if ((0 != transaction) && !transaction->check_this(cyg_quick)) {
3107
            return false;
3108
        }
3109
    }
3110
 
3111
    return CdlContainerBody::check_this(zeal);
3112
}
3113
 
3114
//}}}
3115
 
3116
//}}}
3117
//{{{  CdlUserVisiblebody               
3118
 
3119
//{{{  Basics                           
3120
 
3121
// ----------------------------------------------------------------------------
3122
// All user-visible object can have (and usually should have) three
3123
// properties: display (originally known as alias), description, and
3124
// doc. There is no additional data associated with a user-visible
3125
// object, everything is handled via the properties.
3126
 
3127
CdlUserVisibleBody::CdlUserVisibleBody()
3128
{
3129
    CYG_REPORT_FUNCNAME("CdlUserVisible:: default constructor");
3130
    CYG_REPORT_FUNCARG1XV(this);
3131
 
3132
    cdluservisiblebody_cookie  = CdlUserVisibleBody_Magic;
3133
    CYGDBG_MEMLEAK_CONSTRUCTOR();
3134
 
3135
    CYG_POSTCONDITION_THISC();
3136
    CYG_REPORT_RETURN();
3137
}
3138
 
3139
CdlUserVisibleBody::~CdlUserVisibleBody()
3140
{
3141
    CYG_REPORT_FUNCNAME("CdlUserVisible:: destructor");
3142
    CYG_REPORT_FUNCARG1XV(this);
3143
    CYG_PRECONDITION_THISC();
3144
 
3145
    cdluservisiblebody_cookie = CdlUserVisibleBody_Invalid;
3146
    CYGDBG_MEMLEAK_DESTRUCTOR();
3147
 
3148
    CYG_REPORT_RETURN();
3149
}
3150
 
3151
std::string
3152
CdlUserVisibleBody::get_class_name() const
3153
{
3154
    CYG_REPORT_FUNCNAME("CdlUserVisible::get_class_name");
3155
    CYG_PRECONDITION_THISC();
3156
    CYG_REPORT_RETURN();
3157
    return "uservisible";
3158
}
3159
 
3160
bool
3161
CdlUserVisibleBody::check_this(cyg_assert_class_zeal zeal) const
3162
{
3163
    if (CdlUserVisibleBody_Magic != cdluservisiblebody_cookie) {
3164
        return false;
3165
    }
3166
    CYGDBG_MEMLEAK_CHECKTHIS();
3167
    return CdlNodeBody::check_this(zeal);
3168
}
3169
 
3170
//}}}
3171
//{{{  Extracting information           
3172
 
3173
// ----------------------------------------------------------------------------
3174
// Extracting the information.
3175
 
3176
std::string
3177
CdlUserVisibleBody::get_display() const
3178
{
3179
    CYG_REPORT_FUNCNAME("CdlUserVisible::get_display");
3180
    CYG_REPORT_FUNCARG1XV(this);
3181
    CYG_PRECONDITION_THISC();
3182
 
3183
    std::string result = "";
3184
    CdlProperty property = get_property(CdlPropertyId_Display);
3185
    if (0 != property) {
3186
 
3187
        CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3188
        CYG_ASSERTC(0 != string_property);
3189
 
3190
        result = string_property->get_string();
3191
    }
3192
 
3193
    CYG_REPORT_RETURN();
3194
    return result;
3195
}
3196
 
3197
std::string
3198
CdlUserVisibleBody::get_description() const
3199
{
3200
    CYG_REPORT_FUNCNAME("CdlUserVisible::get_description");
3201
    CYG_REPORT_FUNCARG1XV(this);
3202
    CYG_PRECONDITION_THISC();
3203
 
3204
    std::string result = "";
3205
    CdlProperty property = get_property(CdlPropertyId_Description);
3206
    if (0 != property) {
3207
 
3208
        CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3209
        CYG_ASSERTC(0 != string_property);
3210
 
3211
        result = string_property->get_string();
3212
    }
3213
 
3214
    CYG_REPORT_RETURN();
3215
    return result;
3216
}
3217
 
3218
std::string
3219
CdlUserVisibleBody::get_doc() const
3220
{
3221
    CYG_REPORT_FUNCNAME("CdlUserVisible::get_doc");
3222
    CYG_REPORT_FUNCARG1XV(this);
3223
    CYG_PRECONDITION_THISC();
3224
 
3225
    std::string result = "";
3226
    CdlProperty property = get_property(CdlPropertyId_Doc);
3227
    if (0 != property) {
3228
 
3229
        CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3230
        CYG_ASSERTC(0 != string_property);
3231
 
3232
        result = string_property->get_string();
3233
    }
3234
 
3235
    CYG_REPORT_RETURN();
3236
    return result;
3237
}
3238
 
3239
std::string
3240
CdlUserVisibleBody::get_doc_url() const
3241
{
3242
    CYG_REPORT_FUNCNAME("CdlUserVisible::get_doc_url");
3243
    CYG_REPORT_FUNCARG1XV(this);
3244
    CYG_PRECONDITION_THISC();
3245
 
3246
    std::string result = "";
3247
    std::string doc_property = get_doc();
3248
    if ("" != doc_property) {
3249
        CdlLoadable owner = get_owner();
3250
        CYG_ASSERTC(0 != owner);
3251
        result = owner->find_absolute_file(doc_property, "doc", true);
3252
    }
3253
 
3254
    CYG_REPORT_RETURN();
3255
    return result;
3256
}
3257
 
3258
//}}}
3259
//{{{  Parsing                          
3260
 
3261
// ----------------------------------------------------------------------------
3262
// Parsing support. There are three property parsers to be added to
3263
// the current set. The checking code should make sure that at most
3264
// one of each property has been specified. In addition it is
3265
// necessary to recurse into the base class.
3266
 
3267
void
3268
CdlUserVisibleBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
3269
{
3270
    CYG_REPORT_FUNCNAME("CdlUserVisible::add_property_parsers");
3271
 
3272
    static CdlInterpreterCommandEntry commands[] =
3273
    {
3274
        CdlInterpreterCommandEntry("display",     &parse_display),
3275
        CdlInterpreterCommandEntry("description", &parse_description),
3276
        CdlInterpreterCommandEntry("doc",         &parse_doc),
3277
        CdlInterpreterCommandEntry("",            0)
3278
    };
3279
 
3280
    for (int i = 0; commands[i].command != 0; i++) {
3281
        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
3282
        for (j = parsers.begin(); j != parsers.end(); j++) {
3283
            if (commands[i].name == j->name) {
3284
                if (commands[i].command != j->command) {
3285
                    CYG_FAIL("Property names are being re-used");
3286
                }
3287
                break;
3288
            }
3289
        }
3290
        if (j == parsers.end()) {
3291
            parsers.push_back(commands[i]);
3292
        }
3293
    }
3294
    CdlNodeBody::add_property_parsers(parsers);
3295
 
3296
    CYG_REPORT_RETURN();
3297
}
3298
 
3299
void
3300
CdlUserVisibleBody::check_properties(CdlInterpreter interp)
3301
{
3302
    CYG_REPORT_FUNCNAME("CdlUserVisible::check_properties");
3303
    CYG_REPORT_FUNCARG2XV(this, interp);
3304
    CYG_PRECONDITION_THISC();
3305
    CYG_PRECONDITION_CLASSC(interp);
3306
 
3307
    if (count_properties(CdlPropertyId_Display) > 1) {
3308
        CdlParse::report_error(interp, "", "There should be at most one display property.");
3309
    }
3310
    if (count_properties(CdlPropertyId_Description) > 1) {
3311
        CdlParse::report_error(interp, "", "There should be at most one description property.");
3312
    }
3313
    if (count_properties(CdlPropertyId_Doc) > 1) {
3314
        CdlParse::report_error(interp, "", "There should be at most one doc property.");
3315
    }
3316
 
3317
    // FIXME: more validation of the doc property, in particular check that
3318
    // the resulting URL would be either remote or to an existing file.
3319
 
3320
    CdlNodeBody::check_properties(interp);
3321
 
3322
    CYG_REPORT_RETURN();
3323
}
3324
 
3325
// ----------------------------------------------------------------------------
3326
// Syntax: description <string>
3327
 
3328
int
3329
CdlUserVisibleBody::parse_description(CdlInterpreter interp, int argc, const char* argv[])
3330
{
3331
    CYG_REPORT_FUNCNAMETYPE("parse_description", "result %d");
3332
 
3333
    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Description, 0, 0);
3334
 
3335
    CYG_REPORT_RETVAL(result);
3336
    return result;
3337
}
3338
 
3339
 
3340
// ----------------------------------------------------------------------------
3341
// Syntax: display <short description>
3342
 
3343
int
3344
CdlUserVisibleBody::parse_display(CdlInterpreter interp, int argc, const char* argv[])
3345
{
3346
    CYG_REPORT_FUNCNAMETYPE("parse_display", "result %d");
3347
 
3348
    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Display, 0, 0);
3349
 
3350
    CYG_REPORT_RETVAL(result);
3351
    return result;
3352
}
3353
 
3354
// ----------------------------------------------------------------------------
3355
// Syntax: doc <url>
3356
 
3357
int
3358
CdlUserVisibleBody::parse_doc(CdlInterpreter interp, int argc, const char* argv[])
3359
{
3360
    CYG_REPORT_FUNCNAMETYPE("parse_doc", "result %d");
3361
 
3362
    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Doc, 0, 0);
3363
 
3364
    CYG_REPORT_RETVAL(result);
3365
    return result;
3366
}
3367
 
3368
//}}}
3369
//{{{  Persistence                      
3370
 
3371
// ----------------------------------------------------------------------------
3372
// There is no data in a user visible object that users will want to edit,
3373
// but the display string, the documentation, and the description are all
3374
// useful and should be present in the savefile as comments.
3375
//
3376
// The intention is that the UserVisible information appears immediately
3377
// above the option/component/whatever definition, e.g.:
3378
// # <display string.
3379
// # doc <URL>
3380
// # <description
3381
// # ...>
3382
// #
3383
 
3384
void
3385
CdlUserVisibleBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
3386
{
3387
    CYG_REPORT_FUNCNAME("CdlUserVisible::save");
3388
    CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
3389
    CYG_PRECONDITION_THISC();
3390
    CYG_PRECONDITION_CLASSC(interp);
3391
    CYG_ASSERTC(0 == indentation);
3392
 
3393
    if (!minimal) {
3394
        std::string data = "";
3395
        std::string display = get_display();
3396
        if ("" != display) {
3397
            data = std::string("# ") + display + "\n";
3398
        }
3399
        // Note that this uses get_doc(), not get_doc_url(). The latter
3400
        // would give an absolute pathname that is applicable to the
3401
        // current user, but it would change if a different user loaded
3402
        // and saved the file. This is a bad idea in terms of version
3403
        // control.
3404
        std::string doc = get_doc();
3405
        if ("" != doc) {
3406
            data += "# doc: " + doc + "\n";
3407
        }
3408
        std::string description = get_description();
3409
        if ("" != description) {
3410
            unsigned int i = 0;
3411
            while (i < description.size()) {
3412
                data += "# ";
3413
                while ((i < description.size()) && isspace(description[i])) {
3414
                    i++;
3415
                }
3416
                while ((i < description.size()) && ('\n' != description[i])) {
3417
                    data += description[i++];
3418
                }
3419
                data += '\n';
3420
            }
3421
        }
3422
        data += "#\n";
3423
 
3424
        interp->write_data(chan, data);
3425
    }
3426
 
3427
    CYG_REPORT_RETURN();
3428
}
3429
 
3430
//}}}
3431
 
3432
//}}}
3433
//{{{  CdlParentableBody                
3434
 
3435
// ----------------------------------------------------------------------------
3436
// A parentable object can have the parent property, i.e. it can be
3437
// positioned anywhere in the hierarchy. There is no data associated
3438
// with such an object.
3439
 
3440
CdlParentableBody::CdlParentableBody()
3441
{
3442
    CYG_REPORT_FUNCNAME("CdlParentable:: default constructor");
3443
    CYG_REPORT_FUNCARG1XV(this);
3444
 
3445
    change_parent_save_position = -1;
3446
    cdlparentablebody_cookie  = CdlParentableBody_Magic;
3447
    CYGDBG_MEMLEAK_CONSTRUCTOR();
3448
 
3449
    CYG_POSTCONDITION_THISC();
3450
    CYG_REPORT_RETURN();
3451
}
3452
 
3453
CdlParentableBody::~CdlParentableBody()
3454
{
3455
    CYG_REPORT_FUNCNAME("CdlParentable:: destructor");
3456
    CYG_REPORT_FUNCARG1XV(this);
3457
    CYG_PRECONDITION_THISC();
3458
 
3459
    cdlparentablebody_cookie = CdlParentableBody_Invalid;
3460
    CYGDBG_MEMLEAK_DESTRUCTOR();
3461
 
3462
    CYG_REPORT_RETURN();
3463
}
3464
 
3465
// ----------------------------------------------------------------------------
3466
 
3467
std::string
3468
CdlParentableBody::get_class_name() const
3469
{
3470
    CYG_REPORT_FUNCNAME("CdlParentable::get_class_name");
3471
    CYG_PRECONDITION_THISC();
3472
    CYG_REPORT_RETURN();
3473
    return "parentable";
3474
}
3475
 
3476
// ----------------------------------------------------------------------------
3477
 
3478
bool
3479
CdlParentableBody::check_this(cyg_assert_class_zeal zeal) const
3480
{
3481
    if (CdlParentableBody_Magic != cdlparentablebody_cookie) {
3482
        return false;
3483
    }
3484
    CYGDBG_MEMLEAK_CHECKTHIS();
3485
    return CdlNodeBody::check_this(zeal);
3486
}
3487
 
3488
// ----------------------------------------------------------------------------
3489
// Parsing support. There is just one property parser to be added.
3490
 
3491
void
3492
CdlParentableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
3493
{
3494
    CYG_REPORT_FUNCNAME("CdlParentable::add_property_parsers");
3495
 
3496
    static CdlInterpreterCommandEntry commands[] =
3497
    {
3498
        CdlInterpreterCommandEntry("parent", &CdlParentableBody::parse_parent),
3499
        CdlInterpreterCommandEntry("",       0)
3500
    };
3501
 
3502
    for (int i = 0; commands[i].command != 0; i++) {
3503
        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
3504
        for (j = parsers.begin(); j != parsers.end(); j++) {
3505
            if (commands[i].name == j->name) {
3506
                if (commands[i].command != j->command) {
3507
                    CYG_FAIL("Property names are being re-used");
3508
                }
3509
                break;
3510
            }
3511
        }
3512
        if (j == parsers.end()) {
3513
            parsers.push_back(commands[i]);
3514
        }
3515
    }
3516
    CdlNodeBody::add_property_parsers(parsers);
3517
 
3518
    CYG_REPORT_RETURN();
3519
}
3520
 
3521
void
3522
CdlParentableBody::check_properties(CdlInterpreter interp)
3523
{
3524
    CYG_REPORT_FUNCNAME("CdlParentable::check_properties");
3525
    CYG_REPORT_FUNCARG2XV(this, interp);
3526
    CYG_PRECONDITION_THISC();
3527
    CYG_PRECONDITION_CLASSC(interp);
3528
 
3529
    if (has_property(CdlPropertyId_Parent)) {
3530
        if (count_properties(CdlPropertyId_Parent) > 1) {
3531
            CdlParse::report_error(interp, "", "There should be at most one `parent' property.");
3532
        }
3533
        CdlProperty_Reference refprop = dynamic_cast<CdlProperty_Reference>(get_property(CdlPropertyId_Parent));
3534
        CYG_ASSERT_CLASSC(this);
3535
        if (get_name() == refprop->get_destination_name()) {
3536
            CdlParse::report_error(interp, "", std::string("Node ") + get_name() + " cannot be its own parent.");
3537
        }
3538
    }
3539
 
3540
    CdlNodeBody::check_properties(interp);
3541
 
3542
    CYG_REPORT_RETURN();
3543
}
3544
 
3545
// ----------------------------------------------------------------------------
3546
// Syntax:: parent <reference to container>
3547
 
3548
void
3549
CdlParentableBody::update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest, CdlUpdate change)
3550
{
3551
    CYG_REPORT_FUNCNAME("CdlParentable::update_handler");
3552
    CYG_PRECONDITION_CLASSC(source);
3553
    CYG_PRECONDITION_ZERO_OR_CLASSC(dest);
3554
 
3555
    // Value and activity updates are of no interest.
3556
    if ((CdlUpdate_ValueChange == change) || (CdlUpdate_ActiveChange == change)) {
3557
        CYG_REPORT_RETURN();
3558
        return;
3559
    }
3560
 
3561
    // Ditto for the second stage Init.
3562
    if (CdlUpdate_Init == change) {
3563
        CYG_REPORT_RETURN();
3564
        return;
3565
    }
3566
 
3567
    // If this object is being unloaded then we need to clean up the hierarchy.
3568
    // Ordinary nodes must be re-parented below the owning loadable. The
3569
    // loadable itself must be re-parented below the toplevel. A subsequent
3570
    // calls to remove_loadable_from_toplevel() will ensure that the loadable
3571
    // is now completely isolated from the remaining configuration, but can
3572
    // still be put back.
3573
    if (CdlUpdate_Unloading == change) {
3574
        CdlToplevel toplevel = source->get_toplevel();
3575
        CYG_ASSERT_CLASSC(toplevel);
3576
        CdlLoadable owner = source->get_owner();
3577
        CYG_ASSERT_CLASSC(owner);
3578
        CdlLoadable loadable = dynamic_cast<CdlLoadable>(source);
3579
        CYG_ASSERT_ZERO_OR_CLASSC(loadable);
3580
 
3581
        if (0 != loadable) {
3582
            toplevel->change_parent(owner, source->get_parent(), toplevel, source);
3583
        } else {
3584
            toplevel->change_parent(owner, source->get_parent(), owner, source);
3585
        }
3586
 
3587
        CYG_REPORT_RETURN();
3588
        return;
3589
    }
3590
 
3591
    // We should have:
3592
    // 1) change == Loaded, dest == (0 | valid)
3593
    // 2) change == Created, dest == valid
3594
    // 3) change == Destroyed, dest == valid (still)
3595
    CYG_ASSERTC((CdlUpdate_Loaded == change)    || (CdlUpdate_Created == change) || (CdlUpdate_Destroyed == change));
3596
    CYG_ASSERTC((CdlUpdate_Created != change)   || (0 != dest));
3597
    CYG_ASSERTC((CdlUpdate_Destroyed != change) || (0 != dest));
3598
 
3599
    if (CdlUpdate_Destroyed == change) {
3600
        dest = 0;
3601
    }
3602
 
3603
    // Now either dest is valid or it is not. If it is then we need to
3604
    // reparent below the destination. Otherwise if the specified
3605
    // parent is "" then we need to reparent below the root. Otherwise
3606
    // the node ends up in the orphans container. There are a few
3607
    // nasty special cases to consider like reparenting below
3608
    // something that is not a container.
3609
    if (0 == dest) {
3610
        CdlToplevel  toplevel = source->get_toplevel();
3611
 
3612
        CdlProperty_Reference refprop = dynamic_cast<CdlProperty_Reference>(prop);
3613
        if ("" == refprop->get_destination_name()) {
3614
            dest = toplevel;
3615
            // Now to find the correct insertion point. Nodes which should be
3616
            // reparented below the root should come first, ahead of any nodes
3617
            // which are not specifically reparented.
3618
            const std::vector<CdlNode>& contents = toplevel->get_contents();
3619
            unsigned int index;
3620
            for (index = 0; index < contents.size(); index++) {
3621
                if (!contents[index]->has_property(CdlPropertyId_Parent)) {
3622
                    break;
3623
                }
3624
            }
3625
            toplevel->change_parent(source->get_owner(), source->get_parent(), toplevel, source, index);
3626
 
3627
        } else {
3628
            // Orphan the node. It still has a parent, either as a
3629
            // consequence of the loading process or because of a previous
3630
            // binding operation.
3631
            toplevel->change_parent(source->get_owner(), source->get_parent(), 0, source);
3632
        }
3633
 
3634
        // The Unresolved conflict is handled by
3635
        // CdlProperty_Reference::update(). The "else" code below may
3636
        // have created some additional data conflicts.
3637
        transaction->clear_structural_conflicts(source, prop, &CdlConflict_DataBody::test);
3638
 
3639
        // Changing the parent may affect the "active" status.
3640
        bool old_state = transaction->is_active(source);
3641
        bool new_state = source->test_active(transaction);
3642
        if (old_state != new_state) {
3643
            transaction->set_active(source, new_state);
3644
        }
3645
 
3646
    } else {
3647
        // The node should no longer be an orphan - probably.
3648
 
3649
        // Check that the destination is actually a container. If it is,
3650
        // reparenting is possible.
3651
        CdlContainer dest_container = dynamic_cast<CdlContainer>(dest);
3652
        if (0 == dest_container) {
3653
 
3654
            // The reference might be resolved, but reparenting is still not possible.
3655
            // Leave the object orphaned as at present, and create a suitable conflict
3656
            // object.
3657
            std::string msg = source->get_class_name() + " " + source->get_name() + " cannot be reparented below " +
3658
                dest->get_class_name() + " " + dest->get_name() + "\n    The latter is not a container.";
3659
            CdlConflict_DataBody::make(transaction, source, prop, msg);
3660
 
3661
        } else {
3662
 
3663
            CdlContainer tmp = dynamic_cast<CdlContainer>(source);
3664
            if ((0 != tmp) && tmp->contains(dest_container, true)) {
3665
 
3666
                // Somebody trying to be clever and reparent an object
3667
                // below one of its existing children? Note that with
3668
                // sufficiently careful use of parent statements this
3669
                // might actually be legal, but for now treat it as
3670
                // too dangerous.
3671
                std::string msg = source->get_class_name() + " " + source->get_name() + " cannot be reparented below " +
3672
                    dest->get_class_name() + " " + dest->get_name() + "\n    This would introduce a cycle.";
3673
                CdlConflict_DataBody::make(transaction, source, prop, msg);
3674
 
3675
            } else {
3676
 
3677
                // It is possible to reparent the object to its correct location
3678
                CdlToplevel toplevel = source->get_toplevel();
3679
                CYG_ASSERTC(toplevel == dest->get_toplevel());
3680
                toplevel->change_parent(source->get_owner(), source->get_parent(), dest_container, source);
3681
 
3682
                bool old_state = transaction->is_active(source);
3683
                bool new_state = source->test_active(transaction);
3684
                if (old_state != new_state) {
3685
                    transaction->set_active(source, new_state);
3686
                }
3687
            }
3688
        }
3689
    }
3690
 
3691
    CYG_REPORT_RETURN();
3692
}
3693
 
3694
int
3695
CdlParentableBody::parse_parent(CdlInterpreter interp, int argc, const char* argv[])
3696
{
3697
    CYG_REPORT_FUNCNAMETYPE("parse_parent", "result %d");
3698
 
3699
    int result = CdlParse::parse_reference_property(interp, argc, argv, CdlPropertyId_Parent, 0, 0, true, &update_handler);
3700
 
3701
    CYG_REPORT_RETVAL(result);
3702
    return result;
3703
}
3704
 
3705
//}}}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.