]> git.kernelconcepts.de Git - karo-tx-redboot.git/blobdiff - tools/src/libcdl/value.cxx
Initial revision
[karo-tx-redboot.git] / tools / src / libcdl / value.cxx
diff --git a/tools/src/libcdl/value.cxx b/tools/src/libcdl/value.cxx
new file mode 100644 (file)
index 0000000..610fffc
--- /dev/null
@@ -0,0 +1,4114 @@
+//{{{  Banner                           
+
+//============================================================================
+//
+//      value.cxx
+//
+//      Implementation of value-related CDL classes.
+//
+//============================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// ----------------------------------------------------------------------------
+// Copyright (C) 2002 Bart Veer
+// Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
+//
+// This file is part of the eCos host tools.
+//
+// This program is free software; you can redistribute it and/or modify it 
+// under the terms of the GNU General Public License as published by the Free 
+// Software Foundation; either version 2 of the License, or (at your option) 
+// any later version.
+// 
+// This program is distributed in the hope that it will be useful, but WITHOUT 
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+// more details.
+// 
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// ----------------------------------------------------------------------------
+//                                                                          
+//####COPYRIGHTEND####
+//============================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   bartv
+// Contact(s):  bartv
+// Date:        1999/07/12
+// Version:     0.02
+//
+//####DESCRIPTIONEND####
+//============================================================================
+
+//}}}
+//{{{  #include's                       
+
+// ----------------------------------------------------------------------------
+#include "cdlconfig.h"
+
+// Get the infrastructure types, assertions, tracing and similar
+// facilities.
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+
+// <cdlcore.hxx> defines everything implemented in this module.
+// It implicitly supplies <string>, <vector> and <map> because
+// the class definitions rely on these headers.
+#include <cdlcore.hxx>
+
+//}}}
+
+//{{{  Statics                          
+
+// ----------------------------------------------------------------------------
+CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlValue);
+CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlListValue);
+CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlValuableBody);
+
+//}}}
+//{{{  CdlSimpleValue class             
+
+//{{{  Constructors                     
+
+// ----------------------------------------------------------------------------
+
+CdlSimpleValue::CdlSimpleValue()
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: default constructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = "0";
+    int_value           = 0;
+    double_value        = 0.0;
+    valid_flags         = int_valid | double_valid | string_valid;
+    format              = CdlValueFormat_Default;
+
+    CYG_REPORT_RETURN();
+}
+
+CdlSimpleValue::CdlSimpleValue(std::string val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: string constructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = val;
+    int_value           = 0;
+    double_value        = 0.0;
+    valid_flags         = string_valid;
+    format              = CdlValueFormat_Default;
+    
+    CYG_REPORT_RETURN();
+}
+
+CdlSimpleValue::CdlSimpleValue(cdl_int val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: int constructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = "0";
+    int_value           = val;
+    double_value        = 0.0;
+    valid_flags         = int_valid;
+    format              = CdlValueFormat_Default;
+    
+    CYG_REPORT_RETURN();
+}
+
+CdlSimpleValue::CdlSimpleValue(double val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: double constructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = "0";
+    int_value           = 0;
+    double_value        = val;
+    valid_flags         = double_valid;
+    format              = CdlValueFormat_Default;
+
+    CYG_REPORT_RETURN();
+}
+
+CdlSimpleValue::CdlSimpleValue(bool val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: bool constructor");
+    CYG_REPORT_FUNCARG2XV(this, val);
+
+    value               = (val) ? "1" : "0";
+    int_value           = (val) ? 1 : 0;
+    double_value        = 0.0;
+    valid_flags         = string_valid | int_valid;
+    format              = CdlValueFormat_Default;
+    
+    CYG_REPORT_RETURN();
+}
+
+CdlSimpleValue::CdlSimpleValue(const CdlSimpleValue& original)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: copy constructor");
+    CYG_REPORT_FUNCARG2XV(this, &original);
+
+    value               = original.value;
+    int_value           = original.int_value;
+    double_value        = original.double_value;
+    valid_flags         = original.valid_flags;
+    format              = original.format;
+    
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Destructor                       
+
+// ----------------------------------------------------------------------------
+
+CdlSimpleValue::~CdlSimpleValue()
+{
+    CYG_REPORT_FUNCNAME("CdlsimpleValue:: destructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = "";
+    int_value           = 0;
+    double_value        = 0.0;
+    valid_flags         = 0;
+    format              = CdlValueFormat_Default;
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Assignment operators             
+
+// ----------------------------------------------------------------------------
+
+CdlSimpleValue&
+CdlSimpleValue::operator=(const CdlSimpleValue& original)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: assignment operator");
+    CYG_REPORT_FUNCARG2XV(this, &original);
+
+    if (this != &original) {
+        value           = original.value;
+        int_value       = original.int_value;
+        double_value    = original.double_value;
+        valid_flags     = original.valid_flags;
+        format          = original.format;
+    }
+    
+    CYG_REPORT_RETURN();
+    return *this;
+}
+
+CdlSimpleValue&
+CdlSimpleValue::operator=(std::string val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: string assignment");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = val;
+    int_value           = 0;
+    double_value        = 0.0;
+    valid_flags         = string_valid;
+    format              = CdlValueFormat_Default;
+
+    CYG_REPORT_RETURN();
+    return *this;
+}
+
+CdlSimpleValue&
+CdlSimpleValue::operator=(cdl_int val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: integer assignment");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = "";
+    int_value           = val;
+    double_value        = 0.0;
+    valid_flags         = int_valid;
+    format              = CdlValueFormat_Default;
+    
+    CYG_REPORT_RETURN();
+    return *this;
+}
+
+CdlSimpleValue&
+CdlSimpleValue::operator=(double val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: double assignment");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = "";
+    int_value           = 0;
+    double_value        = val;
+    valid_flags         = double_valid;
+    format              = CdlValueFormat_Default;
+
+    CYG_REPORT_RETURN();
+    return *this;
+}
+
+// ----------------------------------------------------------------------------
+// Converting a boolean into a simple value. This is sufficiently common
+// to warrant its own member function, and in addition it avoids
+// ambiguity when assigning 0.
+
+CdlSimpleValue&
+CdlSimpleValue::operator=(bool val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: bool assignment");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = (val) ? "1" : "0";
+    int_value           = (val) ? 1 : 0;
+    double_value        = 0.0;
+    valid_flags         = string_valid | int_valid;
+    format              = CdlValueFormat_Default;
+
+    CYG_REPORT_RETURN();
+    return *this;
+}
+
+//}}}
+//{{{  CdlValuable -> CdlSimpleValue    
+
+// ----------------------------------------------------------------------------
+// This routine bridges the gap between the full data held in the CdlValuable
+// object and the basic information needed for expression evaluation.
+
+void
+CdlSimpleValue::eval_valuable(CdlEvalContext& context, CdlValuable valuable, CdlSimpleValue& result)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue:: valuable assignment");
+    CYG_REPORT_FUNCARG3XV(&context, valuable, &result);
+    CYG_PRECONDITION_CLASSC(valuable);
+
+    // If the valuable is not currently active then its value is
+    // always zero for the purposes of expression evaluation.
+    // FIXME: this check should be on a per-transaction basis.
+    if (((0 != context.transaction) && !context.transaction->is_active(valuable)) ||
+        ((0 == context.transaction) && !valuable->is_active())) {
+        
+        result.value           = "0";
+        result.int_value       = 0;
+        result.double_value    = 0.0;
+        result.valid_flags     = string_valid | int_valid;
+        result.format          = CdlValueFormat_Default;
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // Get hold of the underlying CdlValue object
+    const CdlValue& val = (0 != context.transaction) ?
+        context.transaction->get_whole_value(valuable) : valuable->get_whole_value();
+        
+    // Otherwise the value depends on the flavor.
+    switch(val.get_flavor()) {
+      case CdlValueFlavor_None :
+      {
+        // This could be treated as an error, but since valuables with flavor
+        // none are permanently enabled a constant "1" is a better result.
+        result.value           = "1";
+        result.int_value       = 1;
+        result.double_value    = 0.0;
+        result.valid_flags     = string_valid | int_valid;
+        result.format          = CdlValueFormat_Default;
+        break;
+      }
+      case CdlValueFlavor_Bool :
+      {
+        bool enabled           = val.is_enabled();
+        result.value           = (enabled) ? "1" : "0";
+        result.int_value       = (enabled) ?  1  :  0;
+        result.double_value    = 0.0;
+        result.valid_flags     = string_valid | int_valid;
+        result.format          = CdlValueFormat_Default;
+        break;
+      }
+      case CdlValueFlavor_BoolData :
+      {
+        if (!val.is_enabled()) {
+                    
+            result.value        = "0";
+            result.int_value    = 0;
+            result.double_value = 0.0;
+            result.valid_flags  = string_valid | int_valid;
+            result.format       = CdlValueFormat_Default;
+                    
+        } else {
+
+            // Just use a copy constructor, let the compiler optimise things.
+            result = val.get_simple_value();
+        }
+        break;
+      }
+      case CdlValueFlavor_Data :
+      {
+        // Just like BoolData, but with no need to check the enabled flag.
+        result = val.get_simple_value();
+        break;
+      }
+      default:
+      {
+        CYG_FAIL("Valuable object with an unknown flavor encountered.");
+      }
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Getting the value                
+
+// ----------------------------------------------------------------------------
+// Some of these calls involve conversion operators.
+
+std::string
+CdlSimpleValue::get_value() const
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::get_value");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    if (!(valid_flags & string_valid)) {
+        if (valid_flags & int_valid) {
+            Cdl::integer_to_string(int_value, value, format);
+        } else if (valid_flags & double_valid) {
+            Cdl::double_to_string(double_value, value, format);
+        } else {
+            CYG_FAIL("Attempt to use uninitialized SimpleValue");
+        }
+        valid_flags |= string_valid;
+    }
+
+    CYG_REPORT_RETURN();
+    return value;
+}
+
+bool
+CdlSimpleValue::has_integer_value() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlSimpleValue::has_integer_value", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    if (!(valid_flags & (int_valid | int_invalid))) {
+        if (valid_flags & double_valid) {
+            if (Cdl::double_to_integer(double_value, int_value)) {
+                valid_flags |= int_valid;
+            } else {
+                valid_flags |= int_invalid;
+            }
+        } else if (valid_flags & string_valid) {
+            if (Cdl::string_to_integer(value, int_value)) {
+                valid_flags |= int_valid;
+            } else {
+                valid_flags |= int_invalid;
+            }
+        } else {
+            CYG_FAIL("Attempt to use uninitialized SimpleValue");
+        }
+    }
+    
+    bool result = (valid_flags & int_valid);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+cdl_int
+CdlSimpleValue::get_integer_value() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlsimpleValue::get_integer_value", "result %ld");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    cdl_int result = 0;
+    if ((valid_flags & int_valid) || has_integer_value()) {
+        result = int_value;
+    }
+
+    CYG_REPORT_RETVAL((int) result);
+    return result;
+}
+
+bool
+CdlSimpleValue::has_double_value() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlSimpleValue::has_double_value", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    if (!(valid_flags & (double_valid | double_invalid))) {
+        if (valid_flags & int_valid) {
+            Cdl::integer_to_double(int_value, double_value);
+            valid_flags |= double_valid;
+        } else if (valid_flags & string_valid) {
+            if (Cdl::string_to_double(value, double_value)) {
+                valid_flags |= double_valid;
+            } else {
+                valid_flags |= double_invalid;
+            }
+        } else {
+            CYG_FAIL("Attempt to use uninitialized SimpleValue");
+        }
+    }
+    bool result = (valid_flags & double_valid);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+double
+CdlSimpleValue::get_double_value() const
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::get_double_value");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    double result = 0.0;
+    if ((valid_flags & double_valid) || has_double_value()) {
+        result = double_value;
+    }
+
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+bool
+CdlSimpleValue::get_bool_value() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlSimpleValue::get_bool_value", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    bool result = false;
+    if (valid_flags & int_valid) {
+        if (0 != int_value) {
+            result = true;
+        }
+    } else if (valid_flags & double_valid) {
+        // Leave it to the compiler to decide what is valid
+        result = double_value;
+    } else if (valid_flags & string_valid) {
+        // string_to_bool copes with "1", "true", and a few other cases.
+        // If the current value does not match any of these then
+        // true corresponds to a non-empty string.
+        if (!Cdl::string_to_bool(value, result)) {
+            if ("" == value) {
+                result = false;
+            } else {
+                result = true;
+            }
+        }
+    } else {
+        // No value defined, default to false.
+        result = false;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  Updating the value               
+
+// ----------------------------------------------------------------------------
+// Normally the assignment operators will be used for this instead.
+
+void
+CdlSimpleValue::set_value(std::string val, CdlValueFormat new_format)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::set_value (string)");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = val;
+    int_value           = 0;
+    double_value        = 0.0;
+    valid_flags         = string_valid;
+    format              = new_format;
+}
+
+
+void
+CdlSimpleValue::set_integer_value(cdl_int val, CdlValueFormat new_format)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::set_integer_value");
+    CYG_REPORT_FUNCARG2XV(this, (int) val);
+
+    value               = "";
+    int_value           = val;
+    double_value        = 0.0;
+    valid_flags         = int_valid;
+    format              = new_format;
+
+    CYG_REPORT_RETURN();
+}
+
+
+void
+CdlSimpleValue::set_double_value(double val, CdlValueFormat new_format)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::set_double_value");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    value               = "";
+    int_value           = 0;
+    double_value        = val;
+    valid_flags         = double_valid;
+    format              = new_format;
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Value format support             
+
+// ----------------------------------------------------------------------------
+
+CdlValueFormat
+CdlSimpleValue::get_value_format() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlSimpleValue::get_value_format", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    CdlValueFormat result = format;
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+void
+CdlSimpleValue::set_value_format(CdlValueFormat new_format)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::set_value_format");
+    CYG_REPORT_FUNCARG2XV(this, new_format);
+
+    format      = new_format;
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlSimpleValue::set_value_format(CdlSimpleValue& other_val)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::set_value_format (simple val)");
+    CYG_REPORT_FUNCARG2XV(this, &other_val);
+
+    format = other_val.format;
+
+    CYG_REPORT_RETURN();
+}
+
+// This gets used for binary operators, e.g. A + B
+// If A has a non-default format then that gets used.
+// Otherwise B's format gets used, which may or may not be default.
+//
+// e.g. 0x1000 + 4 -> 0x1004
+//      10 + 0x100 -> 0x10A
+//      10 + 32    -> 42
+
+void
+CdlSimpleValue::set_value_format(CdlSimpleValue& val1, CdlSimpleValue& val2)
+{
+    CYG_REPORT_FUNCNAME("CdlSimpleValue::set_value_format");
+    CYG_REPORT_FUNCARG3XV(this, &val1, &val2);
+
+    format = (CdlValueFormat_Default != val1.format) ? val1.format : val2.format;
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Comparison operators             
+
+// ----------------------------------------------------------------------------
+
+bool
+CdlSimpleValue::operator==(const CdlSimpleValue& other) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlSimpleValue:: operator==", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, &other);
+
+    bool result = false;
+    
+    if (has_integer_value()) {
+        if (other.has_integer_value()) {
+            cdl_int val1 = get_integer_value();
+            cdl_int val2 = other.get_integer_value();
+            result = (val1 == val2);
+        }
+    } else if (has_double_value()) {
+        if (other.has_double_value()) {
+            double val1 = get_double_value();
+            double val2 = other.get_double_value();
+            result = (val1 == val2);
+        }
+    } else {
+        std::string val1 = get_value();
+        std::string val2 = other.get_value();
+        result = (val1 == val2);
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlSimpleValue::operator!=(const CdlSimpleValue& other) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlSimpleValue:: operator!=", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, &other);
+
+    bool result = true;
+    if (has_integer_value()) {
+        if (other.has_integer_value()) {
+            cdl_int val1 = get_integer_value();
+            cdl_int val2 = other.get_integer_value();
+            result = (val1 != val2);
+        }
+    } else if (has_double_value()) {
+        if (other.has_double_value()) {
+            double val1 = get_double_value();
+            double val2 = other.get_double_value();
+            result = (val1 != val2);
+        }
+    } else {
+        std::string val1 = get_value();
+        std::string val2 = other.get_value();
+        result = (val1 != val2);
+    }
+
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+
+//}}}
+//{{{  CdlValue class                   
+
+// ----------------------------------------------------------------------------
+// This should really be a class static constant, but VC++ does not implement
+// that part of the language. A constant here avoids the need for lots of
+// occurrences of 4 throughout the value-related routines.
+
+static const int CdlValue_number_of_sources = 4;
+
+//{{{  Constructors                             
+
+// ----------------------------------------------------------------------------
+// The default flavor depends on the type of entity being created. For
+// example CDL options are boolean by default, but packages are booldata.
+// The intelligence to do the right thing lives in set_flavor().
+
+CdlValue::CdlValue(CdlValueFlavor flavor_arg)
+{
+    CYG_REPORT_FUNCNAME("CdlValue:: constructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    current_source = CdlValueSource_Default;
+    source_valid[CdlValueSource_Default]        = true;
+    source_valid[CdlValueSource_Inferred]       = false;
+    source_valid[CdlValueSource_Wizard]         = false;
+    source_valid[CdlValueSource_User]           = false;
+    enabled[CdlValueSource_Default]             = false;
+    enabled[CdlValueSource_Inferred]            = false;
+    enabled[CdlValueSource_Wizard]              = false;
+    enabled[CdlValueSource_User]                = false;
+
+    // The SimpleValues will initialize themselves.
+    
+    cdlvalue_cookie         = CdlValue_Magic;
+    CYGDBG_MEMLEAK_CONSTRUCTOR();
+
+    // This cannot happen until after the object is valid.
+    set_flavor(flavor_arg);
+        
+    CYG_POSTCONDITION_THISC();
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// Copy constructor. This is not really required, a default
+// member-wise copy would be fine and more efficient, but it would
+// lose tracing and assertion.
+
+CdlValue::CdlValue(const CdlValue& original)
+{
+    CYG_REPORT_FUNCNAME("CdlValue:: copy constructor");
+    CYG_REPORT_FUNCARG2XV(this, &original);
+    CYG_INVARIANT_CLASSOC(CdlValue, original);
+
+    flavor              = original.flavor;
+    current_source      = original.current_source;
+    for (int i = 0; i < CdlValue_number_of_sources; i++) {
+        source_valid[i] = original.source_valid[i];
+        enabled[i]      = original.enabled[i];
+        values[i]       = original.values[i];
+    }
+
+    cdlvalue_cookie = CdlValue_Magic;
+    CYGDBG_MEMLEAK_CONSTRUCTOR();
+    
+    CYG_POSTCONDITION_THISC();
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// Assignment operator. Again this is not required, the default would be
+// fine and more efficient, but tracing and assertions are good things.
+
+CdlValue& CdlValue::operator=(const CdlValue& original)
+{
+    CYG_REPORT_FUNCNAME("CdlValue:: assignment operator");
+    CYG_REPORT_FUNCARG2XV(this, &original);
+    CYG_INVARIANT_CLASSOC(CdlValue, original);
+
+    if (this != &original) {
+        flavor          = original.flavor;
+        current_source  = original.current_source;
+        for (int i = 0; i < CdlValue_number_of_sources; i++) {
+            source_valid[i]     = original.source_valid[i];
+            enabled[i]          = original.enabled[i];
+            values[i]           = original.values[i];
+        }
+    }
+
+    cdlvalue_cookie = CdlValue_Magic;
+    CYG_POSTCONDITION_THISC();
+    CYG_REPORT_RETURN();
+    return *this;
+}
+
+//}}}
+//{{{  Destructor                               
+
+// ----------------------------------------------------------------------------
+
+CdlValue::~CdlValue()
+{
+    CYG_REPORT_FUNCNAME("CdlValue:: destructor");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    cdlvalue_cookie     = CdlValue_Invalid;
+    flavor              = CdlValueFlavor_Invalid;
+    current_source      = CdlValueSource_Invalid;
+    for (int i = 0; i < CdlValue_number_of_sources; i++) {
+        source_valid[i]         = false;
+        enabled[i]              = false;
+        // The CdlSimpleValue array will take care of itself.
+    }
+    CYGDBG_MEMLEAK_DESTRUCTOR();
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  check_this()                             
+
+// ----------------------------------------------------------------------------
+bool
+CdlValue::check_this(cyg_assert_class_zeal zeal) const
+{
+    if (CdlValue_Magic != cdlvalue_cookie) {
+        return false;
+    }
+    CYGDBG_MEMLEAK_CHECKTHIS();
+
+    if (!source_valid[CdlValueSource_Default]) {
+        return false;
+    }
+
+    if ((CdlValueFlavor_None == flavor) || (CdlValueFlavor_Data == flavor)) {
+        for (int i = 0; i < CdlValue_number_of_sources; i++) {
+            if (!enabled[i]) {
+                return false;
+            }
+        }
+    }
+    for (int i = 0; i < CdlValue_number_of_sources; i++) {
+        if (source_valid[i]) {
+            if (!values[i].check_this(zeal)) {
+                return false;
+            }
+        }
+    }
+    
+    return true;
+}
+
+//}}}
+//{{{  Flavor manipulation                      
+
+// ----------------------------------------------------------------------------
+// Get hold of the current flavor.
+CdlValueFlavor
+CdlValue::get_flavor(void) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValue::get_flavor", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlValueFlavor result = flavor;
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// set_flavor() may be invoked once or twice for a given entity. The first
+// time is from inside the constructor with the default flavor for this
+// particular class of entity. It may then be called again if the
+// entity has a "flavor" property that overrides this. All old data
+// will be lost, so evaluating a default value etc. should be done after
+// the call to set_flavor(), and there should be no subsequent calls to
+// set_flavor().
+
+void
+CdlValue::set_flavor(CdlValueFlavor flavor_arg)
+{
+    CYG_REPORT_FUNCNAME("CdlValue:: set_flavor");
+    CYG_REPORT_FUNCARG2XV(this, flavor_arg);
+    
+    // No precondition here, set_flavor() is called from inside the constructor
+    CYG_PRECONDITIONC((CdlValueFlavor_None     == flavor_arg) || \
+                      (CdlValueFlavor_Bool     == flavor_arg) || \
+                      (CdlValueFlavor_BoolData == flavor_arg) || \
+                      (CdlValueFlavor_Data     == flavor_arg));
+
+    flavor = flavor_arg;
+    switch(flavor) {
+      case CdlValueFlavor_None :
+        {
+            // All value sources are enabled, but "default" remains
+            // the only valid one. All data parts are set to "1",
+            // although that should not really matter.
+            enabled[CdlValueSource_Default]     = true;
+            enabled[CdlValueSource_Inferred]    = true;
+            enabled[CdlValueSource_Wizard]      = true;
+            enabled[CdlValueSource_User]        = true;
+            
+            CdlSimpleValue simple_val((cdl_int) 1);
+            values[CdlValueSource_Default]      = simple_val;
+            values[CdlValueSource_Inferred]     = simple_val;
+            values[CdlValueSource_Wizard]       = simple_val;
+            values[CdlValueSource_User]         = simple_val;
+            break;
+        }
+          
+      case CdlValueFlavor_Bool :
+        {
+            // All value sources start out as disabled, but with a
+            // constant data part of 1. Users can only control the
+            // boolean part. This is consistent with header file
+            // generation: no #define is generated for disabled
+            // options, but if the option is enabled then the data
+            // part will be used for the value.
+            enabled[CdlValueSource_Default]     = false;
+            enabled[CdlValueSource_Inferred]    = false;
+            enabled[CdlValueSource_Wizard]      = false;
+            enabled[CdlValueSource_User]        = false;
+
+            // BLV - keep the data part at 0 for now. There is too
+            // much confusion in the code between value as a string
+            // representation, and value as the data part of the
+            // bool/data pair. This needs to be fixed, but it requires
+            // significant API changes.
+#if 0            
+            CdlSimpleValue simple_val(cdl_int(1));
+#else
+            CdlSimpleValue simple_val(cdl_int(0));
+#endif            
+            values[CdlValueSource_Default]      = simple_val;
+            values[CdlValueSource_Inferred]     = simple_val;
+            values[CdlValueSource_Wizard]       = simple_val;
+            values[CdlValueSource_User]         = simple_val;
+            break;
+        }
+          
+      case CdlValueFlavor_BoolData :
+        {
+            // All value sources start out as disabled, just like
+            // booleans. Nothing is known about the data part.
+            enabled[CdlValueSource_Default]       = false;
+            enabled[CdlValueSource_Inferred]      = false;
+            enabled[CdlValueSource_Wizard]        = false;
+            enabled[CdlValueSource_User]          = false;
+            break;
+        }
+          
+      case CdlValueFlavor_Data :
+        {
+            // All value sources start out as enabled, and cannot be
+            // changed. Nothing is known about the data part.
+            enabled[CdlValueSource_Default]       = true;
+            enabled[CdlValueSource_Inferred]      = true;
+            enabled[CdlValueSource_Wizard]        = true;
+            enabled[CdlValueSource_User]          = true;
+            break;
+        }
+
+      default :
+        break;
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Source manipulation                      
+
+// ----------------------------------------------------------------------------
+
+void
+CdlValue::set_source(CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValue::set_source");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_INVARIANT_THISC(CdlValue);
+    CYG_PRECONDITIONC((0 <= source) && (source <= CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    current_source = source;
+
+    CYG_REPORT_RETURN();
+}
+
+CdlValueSource
+CdlValue::get_source(void) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValue::get_source", "source %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlValueSource result = current_source;
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValue::has_source(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValue::has_source", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+
+    bool result = source_valid[source];
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// Invalidate a specific source. If that source happens to be the current one,
+// switch to the highest-priority valid source.
+
+void
+CdlValue::invalidate_source(CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValue::invalidate_source");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITIONC(CdlValueSource_Default != source);
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+
+    if (CdlValueSource_Default != source) {
+        source_valid[source]        = false;
+        if (current_source == source) {
+            if (source_valid[CdlValueSource_User]) {
+                current_source = CdlValueSource_User;
+            } else if (source_valid[CdlValueSource_Wizard]) {
+                current_source = CdlValueSource_Wizard;
+            } else if (source_valid[CdlValueSource_Inferred]) {
+                current_source = CdlValueSource_Inferred;
+            } else {
+                current_source = CdlValueSource_Default;
+            }
+        }
+    }
+    
+    CYG_POSTCONDITIONC(source_valid[current_source]);
+}
+
+//}}}
+//{{{  Retrieving the data                      
+
+// ----------------------------------------------------------------------------
+// Check the enabled flag for the appropriate source. The specified source
+// is normally provided by a default argument CdlValueSource_Current, which
+// 99.9...% of the time is what we are after.
+//
+// Note that this member can be used even for entities of flavor none
+// and data, and the result will be true. However it is not legal to
+// disable such entities.
+
+bool
+CdlValue::is_enabled(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValue::is_enabled", "enabled %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    if (CdlValueSource_Current == source) {
+        source = current_source;
+    }
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    bool result = enabled[source];
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// Access to the value field.
+
+std::string
+CdlValue::get_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValue::get_value");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    if (CdlValueSource_Current == source) {
+        source = current_source;
+    }
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    std::string result = values[source].get_value();
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+bool
+CdlValue::has_integer_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValue::has_integer_value", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_INVARIANT_THISC(CdlValue);
+
+    if (CdlValueSource_Current == source) {
+        source = current_source;
+    }
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    bool result = values[source].has_integer_value();
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValue::has_double_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValue::has_value", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_INVARIANT_THISC(CdlValue);
+    
+    if (CdlValueSource_Current == source) {
+        source = current_source;
+    }
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    bool result = values[source].has_double_value();
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+cdl_int
+CdlValue::get_integer_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValue::get_integer_value", "value %ld");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    if (CdlValueSource_Current == source) {
+        source = current_source;
+    }
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    cdl_int result = values[source].get_integer_value();
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+double
+CdlValue::get_double_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValue::get_double_value");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    if (CdlValueSource_Current == source) {
+        source = current_source;
+    }
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    double result = values[source].get_double_value();
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+CdlSimpleValue
+CdlValue::get_simple_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValue::get_simple_value");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    if (CdlValueSource_Current == source) {
+        source = current_source;
+    }
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    CYG_PRECONDITIONC(source_valid[source]);
+
+    CYG_REPORT_RETURN();
+    return values[source];
+}
+
+//}}}
+//{{{  Value modification                       
+
+// ----------------------------------------------------------------------------
+
+void
+CdlValue::set_enabled(bool val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValue::set_enabled");
+    CYG_REPORT_FUNCARG3XV(this, val, source);
+    CYG_INVARIANT_THISC(CdlValue);
+    CYG_PRECONDITIONC((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor));
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+
+    enabled[source] = val;
+    source_valid[source] = true;
+    if (source > current_source) {
+        current_source = source;
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValue::set_value(CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValue::set_value");
+    CYG_REPORT_FUNCARG3XV(this, &val, source);
+    CYG_INVARIANT_THISC(CdlValue);
+    CYG_PRECONDITIONC((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor));
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+    
+    values[source] = val;
+    source_valid[source] = true;
+    if (source > current_source) {
+        current_source = source;
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValue::set_enabled_and_value(bool enabled_arg, CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValue::set_enabled_and_value");
+    CYG_REPORT_FUNCARG4XV(this, enabled_arg, &val, source);
+    CYG_INVARIANT_THISC(CdlValue);
+    CYG_PRECONDITIONC(CdlValueFlavor_BoolData == flavor);
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+
+    enabled[source]      = enabled_arg;
+    values[source]       = val;
+    source_valid[source] = true;
+    if (source > current_source) {
+        current_source = source;
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// Given a SimpleValue, this member function does the right thing
+// for the flavor.
+
+void
+CdlValue::set(CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValue::set");
+    CYG_REPORT_FUNCARG3XV(this, &val, source);
+    CYG_INVARIANT_THISC(CdlValue);
+    CYG_ASSERTC((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor));
+    CYG_PRECONDITIONC((0 <= source) && (source < CdlValue_number_of_sources));
+
+    switch(flavor) {
+      case CdlValueFlavor_Bool:
+        enabled[source] = val.get_bool_value();
+        break;
+
+      case CdlValueFlavor_BoolData:
+        if (!val.get_bool_value()) {
+            enabled[source] = false;
+            values[source]  = (cdl_int) 0;
+        } else {
+            enabled[source] = true;
+            values[source]  = val;
+        }
+        break;
+                    
+      case CdlValueFlavor_Data:
+        values[source] = val;
+        break;
+                    
+      default:
+        CYG_FAIL("Unknown value flavor detected.");
+    }
+    
+    source_valid[source] = true;
+    if (source > current_source) {
+        current_source = source;
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+
+//}}}
+//{{{  CdlListValue class               
+
+// ----------------------------------------------------------------------------
+// List values. Most of this is straightforward.
+
+CdlListValue::CdlListValue()
+{
+    CYG_REPORT_FUNCNAME("CdlListValue:: default constructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    // The only data fields are embedded objects which will have been
+    // filled in already.
+    cdllistvalue_cookie = CdlListValue_Magic;
+    CYGDBG_MEMLEAK_CONSTRUCTOR();
+    
+    CYG_POSTCONDITION_THISC();
+    CYG_REPORT_RETURN();
+}
+
+CdlListValue::CdlListValue(const CdlListValue& original)
+{
+    CYG_REPORT_FUNCNAME("CdlListValue:: copy constructor");
+    CYG_REPORT_FUNCARG2XV(this, &original);
+    CYG_INVARIANT_CLASSOC(CdlListValue, original);
+    
+    // This may get expensive, but should not happen very often.
+    table               = original.table;
+    integer_ranges      = original.integer_ranges;
+    double_ranges       = original.double_ranges;
+    cdllistvalue_cookie = CdlListValue_Magic;
+    CYGDBG_MEMLEAK_CONSTRUCTOR();
+    
+    CYG_POSTCONDITION_THISC();
+    CYG_REPORT_RETURN();
+}
+
+CdlListValue & CdlListValue::operator=(const CdlListValue& original)
+{
+    CYG_REPORT_FUNCNAME("CdlListValue:: assignment operator");
+    CYG_REPORT_FUNCARG2XV(this, &original);
+    CYG_INVARIANT_CLASSOC(CdlListValue, original);
+
+    if (this != &original) {
+        table.clear();
+        integer_ranges.clear();
+        double_ranges.clear();
+        table          = original.table;
+        integer_ranges = original.integer_ranges;
+        double_ranges  = original.double_ranges;
+    }
+    
+    CYG_POSTCONDITION_THISC();
+    CYG_REPORT_RETURN();
+    return *this;
+}
+
+CdlListValue::~CdlListValue()
+{
+    CYG_REPORT_FUNCNAME("CdlListValue:: destructor");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    cdllistvalue_cookie = CdlListValue_Invalid;
+    table.clear();
+    integer_ranges.clear();
+    double_ranges.clear();
+    CYGDBG_MEMLEAK_DESTRUCTOR();
+    
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// Finding out about the current legal values. These routines can be
+// used by GUI-related code to figure out a sensible widget to be used
+// for a CDL entity. In nearly all cases life will be simple: either
+// there will be a fixed set of legal values and the user merely has
+// to choose one of these; or there will be a simple numerical range.
+// Occasionally life may be more complicated, if the full generality
+// of CDL list expressions is being used, and it will be necessary to
+// use an entry box instead. Note that the entity's flavor may also
+// affect the user interface.
+
+const std::vector<CdlSimpleValue>&
+CdlListValue::get_table(void) const
+{
+    CYG_REPORT_FUNCNAME("CdlListValue::get_table");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CYG_REPORT_RETURN();
+    return table;
+}
+
+const std::vector<std::pair<cdl_int, cdl_int> >&
+CdlListValue::get_integer_ranges(void) const
+{
+    CYG_REPORT_FUNCNAME("CdlListValue::get_integer_ranges");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CYG_REPORT_RETURN();
+    return integer_ranges;
+}
+
+const std::vector<std::pair<double, double> >&
+CdlListValue::get_double_ranges(void) const
+{
+    CYG_REPORT_FUNCNAME("CdlListValue::get_double_ranges");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CYG_REPORT_RETURN();
+    return double_ranges;
+}
+
+// ----------------------------------------------------------------------------
+// Membership. This can be quite complicated.
+//
+// 1) anything which has an integer representation must be checked against
+//    the integer ranges and the vector of integer constants. It must
+//    also be checked against the floating point ranges, since calculations
+//    may have resulted in the fractional part disappearing, assuming that
+//    the integer has a floating point representation.
+//
+// 2) similarly anything which has a floating point representation must
+//    be checked against the floating point ranges and constant vector.
+//    In addition it may have an empty fractional part in which case
+//    integer comparisons have to be attempted as well.
+//
+// 3) string data needs to be tested first of all for integer and double
+//    representations. If these fail then the comparison should be against
+//    the string vector.
+//
+// For floating point data exact comparisons are of course meaningless,
+// and arguably the vector of floating point constants is useless. The
+// ranges vector is better, but still not ideal. It may be necessary
+// to introduce an epsilon fudge factor.
+
+bool
+CdlListValue::is_member(CdlSimpleValue& val) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlListValue::is_member (CdlSimpleValue)", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, &val);
+    CYG_PRECONDITION_THISC();
+
+    bool result = false;
+    if (val.has_integer_value()) {
+        result = is_member(val.get_integer_value(), false);
+    }
+    if (!result && val.has_double_value()) {
+        result = is_member(val.get_double_value(), false);
+    }
+    if (!result) {
+        result = is_member(val.get_value());
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlListValue::is_member(std::string val, bool allow_conversions) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlListValue::is_member (string)", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, &val, allow_conversions);
+    CYG_PRECONDITION_THISC();
+
+    bool        result = false;
+    if (allow_conversions) {
+        cdl_int     integer_value;
+        double      double_value;
+
+        if (Cdl::string_to_integer(val, integer_value)) {
+            result = is_member(integer_value, false);
+        }
+        if (!result && Cdl::string_to_double(val, double_value)) {
+            result = is_member(double_value, false);
+        }
+    }
+    if (!result) {
+        for (std::vector<CdlSimpleValue>::const_iterator val_i = table.begin(); val_i != table.end(); val_i++) {
+            if (val_i->get_value() == val) {
+                result = true;
+                break;
+            }
+        }
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlListValue::is_member(cdl_int val, bool allow_conversions) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlListValue::is_member (int)", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, &val, allow_conversions);
+    CYG_PRECONDITION_THISC();
+
+    bool result = false;
+    for (std::vector<CdlSimpleValue>::const_iterator val_i = table.begin(); val_i != table.end(); val_i++) {
+        if (val_i->has_integer_value() && (val_i->get_integer_value() == val)) {
+            result = true;
+            break;
+        }
+    }
+    if (!result) {
+        for (std::vector<std::pair<cdl_int,cdl_int> >::const_iterator i = integer_ranges.begin();
+             i != integer_ranges.end(); i++) {
+            if ((val >= i->first) && (val <= i->second)) {
+                result = true;
+                break;
+            }
+        }
+    }
+    if (!result && allow_conversions) {
+        double double_value = Cdl::integer_to_double(val);
+        result = is_member(double_value, false);
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlListValue::is_member(double val, bool allow_conversions) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlListValue::is_member (double)", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, &val, allow_conversions);
+    CYG_PRECONDITION_THISC();
+
+    bool result = false;
+    for (std::vector<CdlSimpleValue>::const_iterator val_i = table.begin(); val_i != table.end(); val_i++) {
+        if (val_i->has_double_value() && (val_i->get_double_value() == val)) {
+            result = true;
+            break;
+        }
+    }
+    if (!result) {
+        for (std::vector<std::pair<double,double> >::const_iterator i = double_ranges.begin();
+             i != double_ranges.end(); i++) {
+            if ((val >= i->first) && (val <= i->second)) {
+                result = true;
+                break;
+            }
+        }
+    }
+    if (!result && allow_conversions) {
+        cdl_int integer_value;
+        if (Cdl::double_to_integer(val, integer_value)) {
+            result = is_member(integer_value, false);
+        }
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+
+bool
+CdlListValue::check_this(cyg_assert_class_zeal zeal) const
+{
+    if (CdlListValue_Magic != cdllistvalue_cookie) {
+        return false;
+    }
+    CYGDBG_MEMLEAK_CHECKTHIS();
+
+    // After construction the various vectors will still be empty, they
+    // do not get filled in until a list expression is evaluated. No
+    // further tests are possible here.
+    return true;
+}
+
+//}}}
+
+//{{{  dialog property                  
+
+// ----------------------------------------------------------------------------
+// Syntax: dialog <reference>
+
+void
+CdlValuableBody::dialog_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                       CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::dialog_update_handler");
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(source);
+    CYG_PRECONDITION_CLASSC(prop);
+    
+    // The main update of interest is Loaded (iff dest != 0), and
+    // Created. These updates indicate that the destination now exists,
+    // so it is possible to check that the destination is a dialog.
+    if (((CdlUpdate_Loaded == change) && (0 != dest)) ||
+        (CdlUpdate_Created == change)) {
+
+        CYG_ASSERT_CLASSC(dest);
+        CdlDialog dialog = dynamic_cast<CdlDialog>(dest);
+        if (0 == dialog) {
+            std::string msg = dest->get_class_name() + " " + dest->get_name() +
+                " cannot be used in a dialog property, it is not a custom dialog.";
+            CdlConflict_DataBody::make(transaction, source, prop, msg);
+        }
+        
+    } else if (CdlUpdate_Destroyed == change) {
+        // If there was a data conflict object, it is no longer relevant
+        transaction->clear_structural_conflicts(source, prop, &CdlConflict_DataBody::test);
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::parse_dialog(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_dialog", "result %d");
+
+    int result = CdlParse::parse_reference_property(interp, argc, argv, CdlPropertyId_Dialog, 0, 0, false, &dialog_update_handler);
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_dialog() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_dialog", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    // It is not enough to have the property, the dialog reference must also be
+    // resolved and go to a dialog.
+    bool        result          = false;
+    CdlProperty property        = get_property(CdlPropertyId_Dialog);
+    if (0 != property) {
+        CdlProperty_Reference ref_prop = dynamic_cast<CdlProperty_Reference>(property);
+        CYG_ASSERTC(0 != ref_prop);
+
+        CdlNode destination = ref_prop->get_destination();
+        if (0 != destination) {
+            CdlDialog dialog = dynamic_cast<CdlDialog>(destination);
+            if (0 != dialog) {
+                result = true;
+            }
+        }
+    }
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+
+CdlDialog
+CdlValuableBody::get_dialog() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_dialog", "result %p");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlDialog   result          = 0;
+    CdlProperty property        = get_property(CdlPropertyId_Dialog);
+    if (0 != property) {
+        CdlProperty_Reference ref_prop = dynamic_cast<CdlProperty_Reference>(property);
+        CYG_ASSERTC(0 != ref_prop);
+
+        CdlNode destination = ref_prop->get_destination();
+        if (0 != destination) {
+            result = dynamic_cast<CdlDialog>(destination);
+        }
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  wizard property                  
+
+// ----------------------------------------------------------------------------
+// Syntax: wizard <reference>
+
+void
+CdlValuableBody::wizard_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                       CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::wizard_update_handler");
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(source);
+    CYG_PRECONDITION_CLASSC(prop);
+    
+    // The main update of interest is Loaded (iff dest != 0), and
+    // Created. These updates indicate that the destination now exists,
+    // so it is possible to check that the destination is a dialog.
+    if (((CdlUpdate_Loaded == change) && (0 != dest)) ||
+        (CdlUpdate_Created == change)) {
+
+        CYG_ASSERT_CLASSC(dest);
+        CdlWizard wizard = dynamic_cast<CdlWizard>(dest);
+        if (0 == wizard) {
+            std::string msg = dest->get_class_name() + " " + dest->get_name() +
+                " cannot be used in a wizard property, it is not a wizard.";
+            CdlConflict_DataBody::make(transaction, source, prop, msg);
+        }
+        
+    } else if (CdlUpdate_Destroyed == change) {
+        // If there was a data conflict object, it is no longer relevant
+        transaction->clear_structural_conflicts(source, prop, &CdlConflict_DataBody::test);
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::parse_wizard(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_wizard", "result %d");
+
+    int result = CdlParse::parse_reference_property(interp, argc, argv, CdlPropertyId_Wizard, 0, 0, false, &wizard_update_handler);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_wizard() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_wizard", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    // It is not enough to have the property, the wizard reference
+    // must also be resolved to a wizard object.
+    bool        result          = false;
+    CdlProperty property        = get_property(CdlPropertyId_Wizard);
+    if (0 != property) {
+        CdlProperty_Reference ref_prop = dynamic_cast<CdlProperty_Reference>(property);
+        CYG_ASSERTC(0 != ref_prop);
+
+        CdlNode destination = ref_prop->get_destination();
+        if (0 != destination) {
+            CdlWizard wizard = dynamic_cast<CdlWizard>(destination);
+            CYG_ASSERTC(0 != wizard);
+            CYG_UNUSED_PARAM(CdlWizard, wizard);
+            result = true;
+        }
+    }
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+CdlWizard
+CdlValuableBody::get_wizard() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_wizard", "result %p");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlWizard   result          = 0;
+    CdlProperty property        = get_property(CdlPropertyId_Wizard);
+    if (0 != property) {
+        CdlProperty_Reference ref_prop = dynamic_cast<CdlProperty_Reference>(property);
+        CYG_ASSERTC(0 != ref_prop);
+
+        CdlNode destination = ref_prop->get_destination();
+        if (0 != destination) {
+            result = dynamic_cast<CdlWizard>(destination);
+            CYG_ASSERTC(0 != result);
+        }
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  legal_values property            
+
+// ----------------------------------------------------------------------------
+// Syntax: legal_values <list expression>
+
+void
+CdlValuableBody::legal_values_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                             CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("legal_values_update_handler");
+
+    // Loaded and Unloading are of no immediate interest, reference
+    // updating happens in the calling code.
+    //
+    // Any other change can affect the list expression and hence
+    // invalidate the current value.
+    if ((CdlUpdate_Loaded == change) || (CdlUpdate_Unloading == change)) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    CdlValuable valuable = dynamic_cast<CdlValuable>(source);
+    CdlProperty_ListExpression lexpr = dynamic_cast<CdlProperty_ListExpression>(prop);
+    CYG_ASSERT_CLASSC(valuable);
+    CYG_ASSERT_CLASSC(lexpr);
+
+    valuable->check_value(transaction);
+
+    CYG_UNUSED_PARAM(CdlNode, dest);
+    CYG_UNUSED_PARAM(CdlProperty_ListExpression, lexpr);
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::parse_legal_values(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_legal_values", "result %d");
+
+    int result = CdlParse::parse_listexpression_property(interp, argc, argv, CdlPropertyId_LegalValues, 0, 0,
+                                                         &legal_values_update_handler);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_legal_values() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_legal_values", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = has_property(CdlPropertyId_LegalValues);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+CdlProperty_ListExpression
+CdlValuableBody::get_legal_values() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_legal_values", "result %p");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlProperty_ListExpression result = 0;
+    CdlProperty       property          = get_property(CdlPropertyId_LegalValues);
+    if (0 != property) {
+        result = dynamic_cast<CdlProperty_ListExpression>(property);
+        CYG_ASSERTC(0 != result);
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  default_value property           
+
+// ----------------------------------------------------------------------------
+// syntax: default_value <expr>
+
+void
+CdlValuableBody::default_value_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                              CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::default_value_update_handler");
+    CYG_REPORT_FUNCARG5XV(transaction, source, prop, dest, change);
+
+    // Loaded and unloading should be ignored.
+    if ((CdlUpdate_Loaded == change) || (CdlUpdate_Unloading == change)) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // Init, Created, Destroyed, ValueChange and ActiveChange should
+    // all result in the expression being re-evaluated and the result
+    // applied.
+    CdlValuable valuable = dynamic_cast<CdlValuable>(source);
+    CYG_ASSERTC(0 != valuable);
+    CdlProperty_Expression expr = dynamic_cast<CdlProperty_Expression>(prop);
+    CYG_ASSERTC(0 != expr);
+    
+    CdlSimpleValue val;
+
+    try {
+        
+        CdlEvalContext context(transaction, source, prop);
+        expr->eval(context, val);
+
+        valuable->set(transaction, val, CdlValueSource_Default);
+
+    } catch(CdlEvalException e) {
+
+        
+        // An EvalException conflict will have been created, so the
+        // user knows that this default_value is not kosher. It is
+        // still a good idea to make sure that the object retains a
+        // sensible value.
+        val = (cdl_int) 0;
+        valuable->set(transaction, val, CdlValueSource_Default);
+    }
+
+    CYG_UNUSED_PARAM(CdlNode, dest);
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::parse_default_value(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_default_value", "result %d");
+    int result = CdlParse::parse_expression_property(interp, argc, argv, CdlPropertyId_DefaultValue, 0, 0,
+                                                     &default_value_update_handler);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_default_value_expression() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_default_value_expression", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = has_property(CdlPropertyId_DefaultValue);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+CdlProperty_Expression
+CdlValuableBody::get_default_value_expression() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_default_value_expression", "result %");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlProperty_Expression result = 0;
+    CdlProperty property          = get_property(CdlPropertyId_DefaultValue);
+    if (0 != property) {
+        result = dynamic_cast<CdlProperty_Expression>(property);
+        CYG_ASSERTC(0 != result);
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  calculated_property              
+
+// ----------------------------------------------------------------------------
+// Syntax: calculated <expression>
+
+void
+CdlValuableBody::calculated_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                           CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::default_value_update_handler");
+    CYG_REPORT_FUNCARG5XV(transaction, source, prop, dest, change);
+
+    // Loaded and unloading should be ignored.
+    if ((CdlUpdate_Loaded == change) || (CdlUpdate_Unloading == change)) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // Init, Created, Destroyed, ValueChange and ActiveChange should
+    // all result in the expression being re-evaluated and the result
+    // applied.
+    CdlValuable valuable = dynamic_cast<CdlValuable>(source);
+    CYG_ASSERTC(0 != valuable);
+    CdlProperty_Expression expr = dynamic_cast<CdlProperty_Expression>(prop);
+    CYG_ASSERTC(0 != expr);
+    
+    CdlSimpleValue val;
+
+    try {
+        
+        CdlEvalContext context(transaction, source, prop);
+        expr->eval(context, val);
+
+        valuable->set(transaction, val, CdlValueSource_Default);
+
+    } catch(CdlEvalException e) {
+
+        
+        // An EvalException conflict will have been created, so the
+        // user knows that this default_value is not kosher. It is
+        // still a good idea to make sure that the object retains a
+        // sensible value.
+        val = (cdl_int) 0;
+        valuable->set(transaction, val, CdlValueSource_Default);
+    }
+
+    CYG_UNUSED_PARAM(CdlNode, dest);
+    CYG_REPORT_RETURN();
+}
+
+// FIXME: check for flavor none?
+int
+CdlValuableBody::parse_calculated(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_calculated", "result %d");
+
+    int result = CdlParse::parse_expression_property(interp, argc, argv, CdlPropertyId_Calculated, 0, 0,
+                                                     &calculated_update_handler);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_calculated_expression() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_calculated_expression", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = has_property(CdlPropertyId_Calculated);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+CdlProperty_Expression
+CdlValuableBody::get_calculated_expression() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_calculated_expression", "result %p");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlProperty_Expression result   = 0;
+    CdlProperty            property = get_property(CdlPropertyId_Calculated);
+    if (0 != property) {
+        result = dynamic_cast<CdlProperty_Expression>(property);
+        CYG_ASSERTC(0 != result);
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  active_if property               
+
+// ----------------------------------------------------------------------------
+// Syntax:
+//    active_if <goal expression>
+
+void
+CdlValuableBody::active_if_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                      CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::active_if_update_handler");
+    CYG_REPORT_FUNCARG5XV(transaction, source, prop, dest, change);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(source);
+    CYG_PRECONDITION_CLASSC(prop);
+
+    // Loaded should be ignored here, the world is still getting sorted out.
+    // Unloading is of no interest, the source is disappearing anyway.
+    if ((CdlUpdate_Loaded == change) || (CdlUpdate_Unloading == change)) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // Any other change warrants re-evaluating the active status of the source.
+    // This can be achieved via a test_active() call, although that may do
+    // more work than is strictly necessary e.g. it may re-evaluate other
+    // is_active properties. In practice it is unlikely that there will
+    // be enough other constraints to warrant more efficient processing.
+    bool old_state = transaction->is_active(source);
+    bool new_state = source->test_active(transaction);
+    if (old_state != new_state) {
+        transaction->set_active(source, new_state);
+    }
+    
+    CYG_UNUSED_PARAM(CdlNode, dest);
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::parse_active_if(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_active_if", "result %d");
+
+    int result = CdlParse::parse_goalexpression_property(interp, argc, argv, CdlPropertyId_ActiveIf, 0, 0,
+                                                         &active_if_update_handler);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_active_if_conditions() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_active_if_conditions", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = has_property(CdlPropertyId_ActiveIf);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+void
+CdlValuableBody::get_active_if_conditions(std::vector<CdlProperty_GoalExpression>& result) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_active_if_conditions");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    std::vector<CdlProperty> properties;
+    get_properties(CdlPropertyId_ActiveIf, properties);
+    std::vector<CdlProperty>::const_iterator i;
+    for (i = properties.begin(); i != properties.end(); i++) {
+        CdlProperty_GoalExpression goal = dynamic_cast<CdlProperty_GoalExpression>(*i);
+        CYG_ASSERTC(0 != goal);
+        result.push_back(goal);
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  requires property                
+
+// ----------------------------------------------------------------------------
+// Syntax: requires <goal expression>
+
+void
+CdlValuableBody::requires_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                         CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::requires_update_handler");
+    CYG_REPORT_FUNCARG5XV(transaction, source, prop, dest, change);
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    // Loaded and Unloading are not of interest.
+    if ((CdlUpdate_Loaded == change) || (CdlUpdate_Unloading == change)) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+    
+    // Any other change should cause normal handling. This happens in
+    // a separate function because "requires" properties also need to
+    // be checked when e.g. the source becomes inactive.
+    CdlValuable valuable = dynamic_cast<CdlValuable>(source);
+    CdlProperty_GoalExpression gexpr = dynamic_cast<CdlProperty_GoalExpression>(prop);
+    CYG_ASSERT_CLASSC(valuable);
+    CYG_ASSERT_CLASSC(gexpr);
+
+    valuable->check_requires(transaction, gexpr);
+
+    CYG_UNUSED_PARAM(CdlNode, dest);
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::parse_requires(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_requires", "result %d");
+
+    int result = CdlParse::parse_goalexpression_property(interp, argc, argv, CdlPropertyId_Requires, 0, 0,
+                                                         &requires_update_handler);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_requires_goals() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_requires_goals", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = has_property(CdlPropertyId_Requires);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+void
+CdlValuableBody::get_requires_goals(std::vector<CdlProperty_GoalExpression>& result) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_requires_goals");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    std::vector<CdlProperty> properties;
+    get_properties(CdlPropertyId_Requires, properties);
+    std::vector<CdlProperty>::const_iterator i;
+    for (i = properties.begin(); i != properties.end(); i++) {
+        CdlProperty_GoalExpression goal = dynamic_cast<CdlProperty_GoalExpression>(*i);
+        CYG_ASSERTC(0 != goal);
+        result.push_back(goal);
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  implements property              
+
+// ----------------------------------------------------------------------------
+// Syntax: implements <reference to interface>
+
+void
+CdlValuableBody::implements_update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest,
+                                           CdlUpdate change)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::implements_update_handler");
+    CYG_REPORT_FUNCARG5XV(transaction, source, prop, dest, change);
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    // Calculation of interface values happens inside
+    // CdlInterfaceBody::recalculate(). That member function simply
+    // checks all of the implementors and recalculates the value from
+    // scratch. It needs to be invoked whenever there is a relevant
+    // change to the implementors. Currently no attempt is made to
+    // optimise interface updates, although this may have to change in
+    // future.
+
+    // Any changes to the interface itself can be ignored.
+    if ((CdlUpdate_ValueChange == change) || (CdlUpdate_ActiveChange == change)) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // The second stage init is irrelevant
+    if (CdlUpdate_Init == change) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // Possibilities:
+    // 1) source is being loaded, dest valid
+    // 2) source is being loaded, dest unknown
+    // 3) source is being unloaded, dest valid
+    // 4) source is being unloaded, dest unknown
+    // 5) dest has been created
+    // 6) dest is going away
+    //
+    // If we have a valid dest, it needs to be updated and any structural
+    // conflicts have to be cleared.
+    //
+    // If there is no dest, the implements property remains unbound.
+    // A suitable conflict is created in the base class.
+    //
+    // If the dest is invalid, a structural conflict has to be created.
+    if (CdlUpdate_Destroyed == change) {
+        // There is no need to do any clean-ups in the dest.
+        dest = 0;
+    }
+    if (0 == dest) {
+        transaction->clear_structural_conflicts(source, prop, &CdlConflict_DataBody::test);
+    } else {
+        CdlInterface interface = dynamic_cast<CdlInterface>(dest);
+
+        if (0 == interface) {
+            std::string msg = source->get_class_name() + " " + source->get_name() + " cannot implement " +
+                dest->get_name() + "\n    The latter is not an interface.";
+            CdlConflict_DataBody::make(transaction, source, prop, msg);
+        } else {
+            transaction->clear_structural_conflicts(source, prop, &CdlConflict_DataBody::test);
+            interface->recalculate(transaction);
+        }
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::parse_implements(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_implements", "result %d");
+
+    int result = CdlParse::parse_reference_property(interp, argc, argv, CdlPropertyId_Implements, 0, 0, false,
+                                                    &implements_update_handler);
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+void
+CdlValuableBody::get_implemented_interfaces(std::vector<CdlInterface>& result) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_implemented_interfaces");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    std::vector<CdlProperty> properties;
+    get_properties(CdlPropertyId_Implements, properties);
+    std::vector<CdlProperty>::const_iterator i;
+    for (i = properties.begin(); i != properties.end(); i++) {
+        CdlProperty_Reference refprop = dynamic_cast<CdlProperty_Reference>(*i);
+        CYG_ASSERTC(0 != refprop);
+        CdlNode node = refprop->get_destination();
+        if (0 != node) {
+            CdlInterface interface = dynamic_cast<CdlInterface>(node);
+            CYG_ASSERT_CLASSC(interface);
+            result.push_back(interface);
+        }
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  Other properties                 
+
+// ----------------------------------------------------------------------------
+// Syntax: flavor <legal flavor>
+
+static void
+parse_flavor_final_check(CdlInterpreter interp, CdlProperty_String prop)
+{
+    CYG_REPORT_FUNCNAME("parse_flavor_final_check");
+    CYG_PRECONDITION_CLASSC(interp);
+    CYG_PRECONDITION_CLASSC(prop);
+    
+    const std::string& str = prop->get_string();
+    std::string copy = std::string(str);
+    CdlValueFlavor flavor;
+
+    if (!Cdl::string_to_flavor(copy, flavor)) {
+        CdlParse::report_property_parse_error(interp, prop, str + " is not a valid CDL flavor.");
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+
+int
+CdlValuableBody::parse_flavor(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_flavor", "result %d");
+
+    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Flavor, 0, &parse_flavor_final_check);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// syntax: group <group name>
+int
+CdlValuableBody::parse_group(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_group", "result %d");
+
+    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Group, 0, 0);
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// Syntax: check_proc <tclcode>
+
+int
+CdlValuableBody::parse_check_proc(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_check_proc", "result %d");
+
+    int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_CheckProc, 0, 0);
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_check_proc() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_check_proc", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = has_property(CdlPropertyId_CheckProc);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+cdl_tcl_code
+CdlValuableBody::get_check_proc() const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_check_proc");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    cdl_tcl_code result         = "";
+    CdlProperty  property       = get_property(CdlPropertyId_CheckProc);
+    if (0 != property) {
+        CdlProperty_TclCode code_prop = dynamic_cast<CdlProperty_TclCode>(property);
+        CYG_ASSERTC(0 != code_prop);
+        result = code_prop->get_code();
+    }
+    
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// Syntax: entry_proc <tclcode>
+
+int
+CdlValuableBody::parse_entry_proc(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAMETYPE("parse_entry_proc", "result %d");
+
+    int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_EntryProc, 0, 0);
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_entry_proc() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_entry_proc", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = has_property(CdlPropertyId_EntryProc);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+cdl_tcl_code
+CdlValuableBody::get_entry_proc() const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_entry_proc");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    cdl_tcl_code result         = "";
+    CdlProperty  property       = get_property(CdlPropertyId_EntryProc);
+    if (0 != property) {
+        CdlProperty_TclCode code_prop = dynamic_cast<CdlProperty_TclCode>(property);
+        CYG_ASSERTC(0 != code_prop);
+        result = code_prop->get_code();
+    }
+
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+//}}}
+
+//{{{  CdlValuable misc                 
+
+// ----------------------------------------------------------------------------
+// Objects with flavor none are not modifiable. Also, objects with the
+// calculated property are not modifiable. Everything else is ok.
+
+bool
+CdlValuableBody::is_modifiable() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuableBody::is_modifiable", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = true;
+    if (CdlValueFlavor_None == get_flavor()) {
+        result = false;
+    } else if (has_property(CdlPropertyId_Calculated)) {
+        result = false;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  CdlValuable::get_widget_hint()   
+
+// ----------------------------------------------------------------------------
+
+void
+CdlValuableBody::get_widget_hint(CdlWidgetHint& hint)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_widget_hint");
+    CYG_REPORT_FUNCARG2XV(this, &hint);
+    CYG_PRECONDITION_THISC();
+
+    // Start by resetting the hint to default values.
+    hint.bool_widget  = CdlBoolWidget_None;
+    hint.value_widget = CdlValueWidget_None;
+    hint.radio_button_interface = "";
+
+    // If the valuable is a loadable then it cannot be modified directly.
+    // Changing the value means unloading and/or loading more data
+    // into the configuration. This should always be handled via a
+    // separate dialog, followed by a tree redisplay
+    CdlConstLoadable loadable = dynamic_cast<CdlConstLoadable>(this);
+    if (0 != loadable) {
+        hint.value_widget = CdlValueWidget_Loadable;
+        CYG_REPORT_RETURN();
+        return;
+    }
+    
+    // If the valuable is not modifiable then we are already done.
+    CdlValueFlavor flavor = this->get_flavor();
+    if ((CdlValueFlavor_None == flavor) || !this->is_modifiable()) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // If there is a custom dialog and dialogs are enabled, use it.
+    if (this->has_dialog() && CdlDialogBody::dialogs_are_enabled()) {
+        if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
+            hint.bool_widget = CdlBoolWidget_CustomDialog;
+        }
+        if ((CdlValueFlavor_Data == flavor) || (CdlValueFlavor_BoolData == flavor)) {
+            hint.value_widget = CdlValueWidget_CustomDialog;
+        }
+        CYG_REPORT_RETURN();
+        return;
+    }
+    
+    // Process the bool part, if any
+    if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
+        
+        // Default to a CheckButton
+        hint.bool_widget = CdlBoolWidget_CheckButton;
+
+        // Under some circumstances it is appropriate to use a radio button instead.
+        // This is the case when there are several mutually exclusive entities.
+        // Most of the time radio buttons should actually be handled by a single
+        // option which has a list of legal values. There are a couple of cases
+        // where this is not appropriate:
+        //
+        // 1) grouping. Some of the mutually exclusive entities could be containers.
+        //    With clever use of a single option and some active_if properties it
+        //    would be possible to get almost the same effect, but not quite.
+        //
+        // 2) external packages. It should be possible to have a third party package
+        //    which could add e.g. a new scheduler.
+        //
+        // The implementation of this involves interfaces. Basically mutually
+        // exclusive entities should implement the same interface, and that
+        // interface should have an explicit requires $cdl_value == 1
+        // In addition all of the options involved should have the same parent.
+        // An entity may implement multiple interfaces, so they all have to be checked
+        CdlInterface radio_interface = 0;
+        std::vector<CdlProperty> implements = this->get_properties(CdlPropertyId_Implements);
+        std::vector<CdlProperty>::const_iterator imp_i;
+        for (imp_i = implements.begin(); (imp_i != implements.end()) && (0 == radio_interface); imp_i++) {
+            CdlProperty_Reference refprop = dynamic_cast<CdlProperty_Reference>(*imp_i);
+            CYG_ASSERT_CLASSC(refprop);
+
+            CdlNode destnode = refprop->get_destination();
+            if (0 == destnode) {
+                continue;
+            }
+            CdlInterface interface = dynamic_cast<CdlInterface>(destnode);
+            CYG_ASSERT_CLASSC(interface);
+            std::vector<CdlProperty_GoalExpression> requires;
+            std::vector<CdlProperty_GoalExpression>::const_iterator req_i;
+            interface->get_requires_goals(requires);
+            for (req_i = requires.begin(); req_i != requires.end(); req_i++) {
+
+                CdlExpression expr = (*req_i)->get_expression();
+                CdlSubexpression& subexpr = expr->sub_expressions[expr->first_subexpression];
+                if (CdlExprOp_Equal != subexpr.op) {
+                    continue;
+                }
+                CdlSubexpression& lhs = expr->sub_expressions[subexpr.lhs_index];
+                CdlSubexpression& rhs = expr->sub_expressions[subexpr.rhs_index];
+                CdlSubexpression* ref_operand = &lhs;
+
+                // Allow for "a == 1" or "1 == a"
+                if ((CdlExprOp_IntegerConstant == lhs.op) && (1 == lhs.constants.get_integer_value())) {
+                    ref_operand = &rhs;
+                } else if ((CdlExprOp_IntegerConstant == rhs.op) && (1 == rhs.constants.get_integer_value())) {
+                    ref_operand = &lhs;
+                } else {
+                    continue;
+                }
+
+                if (CdlExprOp_Reference != ref_operand->op) {
+                    continue;
+                }
+                CdlReference& ref = expr->references[ref_operand->reference_index];
+                if (ref.get_destination() == interface) {
+                    break;
+                }
+            }
+            if (req_i == requires.end()) {
+                continue;
+            }
+
+            CdlContainer parent = this->get_parent();
+            CYG_ASSERT_CLASSC(parent);
+            std::vector<CdlValuable> implementers;
+            std::vector<CdlValuable>::const_iterator imp_i;
+            interface->get_implementers(implementers);
+            for (imp_i = implementers.begin(); imp_i != implementers.end(); imp_i++) {
+                if (parent != (*imp_i)->get_parent()) {
+                    break;
+                }
+            }
+
+            if (imp_i == implementers.end()) {
+                // An interface has been found that matches the constraints.
+                radio_interface = interface;
+            }
+        }
+        if (0 != radio_interface) {
+            hint.bool_widget = CdlBoolWidget_Radio;
+            hint.radio_button_interface = radio_interface->get_name();
+        }
+    }
+
+    // Process the data part, if any
+    if ((CdlValueFlavor_Data == flavor) || (CdlValueFlavor_BoolData == flavor)) {
+        
+        // Default to a simple entry box.
+        hint.value_widget = CdlValueWidget_EntryBox;
+        
+        // If there is a legal_values list, this will normally indicate
+        // which widget should be used.
+        if (this->has_legal_values()) {
+            // The legal_values expression needs to be evaluated and examined.
+            // If the result is a simple numerical range then all we need to
+            // figure out is whether to default to decimal, hex, octal or double.
+            // Otherwise if the result is a simple list and all of the entries
+            // are numerical, that is sufficient information. If a list with
+            // non-numerical entries that is fine as well. Anything more complicated
+            // needs to revert to an entry box.
+            CdlProperty_ListExpression lexpr = this->get_legal_values();
+            CdlEvalContext             context(0, this, lexpr);
+            CdlListValue               val;
+
+            try {
+                lexpr->eval(context, val);
+                const std::vector<CdlSimpleValue>& table = val.get_table();
+                const std::vector<std::pair<cdl_int, cdl_int> >& int_ranges = val.get_integer_ranges();
+                const std::vector<std::pair<double, double> >&   double_ranges = val.get_double_ranges();
+                
+                if ((0 == table.size()) && (0 == int_ranges.size()) && (1 == double_ranges.size())) {
+                    
+                    // A straightforward range of double precision numbers
+                    hint.value_widget = CdlValueWidget_DoubleRange;
+                    
+                } else if ((0 == table.size()) && (1 == int_ranges.size()) && (0 == double_ranges.size())) {
+
+                    // Bummer. The formatting information has been lost.
+                    // To fix this the two sets of ranges should be collapsed into pairs of
+                    // CdlSimpleValue's.
+                    hint.value_widget = CdlValueWidget_DecimalRange;
+                    
+                } else if ((1 <= table.size() && (0 == int_ranges.size()) && (0 == double_ranges.size()))) {
+
+                    // If all of the values are numerical, then we have a numeric set.
+                    // Otherwise we have a string set.
+                    bool all_numeric = true;
+                    std::vector<CdlSimpleValue>::const_iterator tab_i;
+                    for (tab_i = table.begin(); (tab_i != table.end()) && all_numeric; tab_i++) {
+                        if (!tab_i->has_double_value() && !tab_i->has_integer_value()) {
+                            all_numeric = false;
+                        }
+                    }
+                    if (all_numeric) {
+                        hint.value_widget = CdlValueWidget_NumericSet;
+                    } else {
+                        hint.value_widget = CdlValueWidget_StringSet;
+                    }
+                    
+                } else {
+                    // The list expression is a complex combination. Leave it as an entry box.
+                    // In some cases it would be possible to do better, for example
+                    //     legal_values -1 1 to 4 8 to 12
+                    // Support for cases like these may get added in future, if such cases
+                    // ever arise in practice.
+                }
+                
+            } catch(...) {
+                // Not a lot that can be done here, unfortunately
+            }
+        } else {
+            // There is no legal_values property, so an entry box is probably the
+            // right thing to use. There is a special case for multiline strings,
+            // identified by a default_value expression that contains a newline.
+            if (this->has_default_value_expression()) {
+                CdlProperty_Expression expr = this->get_default_value_expression();
+                CdlEvalContext         context(0, this, expr);
+                CdlSimpleValue         val;
+                try {
+                    expr->eval(context, val);
+                    std::string tmp = val.get_value();
+                    if (std::string::npos != tmp.find('\n')) {
+                        hint.value_widget = CdlValueWidget_MultilineString;
+                    }
+                } catch(...) {
+                    // Not a lot that can be done here, unfortunately
+                }
+            }
+        }
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  CdlValuable get operations       
+
+// ----------------------------------------------------------------------------
+const CdlValue&
+CdlValuableBody::get_whole_value() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_whole_value", "result %p");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CYG_REPORT_RETVAL(&value);
+    return value;
+}
+
+CdlValueFlavor
+CdlValuableBody::get_flavor() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_flavor", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlValueFlavor result = value.get_flavor();
+    CYG_REPORT_RETVAL((int) result);
+    return result;
+}
+
+CdlValueSource
+CdlValuableBody::get_source() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_source", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    CdlValueSource result = value.get_source();
+    CYG_REPORT_RETVAL((int) result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_source(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_source", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    bool result = value.has_source(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::is_enabled(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::is_enabled", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    bool result = value.is_enabled(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+std::string
+CdlValuableBody::get_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_value");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    std::string result = value.get_value(source);
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+bool
+CdlValuableBody::has_integer_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_integer_value", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    bool result = value.has_integer_value(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+cdl_int
+CdlValuableBody::get_integer_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_integer_value", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    cdl_int result = value.get_integer_value(source);
+    CYG_REPORT_RETVAL((int) result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_double_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_double_value", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    bool result = value.has_double_value(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+double
+CdlValuableBody::get_double_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_double_value");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    double result = value.get_double_value();
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+CdlSimpleValue
+CdlValuableBody::get_simple_value(CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_simple_value");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    CdlSimpleValue result = value.get_simple_value(source);
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+CdlValueSource
+CdlValuableBody::get_source(CdlTransaction transaction) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_source", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, transaction);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    CdlValueSource result = transaction_value.get_source();
+    CYG_REPORT_RETVAL((int) result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_source(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_source", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    bool result = transaction_value.has_source(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+bool
+CdlValuableBody::is_enabled(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::is_enabled", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    bool result = transaction_value.is_enabled(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+std::string
+CdlValuableBody::get_value(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_value");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    std::string result = transaction_value.get_value(source);
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+bool
+CdlValuableBody::has_integer_value(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_integer_value", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    bool result = transaction_value.has_integer_value(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+cdl_int
+CdlValuableBody::get_integer_value(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::get_integer_value", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    cdl_int result = transaction_value.get_integer_value(source);
+    CYG_REPORT_RETVAL((int) result);
+    return result;
+}
+
+bool
+CdlValuableBody::has_double_value(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::has_double_value", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    bool result = transaction_value.has_double_value(source);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+double
+CdlValuableBody::get_double_value(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_double_value");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    double result = transaction_value.get_double_value();
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+CdlSimpleValue
+CdlValuableBody::get_simple_value(CdlTransaction transaction, CdlValueSource source) const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_simple_value");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& transaction_value = transaction->get_whole_value(this);
+    CdlSimpleValue result = transaction_value.get_simple_value(source);
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+//}}}
+//{{{  CdlValuable internal modify ops  
+
+// ----------------------------------------------------------------------------
+// There has been a change to either the value itself or to the
+// set of legal values. It is necessary to validate the current
+// value, maintaining a suitable conflict object.
+void
+CdlValuableBody::check_value(CdlTransaction transaction)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::check_value");
+    CYG_REPORT_FUNCARG2XV(this, transaction);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    // Checking the value only makes sense for BoolData and Data
+    // values.
+    CdlValueFlavor flavor = value.get_flavor();
+    if ((CdlValueFlavor_BoolData != flavor) && (CdlValueFlavor_Data != flavor)) {
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // If the valuable is not currently active and enabled then it
+    // does not matter whether or not the value is legal. Any old
+    // conflicts should be destroyed.
+    if (!(transaction->is_active(this) && this->is_enabled(transaction))) {
+        transaction->clear_conflicts(this, &CdlConflict_IllegalValueBody::test);
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // If there is a legal_values property, check membership.
+    if (this->has_property(CdlPropertyId_LegalValues)) {
+        CdlProperty_ListExpression lexpr = dynamic_cast<CdlProperty_ListExpression>(get_property(CdlPropertyId_LegalValues));
+        CYG_ASSERT_CLASSC(lexpr);
+
+        CdlSimpleValue val = this->get_simple_value(transaction);
+        CdlEvalContext context(transaction, this, lexpr);
+        try {
+            if (!lexpr->is_member(context, val)) {
+                if (!transaction->has_conflict(this, lexpr, &CdlConflict_IllegalValueBody::test)) {
+                    CdlConflict_IllegalValueBody::make(transaction, this, lexpr);
+                }
+            
+            } else {
+                // Tne current value is legal. Get rid of any old conflicts.
+                transaction->clear_conflicts(this, lexpr, &CdlConflict_IllegalValueBody::test);
+            }
+        } catch(CdlEvalException e) {
+            // There should now be an EvalException conflict for this
+            // node, so there is no point in having an IllegalValue conflict
+            // as well.
+            transaction->clear_conflicts(this, lexpr, &CdlConflict_IllegalValueBody::test);
+        }              
+
+        // FIXME: add support for check_proc
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// There has been a change that may affect "requires" properties.
+// Again do the necessary checking and maintain suitable conflict
+// objects.
+void
+CdlValuableBody::check_requires(CdlTransaction transaction)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::check_requires");
+    CYG_REPORT_FUNCARG2XV(this, transaction);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    std::vector<CdlProperty> requires_properties;
+    std::vector<CdlProperty>::const_iterator prop_i;
+    get_properties(CdlPropertyId_Requires, requires_properties);
+    for (prop_i = requires_properties.begin(); prop_i != requires_properties.end(); prop_i++) {
+
+        CdlProperty_GoalExpression gexpr = dynamic_cast<CdlProperty_GoalExpression>(*prop_i);
+        CYG_ASSERT_CLASSC(gexpr);
+        this->check_requires(transaction, gexpr);
+    }
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::check_requires(CdlTransaction transaction, CdlProperty_GoalExpression gexpr)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::check_requires (property)");
+    CYG_REPORT_FUNCARG3XV(this, transaction, gexpr);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_ASSERT_CLASSC(gexpr);
+
+    // If the valuable is not currently active and enabled then the "requires"
+    // properties are irrelevant, and any old conflicts should be destroyed.
+    if (!transaction->is_active(this) || !this->is_enabled(transaction)) {
+        transaction->clear_conflicts(this, gexpr, &CdlConflict_RequiresBody::test);
+        CYG_REPORT_RETURN();
+        return;
+    }
+
+    // What is the current value of the goal expression?
+    try {
+        CdlEvalContext context(transaction, this, gexpr);
+        if (gexpr->eval(context)) {
+            // The goal is satisfied.
+            transaction->clear_conflicts(this, gexpr, &CdlConflict_RequiresBody::test);
+        } else {
+            // The goal is not satisfied. Make sure there is a conflict object.
+            if (!transaction->has_conflict(this, gexpr, &CdlConflict_RequiresBody::test)) {
+                CdlConflict_RequiresBody::make(transaction, this, gexpr);
+            }
+        }
+    } catch(CdlEvalException e) {
+        // There should now be an EvalException conflict associated with this node,
+        // having a requires conflict as well serves no purpose
+        transaction->clear_conflicts(this, gexpr, &CdlConflict_RequiresBody::test);
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// The update handler. If there is a change to the value or active state
+// then it is necessary to reevaluate any requires properties, and to
+// check whether or not the value is legal wrt legal_values etc.
+void
+CdlValuableBody::update(CdlTransaction transaction, CdlUpdate update)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::update");
+    CYG_REPORT_FUNCARG3XV(this, transaction, update);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    if ((CdlUpdate_ValueChange == update) || (CdlUpdate_ActiveChange == update)) {
+        this->check_value(transaction);
+        this->check_requires(transaction);
+    }
+    
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// Should this node be active. In addition to the base class' checks that
+// the parent is active and enabled, any active_if constraints need
+// to be evaluated.
+
+bool
+CdlValuableBody::test_active(CdlTransaction transaction)
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::test_active", "result %d");
+    CYG_REPORT_FUNCARG2XV(this, transaction);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    bool result = true;
+    if (!this->CdlNodeBody::test_active(transaction)) {
+        result = false;
+    }
+
+    if (result) {
+        std::vector<CdlProperty> active_if_properties;
+        std::vector<CdlProperty>::const_iterator prop_i;
+        
+        this->get_properties(CdlPropertyId_ActiveIf, active_if_properties);
+        for (prop_i = active_if_properties.begin(); result && (prop_i != active_if_properties.end()); prop_i++) {
+            
+            CdlProperty_GoalExpression gexpr = dynamic_cast<CdlProperty_GoalExpression>(*prop_i);
+            CYG_ASSERT_CLASSC(gexpr);
+            CdlEvalContext context(transaction, this, gexpr);
+            try {
+                if (!gexpr->eval(context)) {
+                    result = false;
+                }
+            } catch(CdlEvalException e) {
+                // Hmmm, an active_if property cannot be evaluated.
+                // Tricky. If the node is inactive then its conflicts
+                // are ignored, which would be a bad thing. For now
+                // assume that the node is active, unless it was already
+                // inactive for other reasons.
+            }
+        }
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  CdlValuable modify operations    
+
+// ----------------------------------------------------------------------------
+// Start with the non-transaction versions. These allocate a new transaction,
+// perform their operation in the context of that transaction, and then
+// commit the transaction.
+
+void
+CdlValuableBody::set_source(CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_source (no transaction)");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+    
+    CdlTransaction transaction = CdlTransactionBody::make(get_toplevel());
+    this->set_source(transaction, source);
+    transaction->body();
+    delete transaction;
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::invalidate_source(CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::invalidate_source (no transaction)");
+    CYG_REPORT_FUNCARG2XV(this, source);
+    CYG_PRECONDITION_THISC();
+
+    CdlTransaction transaction = CdlTransactionBody::make(get_toplevel());
+    this->invalidate_source(transaction, source);
+    transaction->body();
+    delete transaction;
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set_enabled(bool val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_enabled (no transaction)");
+    CYG_REPORT_FUNCARG3XV(this, val, source);
+    CYG_PRECONDITION_THISC();
+    
+    CdlTransaction transaction = CdlTransactionBody::make(get_toplevel());
+    this->set_enabled(transaction, val, source);
+    transaction->body();
+    delete transaction;
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set_value(CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_value (no transaction)");
+    CYG_REPORT_FUNCARG3XV(this, &val, source);
+    CYG_PRECONDITION_THISC();
+
+    CdlTransaction transaction = CdlTransactionBody::make(get_toplevel());
+    this->set_value(transaction, val, source);
+    transaction->body();
+    delete transaction;
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set_enabled_and_value(bool enabled_arg, CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_enabled_and_value (no transaction)");
+    CYG_REPORT_FUNCARG4XV(this, enabled_arg, &val, source);
+    CYG_PRECONDITION_THISC();
+
+    CdlTransaction transaction = CdlTransactionBody::make(get_toplevel());
+    this->set_enabled_and_value(transaction, enabled_arg, val, source);
+    transaction->body();
+    delete transaction;
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set(CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set (no transaction)");
+    CYG_REPORT_FUNCARG3XV(this, &val, source);
+    CYG_PRECONDITION_THISC();
+
+    CdlTransaction transaction = CdlTransactionBody::make(get_toplevel());
+    this->set(transaction, val, source);
+    transaction->body();
+    delete transaction;
+
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// These member functions operate in the context of a transaction. The
+// basic format is:
+//
+//  1) find out the state before the change
+//  2) make a local CdlValue copy, and modify it.
+//  3) update the value held in the transaction.
+//
+// Values checks etc. happen during propagation, mainly from inside
+// the update handler. There is code in CdlTransaction::set_whole_value()
+// to avoid unnecessary propagation.
+
+void
+CdlValuableBody::set_source(CdlTransaction transaction, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_source");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_ASSERTC((source == CdlValueSource_Default) || !has_property(CdlPropertyId_Calculated));
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+    
+    const CdlValue& old_value = transaction->get_whole_value(this);
+    CdlValue        new_value = old_value;
+    new_value.set_source(source);
+    transaction->set_whole_value(this, old_value, new_value);
+
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::invalidate_source(CdlTransaction transaction, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::invalidate_source");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+    
+    const CdlValue& old_value = transaction->get_whole_value(this);
+    CdlValue        new_value = old_value;
+    new_value.invalidate_source(source);
+    transaction->set_whole_value(this, old_value, new_value);
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set_enabled(CdlTransaction transaction, bool enabled_arg, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_enabled");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_ASSERTC((source == CdlValueSource_Default) || !has_property(CdlPropertyId_Calculated));
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    const CdlValue& old_value = transaction->get_whole_value(this);
+    CdlValue        new_value = old_value;
+    new_value.set_enabled(enabled_arg, source);
+    transaction->set_whole_value(this, old_value, new_value);
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set_value(CdlTransaction transaction, CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_enabled");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_ASSERTC((source == CdlValueSource_Default) || !has_property(CdlPropertyId_Calculated));
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+    
+    const CdlValue& old_value = transaction->get_whole_value(this);
+    CdlValue        new_value = old_value;
+    new_value.set_value(val, source);
+    transaction->set_whole_value(this, old_value, new_value);
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set_enabled_and_value(CdlTransaction transaction, bool enabled_arg, CdlSimpleValue& val,
+                                       CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set_enabled");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_ASSERTC((source == CdlValueSource_Default) || !has_property(CdlPropertyId_Calculated));
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+    
+    const CdlValue& old_value = transaction->get_whole_value(this);
+    CdlValue        new_value = old_value;
+    new_value.set_enabled_and_value(enabled_arg, val, source);
+    transaction->set_whole_value(this, old_value, new_value);
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set(CdlTransaction transaction, CdlSimpleValue& val, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set");
+    CYG_REPORT_FUNCARG3XV(this, transaction, source);
+    CYG_ASSERTC((source == CdlValueSource_Default) || !has_property(CdlPropertyId_Calculated));
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+    
+    const CdlValue& old_value = transaction->get_whole_value(this);
+    CdlValue        new_value = old_value;
+    new_value.set(val, source);
+    transaction->set_whole_value(this, old_value, new_value);
+    
+    CYG_REPORT_RETURN();
+}
+
+void
+CdlValuableBody::set(CdlTransaction transaction, const CdlValue& val)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::set");
+    CYG_REPORT_FUNCARG2XV(this, transaction);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+    
+    const CdlValue& old_value = transaction->get_whole_value(this);
+    CdlValue        new_value = val;
+    transaction->set_whole_value(this, old_value, new_value);
+
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  CdlValuable basics               
+
+// ----------------------------------------------------------------------------
+// The CdlValuable class implements the concept of CDL objects that take
+// a value. There are lots of properties associated with that.
+
+CdlValuableBody::CdlValuableBody(CdlValueFlavor flavor)
+    : value(flavor)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable:: default constructor");
+    CYG_REPORT_FUNCARG1XV(this);
+
+    cdlvaluablebody_cookie = CdlValuableBody_Magic;
+    CYGDBG_MEMLEAK_CONSTRUCTOR();
+    
+    CYG_POSTCONDITION_THISC();
+    CYG_REPORT_RETURN();
+}
+
+CdlValuableBody::~CdlValuableBody()
+{
+    CYG_REPORT_FUNCNAME("CdlValuableBody:: destructor");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    cdlvaluablebody_cookie = CdlValuableBody_Invalid;
+    CYGDBG_MEMLEAK_DESTRUCTOR();
+    
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+
+std::string
+CdlValuableBody::get_class_name() const
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::get_class_name");
+    CYG_PRECONDITION_THISC();
+    CYG_REPORT_RETURN();
+    return "valuable";
+}
+
+// ----------------------------------------------------------------------------
+bool
+CdlValuableBody::check_this(cyg_assert_class_zeal zeal) const
+{
+    if (CdlValuableBody_Magic != cdlvaluablebody_cookie) {
+        return false;
+    }
+    CYGDBG_MEMLEAK_CHECKTHIS();
+
+    if (has_property(CdlPropertyId_Calculated) && (CdlValueSource_Default != value.get_source())) {
+        CYG_FAIL("Calculated valuables can only have a default value.");
+        return false;
+    }
+    
+    return CdlNodeBody::check_this(zeal) && value.check_this(zeal);
+}
+
+//}}}
+//{{{  CdlValuable parsing support      
+
+// ----------------------------------------------------------------------------
+// Parsing support. Adding the appropriate parsers is straightforward.
+
+void
+CdlValuableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::add_property_parsers");
+
+    static CdlInterpreterCommandEntry commands[] =
+    {
+        CdlInterpreterCommandEntry("active_if",          &parse_active_if    ),
+        CdlInterpreterCommandEntry("calculated",         &parse_calculated   ),
+        CdlInterpreterCommandEntry("check_proc",         &parse_check_proc   ),
+        CdlInterpreterCommandEntry("default_value",      &parse_default_value),
+        CdlInterpreterCommandEntry("dialog",             &parse_dialog       ),
+        CdlInterpreterCommandEntry("entry_proc",         &parse_entry_proc   ),
+        CdlInterpreterCommandEntry("flavor",             &parse_flavor       ),
+        CdlInterpreterCommandEntry("group",              &parse_group        ),
+        CdlInterpreterCommandEntry("implements",         &parse_implements   ),
+        CdlInterpreterCommandEntry("legal_values",       &parse_legal_values ),
+        CdlInterpreterCommandEntry("requires",           &parse_requires     ),
+        CdlInterpreterCommandEntry("wizard",             &parse_wizard       ),
+        CdlInterpreterCommandEntry("",                   0                   )
+    };
+
+    for (int i = 0; commands[i].command != 0; i++) {
+        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
+        for (j = parsers.begin(); j != parsers.end(); j++) {
+            if (commands[i].name == j->name) {
+                if (commands[i].command != j->command) {
+                    CYG_FAIL("Property names are being re-used");
+                }
+                break;
+            }
+        }
+        if (j == parsers.end()) {
+            parsers.push_back(commands[i]);
+        }
+    }
+    CdlNodeBody::add_property_parsers(parsers);
+    
+    CYG_REPORT_RETURN();
+}
+
+// Validatation is quite a bit more complicated...
+void
+CdlValuableBody::check_properties(CdlInterpreter interp)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::check_properties");
+    CYG_REPORT_FUNCARG2XV(this, interp);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(interp);
+
+    // There should be at most one of flavor, entry_proc, check_proc,
+    // default_value, legal_values, dialog, and calculated. There can
+    // be any number of active_if, requires, and implements.
+    // NOTE: should multiple entry_proc's and check_proc's be allowed?
+    //       This could prove useful if there are a sensible number
+    //       of library check_proc's.
+    if (count_properties(CdlPropertyId_Flavor) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one flavor property.");
+    }
+    if (count_properties(CdlPropertyId_EntryProc) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one entry_proc property.");
+    }
+    if (count_properties(CdlPropertyId_CheckProc) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one check_proc property.");
+    }
+    if (count_properties(CdlPropertyId_DefaultValue) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one default_value property.");
+    }
+    if (count_properties(CdlPropertyId_LegalValues) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one legal_values property.");
+    }
+    if (count_properties(CdlPropertyId_Dialog) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one dialog property.");
+    }
+    if (count_properties(CdlPropertyId_Wizard) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one wizard property.");
+    }
+    if (count_properties(CdlPropertyId_Calculated) > 1) {
+        CdlParse::report_error(interp, "", "There should be at most one calculated property.");
+    }
+
+    // If there is a flavor property, update the flavor in the base class
+    if (has_property(CdlPropertyId_Flavor)) {
+        CdlProperty_String flavor_property = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_Flavor));
+        CYG_ASSERTC(0 != flavor_property);
+        
+        std::string flavor_string = flavor_property->get_string();
+        CdlValueFlavor flavor;
+        // The property parsing code should have caught any problems already.
+        if (!Cdl::string_to_flavor(flavor_string, flavor)) {
+            CdlParse::report_error(interp, "", "Invalid flavor " + flavor_string);
+        } else {
+            value.set_flavor(flavor);
+        }
+
+        // If the flavor is "none" then the entity is not modifiable,
+        // and most of the properties do not make sense. However this
+        // is not enforced at parse-time: temporarily switching to
+        // flavor none may make sense during debugging.
+        // FIXME: no longer correct
+    }
+
+    // For boolean entities legal_values does not make much sense.
+    // In theory a legal_values property could be used to restrict
+    // the value to just true or just false, but the same effect
+    // can be achieved more sensibly with a "requires" property.
+    //
+    // check_proc is allowed, this can be used to check programatically
+    // that the current value is legal.
+    if (CdlValueFlavor_Bool == get_flavor()) {
+        if (has_property(CdlPropertyId_LegalValues)) {
+            CdlParse::report_error(interp, "", "The \"legal_values\" property is not applicable to boolean entities."); 
+        }
+    }
+
+    // default_value and calculated are mutually exclusive
+    if (has_property(CdlPropertyId_Calculated) && has_property(CdlPropertyId_DefaultValue)) {
+        CdlParse::report_error(interp, "", "The properties \"default_value\" and \"calculated\" cannot be used together.");
+    }
+
+#if 0
+    // Dialog is not mutually exclusive with entry_proc.
+    // Custom dialogs may not be supported, in which case it is likely that
+    // a text entry widget will be used and an entry_proc may well be
+    // applicable.
+    if (has_property(CdlPropertyId_Dialog) && has_property(CdlPropertyId_EntryProc)) {
+        CdlParse::report_error(interp, "", "The properties \"dialog\" and \"entry_proc\" cannot be used together.");
+    }
+#endif    
+
+    // All of the expressions may be invalid because of unresolved references,
+    // ditto for implements and for dialog. 
+    
+    CdlNodeBody::check_properties(interp);
+    
+    CYG_REPORT_RETURN();
+}
+
+//}}}
+//{{{  CdlValuable persistence support  
+
+// ----------------------------------------------------------------------------
+void
+CdlValuableBody::initialize_savefile_support(CdlToplevel toplevel, std::string major_command)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::initialize_savefile_support");
+    CYG_PRECONDITION_CLASSC(toplevel);
+    CYG_PRECONDITIONC("" != major_command);
+
+    toplevel->add_savefile_subcommand(major_command, "value_source", 0, &savefile_value_source_command);
+    toplevel->add_savefile_subcommand(major_command, "user_value",   0, &savefile_user_value_command);
+    toplevel->add_savefile_subcommand(major_command, "wizard_value", 0, &savefile_wizard_value_command);
+    toplevel->add_savefile_subcommand(major_command, "inferred_value", 0, &savefile_inferred_value_command);
+
+    CYG_REPORT_RETURN();
+}
+
+// ----------------------------------------------------------------------------
+// Is a savefile entry actually needed for this valuable? When performing
+// a minimal save there is no point in outputting valuables which have
+// a default value.
+bool
+CdlValuableBody::value_savefile_entry_needed() const
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlValuable::value_savefile_entry_needed", "result %d");
+    CYG_REPORT_FUNCARG1XV(this);
+    CYG_PRECONDITION_THISC();
+
+    bool result = false;
+
+    if (this->is_modifiable()) {
+        if (this->has_source(CdlValueSource_User) ||
+            this->has_source(CdlValueSource_Wizard) ||
+            this->has_source(CdlValueSource_Inferred)) {
+
+            result = true;
+        }
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// This utility is useful for outputting a particular value source
+
+static std::string one    = "1";     // Needed to avoid confusing the compiler
+static std::string zero   = "0";
+
+static std::string
+value_to_string(CdlValuable valuable, CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("value_to_string");
+
+    std::string data = "";
+    
+    switch(valuable->get_flavor()) {
+      case CdlValueFlavor_Bool :
+        data += (valuable->is_enabled(source) ? one : zero);
+        break;
+      case CdlValueFlavor_BoolData :
+        data += (valuable->is_enabled(source) ? one : zero) + " " +
+            CdlInterpreterBody::quote(valuable->get_value(source));
+        break;
+      case CdlValueFlavor_Data:
+        data += CdlInterpreterBody::quote(valuable->get_value(source));
+        break;
+      default:
+        CYG_FAIL("Invalid value flavor detected");
+        break;
+    }
+    return data;
+}
+
+// Another utility to figure out the expected value source, given which
+// sources are available.
+static CdlValueSource
+get_expected_source(CdlValuable valuable)
+{
+    CYG_REPORT_FUNCNAMETYPE("get_expected_source", "result %d");
+    CYG_REPORT_FUNCARG1XV(valuable);
+
+    CdlValueSource expected_source = CdlValueSource_Default;
+        
+    if (valuable->has_source(CdlValueSource_User)) {
+        expected_source = CdlValueSource_User;
+    } else if (valuable->has_source(CdlValueSource_Wizard)) {
+        expected_source = CdlValueSource_Wizard;
+    } else if (valuable->has_source(CdlValueSource_Inferred)) {
+        expected_source = CdlValueSource_Inferred;
+    }
+
+    CYG_REPORT_RETVAL((int) expected_source);
+    return expected_source;
+}
+
+// And another utility, to list the valuables listed in an expression.
+// e.g. for an expression of the form
+//
+//      requires (AAA + BBB) > CCC
+//
+// this would produce:
+//
+//      AAA == 1
+//      BBB == 2
+//      CCC == 0
+//
+// No indentation happens here, instead the calling code is assumed
+// to use multiline_comment()
+static std::string
+follow_expr_references(CdlProperty property, CdlExpression expr)
+{
+    CYG_REPORT_FUNCNAME("follow_expr_references");
+    CYG_REPORT_FUNCARG1XV(expr);
+    CYG_PRECONDITION_CLASSC(expr);
+
+    std::string    data = "";
+    CdlSimpleValue simple_value;
+    std::vector<CdlReference>::const_iterator ref_i;
+    
+    for (ref_i = expr->references.begin(); ref_i != expr->references.end(); ref_i++) {
+        const std::string& refname = ref_i->get_destination_name();
+        CdlNode refnode = ref_i->get_destination();
+        CdlValuable refvaluable = 0;
+        if (0 != refnode) {
+            refvaluable = dynamic_cast<CdlValuable>(refnode);
+        }
+        data += refname + " ";
+        if (0 == refvaluable) {
+            data += "(unknown) == 0";
+        } else {
+            CdlEvalContext context(0, refvaluable, property);
+            CdlSimpleValue::eval_valuable(context, refvaluable, simple_value);
+            data += "== " + CdlInterpreterBody::quote(simple_value.get_value());
+        }
+        data += '\n';
+    }
+    
+    CYG_REPORT_RETURN();
+    return data;
+}
+
+// ----------------------------------------------------------------------------
+
+void
+CdlValuableBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool modifiable, bool minimal)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::save");
+    CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(interp);
+
+    std::string data = "";
+    std::string indent_string = std::string(indentation, ' ');
+    std::string tmp_value     = "";
+    CdlSimpleValue simple_value;
+
+    // If performing a minimal save, the only fields of interest are the
+    // user value, the wizard value, the inferred value, and the value source.
+    // Not all of these need be present.
+    //
+    // Having two places where these fields get output is unfortunate,
+    // but the alternative is an awful lot of "if (minimal)" tests
+    // in the main code.
+    if (minimal) {
+        
+        if (modifiable) {
+            if (this->has_source(CdlValueSource_User)) {
+                data += indent_string + "user_value " + value_to_string(this, CdlValueSource_User) + "\n";
+            }
+            if (this->has_source(CdlValueSource_Wizard)) {
+                data += indent_string + "wizard_value " + value_to_string(this, CdlValueSource_Wizard) + "\n";
+            }
+            if (this->has_source(CdlValueSource_Inferred)) {
+                data += indent_string + "inferred_value " + value_to_string(this, CdlValueSource_Inferred) + "\n";
+            }
+            CdlValueSource expected_source = get_expected_source(this);
+            if (expected_source != this->get_source()) {
+                std::string current_source_string;
+                if (!Cdl::source_to_string(this->get_source(), current_source_string)) {
+                    CYG_FAIL("Invalid current value source detected");
+                }
+                data += indent_string + "value_source " + current_source_string + "\n";
+            }
+        }
+        
+    } else {
+    
+        // Right at the start, indicate whether or not this property is active.
+        if (!this->is_active()) {
+            data += indent_string + "# This option is not active\n";
+            // If the entity is inactive because the parent is inactive or disabled,
+            // say so here. This is in addition to any unsatisfied active_if
+            // conditions, which will be reported below.
+            CdlContainer parent = this->get_parent();
+            if (!parent->is_active()) {
+                data += indent_string + "# The parent " + parent->get_name() + " is not active\n";
+            }
+            CdlValuable tmp = dynamic_cast<CdlValuable>(parent);
+            if ((0 != tmp) && !tmp->is_enabled()) {
+                data += indent_string + "# The parent " + parent->get_name() + " is disabled\n";
+            }
+        }
+        if (this->has_active_if_conditions()) {
+            std::vector<CdlProperty_GoalExpression> active_if_conditions;
+            this->get_active_if_conditions(active_if_conditions);
+            std::vector<CdlProperty_GoalExpression>::const_iterator expr_i;
+            for (expr_i = active_if_conditions.begin(); expr_i != active_if_conditions.end(); expr_i++) {
+                data += indent_string + "# ActiveIf constraint: " +
+                    CdlInterpreterBody::extend_comment((*expr_i)->get_original_string(), indentation, 4) +
+                    '\n';
+
+                CdlExpression expr = (*expr_i)->get_expression();
+                data += CdlInterpreterBody::multiline_comment(follow_expr_references(*expr_i, expr), indentation, 4);
+                CdlEvalContext context(0, this, *expr_i);
+                bool active_if_value = false;
+                try {
+                    active_if_value = (*expr_i)->eval(context);
+                } catch(CdlEvalException e) {
+                    active_if_value = false;
+                } catch(std::bad_alloc) {
+                    throw;
+                }
+                data += indent_string + "#   --> " + (active_if_value ? one : zero) + "\n";
+            }
+        }
+        
+        // If there has been any information related to the active status,
+        // add a blank line before we start worrying about values.
+        if (0 < data.size()) {
+            data += '\n';
+        }
+
+        if (CdlValueFlavor_None == this->get_flavor()) {
+            data += indent_string + "# There is no associated value.\n";
+        } else if (this->has_property(CdlPropertyId_Calculated)) {
+            CdlProperty_Expression expr = this->get_calculated_expression();
+            data += indent_string + "# Calculated value: " +
+                CdlInterpreterBody::extend_comment(expr->get_original_string(), indentation, 4) + '\n';
+            data += CdlInterpreterBody::multiline_comment(follow_expr_references(expr, expr), indentation, 4);
+        } else if (!modifiable) {
+            data += indent_string + "# This value cannot be modified here.\n";
+        }
+         
+        // Output the flavor. This clutters up the savefile a bit.
+        // However it is necessary so that the user can distinguish
+        // between bool, booldata and data items
+        switch(this->get_flavor()) {
+          case CdlValueFlavor_Bool:
+            data += indent_string + "# Flavor: bool\n";
+            break;
+          case CdlValueFlavor_BoolData:
+            data += indent_string + "# Flavor: booldata\n";
+            break;
+          case CdlValueFlavor_Data:
+            data += indent_string + "# Flavor: data\n";
+            break;
+          default:
+            break;
+        }
+            
+        // If the value is not modifiable, just list the current value.
+        // This is not in a form that allows users to change it easily.
+        if (!modifiable) {
+            switch(this->get_flavor()) {
+              case CdlValueFlavor_None :
+                break;
+              case CdlValueFlavor_Bool :
+                data += indent_string + "# Current value: " + (this->is_enabled() ? one : zero) + '\n';
+                break;
+              case CdlValueFlavor_BoolData :
+                data += indent_string + "# Current value: " + (this->is_enabled() ? one : zero) + " " +
+                    CdlInterpreterBody::extend_comment(this->get_value(), indentation, 4) + '\n';
+                break;
+              case CdlValueFlavor_Data :
+                data += indent_string + "# Current_value: " +
+                    CdlInterpreterBody::extend_comment(this->get_value(), indentation, 4) + '\n';
+                break;
+              default:
+                break;
+            }
+        
+        } else if (CdlValueFlavor_None != this->get_flavor()) {
+
+            // If there is a user value, output it. Otherwise output
+            // a comment that allows users to edit the user value conveniently.
+            // It is assumed that the user will want a value similar to the
+            // default one, so that is provided as the starting point
+            if (this->has_source(CdlValueSource_User)) {
+                data += indent_string + "user_value " + value_to_string(this, CdlValueSource_User) + "\n";
+            } else {
+                data += indent_string + "# No user value, uncomment the following line to provide one.\n" +
+                    indent_string + "# user_value " +
+                    CdlInterpreterBody::extend_comment(value_to_string(this, CdlValueSource_Default), indentation, 0) + "\n";
+            }
+        
+            // Output a wizard value iff there is one. There is little point
+            // in letting users edit a wizard value, they should be running
+            // the wizard itself.
+            if (this->has_source(CdlValueSource_Wizard)) {
+                data += indent_string + "# The wizard value should not be edited directly.\n" +
+                    indent_string + "# Instead the wizard should be run again if necessary.\n";
+                data += indent_string + "wizard_value " + value_to_string(this, CdlValueSource_Wizard) + "\n";
+            }
+
+            // List the inferred value. This needs to be a command,
+            if (this->has_source(CdlValueSource_Inferred)) {
+                data += indent_string + "# The inferred value should not be edited directly.\n";
+                data += indent_string + "inferred_value " + value_to_string(this, CdlValueSource_Inferred) + "\n";
+            }
+        
+            // Output the value source iff it is unusual. If the current
+            // source is the highest priority one then there is no point
+            // in outputting a command, but a comment is usual. The value
+            // source needs to come after wizard and inferred values
+            std::string    current_source_string;
+            CdlValueSource expected_source = get_expected_source(this);
+            CdlValueSource current_source  = this->get_source();
+            if (!Cdl::source_to_string(current_source, current_source_string)) {
+                CYG_FAIL("Invalid current value source detected");
+            }
+            if (this->get_source() == expected_source) {
+                data += indent_string + "# value_source " + current_source_string + "\n";
+            } else {
+                data += indent_string + "value_source " + current_source_string + "\n";
+            }
+
+            // Always output the default value as a comment.
+            data += indent_string + "# Default value: ";
+            
+            // If there is no default_value expression or if the expression involves
+            // only constants, just output the current default value. Otherwise
+            // output both the expression and the value
+            CdlProperty prop = this->get_property(CdlPropertyId_DefaultValue);
+            CdlProperty_Expression expr = dynamic_cast<CdlProperty_Expression>(prop);
+            if ((0 == expr) || (0 == expr->references.size())) {
+                // There is no default_value expression, so just output the current value
+                data += CdlInterpreterBody::extend_comment(value_to_string(this, CdlValueSource_Default), indentation, 4)
+                    + "\n";
+            } else {
+                data += CdlInterpreterBody::extend_comment(expr->get_original_string(), indentation, 4) + "\n";
+                data += CdlInterpreterBody::multiline_comment(follow_expr_references(expr, expr), indentation, 4);
+                data += indent_string + "#   --> " +
+                    CdlInterpreterBody::extend_comment(value_to_string(this, CdlValueSource_Default), indentation, 4) + "\n";
+            }
+        }
+
+        // If there is a legal_values property, add the details.
+        if (this->has_property(CdlPropertyId_LegalValues)) {
+            CdlProperty_ListExpression lexpr = this->get_legal_values();
+            data += indent_string + "# Legal values: " +
+                CdlInterpreterBody::extend_comment(lexpr->get_original_string(), indentation, 4) + '\n';
+        
+            std::vector<CdlExpression>::const_iterator expr_i;
+            std::vector<std::pair<CdlExpression,CdlExpression> >::const_iterator ranges_i;
+            for (expr_i = lexpr->data.begin(); expr_i != lexpr->data.end(); expr_i++) {
+                data += CdlInterpreterBody::multiline_comment(follow_expr_references(lexpr, *expr_i), indentation, 4);
+            }
+            for (ranges_i = lexpr->ranges.begin(); ranges_i != lexpr->ranges.end(); ranges_i++) {
+                data += CdlInterpreterBody::multiline_comment(follow_expr_references(lexpr, ranges_i->first), indentation, 4);
+                data += CdlInterpreterBody::multiline_comment(follow_expr_references(lexpr, ranges_i->second), indentation, 4);
+            }
+        }
+    
+        // If there is a check_proc property, mention this.
+        if (this->has_property(CdlPropertyId_CheckProc)) {
+            data += indent_string + "# There is a check_proc routine that will check the value.\n";
+        }
+
+        // Output all requires properties
+        if (this->has_property(CdlPropertyId_Requires)) {
+            std::vector<CdlProperty_GoalExpression> requires_goals;
+            this->get_requires_goals(requires_goals);
+            std::vector<CdlProperty_GoalExpression>::const_iterator expr_i;
+            for (expr_i = requires_goals.begin(); expr_i != requires_goals.end(); expr_i++) {
+                data += indent_string + "# Requires: " +
+                    CdlInterpreterBody::extend_comment((*expr_i)->get_original_string(), indentation, 4) + "\n";
+
+                CdlExpression expr = (*expr_i)->get_expression();
+                data += CdlInterpreterBody::multiline_comment(follow_expr_references(*expr_i, expr), indentation, 4);
+                CdlEvalContext context(0, this, *expr_i);
+                bool active_if_value = false;
+                try {
+                    active_if_value = (*expr_i)->eval(context);
+                } catch(CdlEvalException e) {
+                    active_if_value = false;
+                } catch(std::bad_alloc) {
+                    throw;
+                }
+                data += indent_string + "#   --> " + (active_if_value ? one : zero) + "\n";
+            }
+        }
+
+        // Output all dependencies that other entities may have on this one.
+        const std::vector<CdlReferrer>& referrers = this->get_referrers();
+        if (0 != referrers.size()) {
+            data += '\n' + indent_string + "# The following properties are affected by this value\n";
+            std::vector<CdlReferrer>::const_iterator ref_i;
+            for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) {
+            
+                CdlNode source = ref_i->get_source();
+                CdlProperty source_prop = ref_i->get_source_property();
+                std::string prop_id = source_prop->get_property_name();
+            
+                if ((prop_id == CdlPropertyId_ActiveIf)     ||
+                    (prop_id == CdlPropertyId_Calculated)   ||
+                    (prop_id == CdlPropertyId_DefaultValue) ||
+                    (prop_id == CdlPropertyId_LegalValues)  ||
+                    (prop_id == CdlPropertyId_Requires)) {
+                
+                    data += indent_string + "# " + source->get_class_name() + " " + source->get_name() + "\n";
+                    data += indent_string + "#     " + prop_id + ": ";
+                    if ((prop_id == CdlPropertyId_Calculated) || (prop_id == CdlPropertyId_DefaultValue)) {
+                        CdlProperty_Expression expr = dynamic_cast<CdlProperty_Expression>(source_prop);
+                        CYG_ASSERT_CLASSC(expr);
+                        data += CdlInterpreterBody::extend_comment(expr->get_original_string(), indentation, 4);
+                    } else if (prop_id == CdlPropertyId_LegalValues) {
+                        CdlProperty_ListExpression lexpr = dynamic_cast<CdlProperty_ListExpression>(source_prop);
+                        CYG_ASSERT_CLASSC(lexpr);
+                        data += CdlInterpreterBody::extend_comment(lexpr->get_original_string(), indentation, 4);
+                    } else if ((prop_id == CdlPropertyId_ActiveIf) || (prop_id == CdlPropertyId_Requires)) {
+                        CdlProperty_GoalExpression gexpr = dynamic_cast<CdlProperty_GoalExpression>(source_prop);
+                        CYG_ASSERT_CLASSC(gexpr);
+                        data += CdlInterpreterBody::extend_comment(gexpr->get_original_string(), indentation, 4);
+                    }
+                    data += '\n';
+                }
+            }
+        }
+    }
+
+    interp->write_data(chan, data);
+    
+    CYG_REPORT_RETURN();
+}
+
+int
+CdlValuableBody::savefile_value_source_command(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::savefile_value_source_command");
+    CYG_REPORT_FUNCARG2XV(interp, argc);
+    CYG_PRECONDITION_CLASSC(interp);
+
+    CdlValuable valuable = dynamic_cast<CdlValuable>(interp->get_node());
+    CYG_ASSERT_CLASSC(valuable);
+    CdlTransaction transaction = interp->get_transaction();
+    CYG_ASSERT_CLASSC(transaction);
+
+    CdlValueSource source = CdlValueSource_Invalid;
+    if ((2 != argc) || !Cdl::string_to_source(argv[1], source) || !valuable->has_source(transaction, source)) {
+        std::string msg = "Invalid value_source command for ";
+        msg += valuable->get_class_name() + " " + valuable->get_name() + "\n";
+        if (CdlValueSource_Invalid == source) {
+            msg += "Expecting one argument, which should \"user\", \"wizard\", \"inferred\" or \"default\"";
+        } else {
+            msg += "The specified value source is not valid.";
+        }
+        CdlParse::report_error(interp, "", msg);
+    } else {
+        valuable->set_source(transaction, source);
+    }
+    
+    return TCL_OK;
+}
+
+int
+CdlValuableBody::savefile_xxx_value_command(CdlInterpreter interp, int argc, const char* argv[], CdlValueSource source)
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::savefile_xxx_value_command");
+    CYG_REPORT_FUNCARG3XV(interp, argc, source);
+    CYG_PRECONDITION_CLASSC(interp);
+
+    CdlValuable valuable = dynamic_cast<CdlValuable>(interp->get_node());
+    CYG_ASSERT_CLASSC(valuable);
+    CdlTransaction transact = interp->get_transaction();
+    CYG_ASSERT_CLASSC(transact);
+
+    bool error = false;
+    bool warn  = false;
+    std::string msg = "";
+    if (CdlValueFlavor_None == valuable->get_flavor()) {
+        msg = "Options with flavor \"none\" cannot be modified.";
+        error = true;
+    } else if (!valuable->is_modifiable()) {
+        msg = "This option is not user-modifiable.";
+        error = true;
+    } else {
+        switch(valuable->get_flavor()) {
+          case CdlValueFlavor_Bool :
+              if (2 != argc) {
+                  msg = "Invalid boolean value, expecting 0 or 1";
+                  error = true;
+              } else {
+                  bool x;
+                  Cdl::string_to_bool(argv[1], x);
+                  valuable->set_enabled(transact, x, source);
+              }
+              break;
+          case CdlValueFlavor_Data :
+              if (2 != argc) {
+                  msg = "Invalid data value, expecting a single string";
+                  error = true;
+              } else {
+                  valuable->set_value(transact, argv[1], source);
+              }
+              break;
+          case CdlValueFlavor_BoolData:
+              if (3 != argc) {
+                  msg = "Invalid booldata value, expecting a boolean followed by a string";
+                  error = true;
+              } else {
+                  bool x;
+                  Cdl::string_to_bool(argv[1], x);
+                  valuable->set_enabled_and_value(transact, x, argv[2], source);
+              }
+              break;
+          default:
+            CYG_FAIL("Invalid value flavor detected");
+            break;
+        }
+    }
+
+    if (error || warn) {
+        msg = std::string("Invalid value command for ") + valuable->get_class_name() + " " + valuable->get_name() + "\n"
+            + msg;
+        if (error) {
+            CdlParse::report_error(interp, "", msg);
+        } else {
+            CdlParse::report_warning(interp, "", msg);
+        }
+    }
+
+    return TCL_OK;
+}
+
+int
+CdlValuableBody::savefile_user_value_command(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::savefile_user_value_command");
+    int result = CdlValuableBody::savefile_xxx_value_command(interp, argc, argv, CdlValueSource_User);
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+int
+CdlValuableBody::savefile_wizard_value_command(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::savefile_wizard_value_command");
+    int result = CdlValuableBody::savefile_xxx_value_command(interp, argc, argv, CdlValueSource_Wizard);
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+int
+CdlValuableBody::savefile_inferred_value_command(CdlInterpreter interp, int argc, const char* argv[])
+{
+    CYG_REPORT_FUNCNAME("CdlValuable::savefile_inferred_value_command");
+    int result = CdlValuableBody::savefile_xxx_value_command(interp, argc, argv, CdlValueSource_Inferred);
+    CYG_REPORT_RETURN();
+    return result;
+}
+
+//}}}