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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [tools/] [src/] [libcdl/] [base.cxx] - Blame information for rev 377

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

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

powered by: WebSVN 2.1.0

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