diff host/libcdl/infer.cxx @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children 6736c52df507
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/host/libcdl/infer.cxx
@@ -0,0 +1,1283 @@
+//{{{  Banner                                   
+
+//============================================================================
+//
+//      infer.cxx
+//
+//      Inference for common conflicts.
+//
+//============================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// ----------------------------------------------------------------------------
+// Copyright (C) 1999, 2000 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/11/1
+// Version:     0.01
+//
+//####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>
+
+//}}}
+
+//{{{  Illegal value resolution                 
+
+// ----------------------------------------------------------------------------
+// This is not yet implemented.
+
+bool
+CdlConflict_IllegalValueBody::inner_resolve(CdlTransaction transaction, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("CdlConflict_IllegalValue::inner_resolve", "result %d");
+    CYG_REPORT_FUNCARG3XV(this, transaction, level);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    CYG_UNUSED_PARAM(CdlTransaction, transaction);
+    
+    CYG_REPORT_RETVAL(false);
+    return false;
+}
+
+//}}}
+//{{{  Requires resolution                      
+
+//{{{  Description                              
+
+// ----------------------------------------------------------------------------
+// The entry point for this code is
+// CdlConflict_RequiresBody::resolve(). "this" is a requires conflict
+// that needs to be resolved, if possible. There are twos argument: a
+// sub-transaction, which should be filled in with the solution if
+// possible; and a recursion level indicator, 0 if this is a top-level
+// inference engine invocation rather than a recursive one. There are
+// additional static parameters inference_recursion_limit and
+// inference_override which control details of the inference process.
+//
+// There are three ways in which a "requires" conflict can be
+// eliminated:
+// 1) change the terms in the expression so that the "requires" property
+//    evaluates to true.
+// 2) disable the source node so that the "requires" property is ignored.
+// 3) or make the source node inactive, with the same effect.
+//
+// The 1st approach is generally better, but may involve changing user
+// settings in situations where the 2nd or 3rd would be ok. Anyway
+// there is clearly a need for some OR logic right from the start.
+//
+// As an example of what is involved in an inference, consider the
+// simple case of a "requires XXX" property. This constraint may not
+// be satisfied because XXX is disabled,  because XXX is inactive,
+// or both.
+//
+// Assume for simplicity that XXX is already active. The inference
+// engine can now figure out that XXX must be enabled (it must be
+// of type bool or booldata, or else the conflict would not have
+// arisen). This is achieved by creating a sub-transaction,
+// enabling XXX in that sub-transaction, propagating the
+// sub-transaction and performing further inference. The inference
+// is successfull if no new conflicts are introduced.
+//
+// However, even if a solution is found it is not necessarily
+// acceptable without user confirmation, subject to
+// inference_override. This is handled in part by the transaction
+// class itself, in the resolve() and user_confirmation_required()
+// members. In cases where the inference engine can choose between
+// several alternatives it needs to consider this issue for each one.
+// 
+// There are a number of key entry points:
+//
+// 1) bool infer_make_active(CdlTransaction, CdlValuable, level)
+//    Do whatever it takes to make the valuable active in
+//    a clean sub-transaction, if possible.
+// 2) bool infer_make_inactive(CdlTransaction, CdlValuable, level)
+//    Do whatever it takes to make the valuable inactive.
+// 3) bool infer_set_valuable_value(CdlTransaction, CdlValuable, CdlSimpleValue&, level)
+//    Try to set the valuable to the specified value, taking into
+//    account the different flavors.
+// 4) bool infer_set_valuable_bool(CdlTransaction, CdlValuable, bool, level)
+//    Similar to (3) but deals with the boolean aspect of the valuable
+//    rather than the data part.
+// 5) bool infer_subexpr(CdlTransaction, CdlExpression, int index, CdlSimpleValue& goal, level)
+//    Process a sub-expression and try to make it evaluate to the
+//    goal.
+// 6) bool infer_subexpr_bool(CdlTransaction, CdlExpression, int index, bool goal, level)
+//    Ditto but only deal with boolean goals. If the expression starts to
+//    involve arithmetic etc. then we need to move to infer_subexpr()
+//
+//    As might be expected, the sub-expression handlers contain a big
+//    switch statement and calls into various auxiliary functions when
+//    necessary.
+//
+// Forward declarations are needed to allow for active_if properties and
+// the like.
+//
+// For convenience the various entry points check whether or not the
+// desired condition is already satisfied.
+
+//}}}
+//{{{  Forward declarations                     
+
+// ----------------------------------------------------------------------------
+static bool infer_make_active(CdlTransaction, CdlNode, int);
+static bool infer_make_inactive(CdlTransaction, CdlNode, int);
+static bool infer_set_valuable_value(CdlTransaction, CdlValuable, CdlSimpleValue&, int);
+static bool infer_set_valuable_bool(CdlTransaction, CdlValuable, bool, int);
+static bool infer_subexpr_value(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int);
+static bool infer_subexpr_bool(CdlTransaction, CdlExpression, unsigned int, bool, int);
+
+//}}}
+//{{{  infer_make_active()                      
+
+// ----------------------------------------------------------------------------
+// Making a node active. This requires the following conditions to be
+// satisfied:
+// 1) the parent must be made active
+// 2) if the parent has flavor bool or booldata, it must be enabled
+// 3) any active_if properties 
+
+static bool
+infer_make_active(CdlTransaction transaction, CdlNode node, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_make_active", "result %d");
+    CYG_REPORT_FUNCARG3XV(transaction, node, level);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(node);
+
+    bool result = false;
+    if (transaction->is_active(node)) {
+        result = true;
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+    
+    CdlContainer parent = node->get_parent();
+    CYG_ASSERT_CLASSC(parent);
+    if (!transaction->is_active(parent)) {
+        if (!infer_make_active(transaction, node, level)) {
+            CYG_REPORT_RETVAL(result);
+            return result;
+        }
+    }
+    // The parent is now active. Does it have to be enabled as well?
+    CdlValuable parent_valuable = dynamic_cast<CdlValuable>(static_cast<CdlNode>(parent));
+    if (0 != parent_valuable) {
+        CdlValueFlavor flavor = parent_valuable->get_flavor();
+        if (((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) &&
+            !parent_valuable->is_enabled(transaction)) {
+            if (!infer_set_valuable_bool(transaction, parent_valuable, true, level)) {
+                CYG_REPORT_RETVAL(result);
+                return result;
+            }
+        }
+    }
+    // The parent is now active and enabled. Are there any active_if properties to worry about?
+    CdlValuable valuable = dynamic_cast<CdlValuable>(node);
+    if (0 != valuable) {
+        std::vector<CdlProperty_GoalExpression> active_if_goals;
+        std::vector<CdlProperty_GoalExpression>::iterator goal_i;
+        valuable->get_active_if_conditions(active_if_goals);
+        for (goal_i = active_if_goals.begin(); goal_i != active_if_goals.end(); goal_i++) {
+            
+            CdlEvalContext context(transaction, valuable, *goal_i);
+            try {
+                if (!(*goal_i)->eval(context)) {
+                    CdlExpression expr = (*goal_i)->get_expression();
+                    if (!infer_subexpr_bool(transaction, expr, expr->first_subexpression, true, level)) {
+                        CYG_REPORT_RETVAL(result);
+                        return result;
+                    }
+                }
+            } catch(...) {
+                CYG_REPORT_RETVAL(result);
+                return result;
+            }
+        }
+        
+    }
+    
+    result = transaction->is_active(node);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_make_inactive()                    
+
+// ----------------------------------------------------------------------------
+// Making a node inactive can be done in three ways:
+// 1) if the parent is boolean, try disabling it
+// 2) otherwise if the parent can be made inactive, that will do
+//    fine.
+// 3) if there are any active_if properties, they could be considered
+//    as well. For now this possibility is ignored.
+
+static bool
+infer_make_inactive(CdlTransaction transaction, CdlNode node, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_make_inactive", "result %d");
+    CYG_REPORT_FUNCARG3XV(transaction, node, level);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(node);
+
+    bool result = false;
+    if (!transaction->is_active(node)) {
+        result = true;
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+
+    CdlContainer parent = node->get_parent();
+    if (0 == parent) {
+        // No point in trying to disable the entire configuration.
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+    
+    CdlValuable  parent_valuable = dynamic_cast<CdlValuable>(parent);
+    if (0 != parent_valuable) {
+        CdlValueFlavor flavor = parent_valuable->get_flavor();
+        if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
+            // Since the current node is active the parent must currently be enabled.
+            // A sub-transaction is needed because an alternative approach is
+            // possible later on.
+            CdlTransaction subtransaction = transaction->make(transaction->get_conflict());
+            if (infer_set_valuable_bool(subtransaction, parent_valuable, false, level)) {
+                subtransaction->commit();
+                delete subtransaction;
+                result = true;
+                CYG_REPORT_RETVAL(result);
+                return result;
+            } else {
+                subtransaction->cancel();
+                delete subtransaction;
+            }
+        }
+    }
+
+    // It is not possible to disable the parent. How about making it inactive?
+    if (infer_make_inactive(transaction, parent, level)) {
+        result = true;
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+
+    // For now do not try to mess about with active_if conditions.
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_set_valuable_value()               
+
+// ----------------------------------------------------------------------------
+// Deal with the value part of a valuable. The valuable is known to exist
+// and be active, so this code only deals with the actual value part.
+
+static bool
+infer_set_valuable_value(CdlTransaction transaction, CdlValuable valuable, CdlSimpleValue& goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_set_valuable_value", "result %d");
+    CYG_REPORT_FUNCARG3XV(transaction, valuable, level);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(valuable);
+    CYG_PRECONDITIONC(transaction->is_active(valuable));
+
+    bool result = false;
+
+    const CdlValue& current_value = transaction->get_whole_value(valuable);
+    CdlValueFlavor  flavor        = current_value.get_flavor();
+
+    // For now only deal with data items. Interpreter a simple value
+    // so that it is relevant for booleans etc. can come later.
+    if (CdlValueFlavor_Data == flavor) {
+        // Now check whether or not the valuable already has the desired value
+        if (goal == current_value.get_simple_value()) {
+            result = true;
+        } else {
+
+            if (valuable->is_modifiable() &&
+                (0 == dynamic_cast<CdlLoadable>(valuable)) &&
+                !transaction->changed_by_user(valuable)) {
+    
+                // Make the change, propagate, and perform further resolution.
+                valuable->set_value(transaction, goal, CdlValueSource_Inferred);
+                valuable->set_source(transaction, CdlValueSource_Inferred);
+                result = transaction->resolve_recursion(level);
+            }
+        }
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_set_valuable_bool()                
+
+// ----------------------------------------------------------------------------
+// Deal with the boolean part of a valuable. It is assumed that active vs.
+// inactive is dealt with elsewhere so this code only needs to worry
+// about the valuable itself.
+
+static bool
+infer_set_valuable_bool(CdlTransaction transaction, CdlValuable valuable, bool goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_set_valuable_bool", "result %d");
+    CYG_REPORT_FUNCARG4XV(transaction, valuable, goal, level);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(valuable);
+
+    bool result = false;
+
+    // Examine the current flavor. If None or Data then the valuable
+    // is always enabled. If BoolData or Boolean then the condition
+    // may be satisfied already, otherwise an attempt must be made
+    // to change the value and see what happens.
+    CdlValueFlavor flavor = valuable->get_flavor();
+    if (CdlValueFlavor_None == flavor) {
+        if (goal) {
+            result = true;
+        }
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+
+    if (CdlValueFlavor_Data == flavor) {
+        std::string value = valuable->get_value(transaction);
+        if (goal) {
+            if (("" != value) && ("0" != value)) {
+                result = true;
+            }
+        } else {
+            if (("" == value) || ("0" == value)) {
+                result = true;
+            }
+        }
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+    
+    CYG_ASSERTC((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor));
+    bool enabled = valuable->is_enabled(transaction);
+    if (enabled == goal) {
+        result = true;
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+
+    // enabled != goal, and we have a boolean or booldata item.
+    // Before we actually try making any changes, is this sensible?
+    if (!valuable->is_modifiable() ||
+        (0 != dynamic_cast<CdlLoadable>(valuable)) ||
+        transaction->changed_by_user(valuable)) {
+
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+    // If we are about to disable a container, better check that this would
+    // not annoy the user either
+    if (!goal) {
+        CdlContainer container = dynamic_cast<CdlContainer>(valuable);
+        if ((0 != container) && transaction->subnode_changed_by_user(container)) {
+            CYG_REPORT_RETVAL(result);
+            return result;
+        }
+    }
+    
+    // Try to change the state, propagate, and perform further resolution.
+    valuable->set_enabled(transaction, goal, CdlValueSource_Inferred);
+    valuable->set_source(transaction, CdlValueSource_Inferred);
+    result = transaction->resolve_recursion(level);
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_subexpr()                          
+
+//{{{  infer_handle_reference()                 
+
+// ----------------------------------------------------------------------------
+// We are processing an expression and have reached a point where we
+// need <reference>, !<reference> or <reference>==<value>. The
+// reference may currently be unbound, in which case 0 is the only
+// goal that can be satisfied. If the reference is bound then it may
+// be possible to satisfy the goal by setting the value. In addition
+// it is necessary to worry about active vs. inactive state.
+
+static bool
+infer_handle_reference_bool(CdlTransaction transaction, CdlValuable valuable, bool goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_reference_bool", "result %d");
+    CYG_REPORT_FUNCARG4XV(transaction, valuable, goal, level);
+
+    bool result = false;
+    if (0 == valuable) {
+        if (!goal) {
+            result = true;
+        }
+        CYG_REPORT_RETVAL(result);
+        return result;
+    }
+
+    // If the valuable should evaluate to true then it must be both active
+    // and be either enabled or have a non-zero value.
+    if (goal) {
+        if (!transaction->is_active(valuable)) {
+            if (!infer_make_active(transaction, valuable, level)) {
+                CYG_REPORT_RETVAL(result);
+                return result;
+            }
+        }
+        if (infer_set_valuable_bool(transaction, valuable, true, level)) {
+            result = true;
+        }
+
+    } else {
+        // If the valuable should evaluate to false then it must be either
+        // inactive or it must be disabled or have a zero value.
+        if (!transaction->is_active(valuable)) {
+            // The goal is already satisfied, no need to proceed
+            result = true;
+            CYG_REPORT_RETVAL(result);
+            return result;
+        }
+        
+        // There is a choice to be made so two sub-transactions are
+        // needed. Disabling is generally preferred to making inactive.
+        CdlTransaction value_transaction    = transaction->make(transaction->get_conflict());
+        CdlTransaction inactive_transaction = 0;
+        bool value_result = infer_set_valuable_bool(value_transaction, valuable, false, level);
+        if (value_result && !value_transaction->user_confirmation_required()) {
+            value_transaction->commit();
+            delete value_transaction;
+            value_transaction = 0;
+            result = true;
+            CYG_REPORT_RETVAL(result);
+            return result;
+        }
+
+        inactive_transaction = transaction->make(transaction->get_conflict());
+        bool inactive_result = infer_make_inactive(inactive_transaction, valuable, level);
+        if (!inactive_result) {
+            if (value_result) {
+                // Changing the value is the only solution.
+                inactive_transaction->cancel();
+                value_transaction->commit();
+                result = true;
+            } else {
+                inactive_transaction->cancel();
+                value_transaction->cancel();
+                result = false;
+            }
+        } else {
+            if (!value_result) {
+                // Making the valuable inactive is the only solution.
+                value_transaction->cancel();
+                inactive_transaction->commit();
+                result = true;
+            } else if (!inactive_transaction->user_confirmation_required()) {
+                // Disabling the valuable would require user confirmation, making it inactive does not
+                value_transaction->cancel();
+                inactive_transaction->commit();
+                result = true;
+            } else {
+                // Both approaches are valid but would require user confirmation.
+                // Pick the preferred one.
+                if (value_transaction->is_preferable_to(inactive_transaction)) {
+                    inactive_transaction->cancel();
+                    value_transaction->commit();
+                    result = true;
+                } else {
+                    value_transaction->cancel();
+                    inactive_transaction->commit();
+                    result = true;
+                }
+            }
+        }
+
+        delete value_transaction;
+        delete inactive_transaction;
+        value_transaction = 0;
+        inactive_transaction = 0;
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+// ----------------------------------------------------------------------------
+// Try to set a valuable to a particular value. Of course the reference
+// may not be bound yet.
+//
+// First check whether or not the valuable is currently active. If it is
+// inactive and the goal is 0 then we have succeeded. If it is active and
+// the goal is 0 then we could try to make the valuable inactive, but
+// this possibility is ignored for now in case it leads to unexpected
+// behaviour. If it is active then we try to set the value, using
+// infer_set_valuable_value().
+
+static bool
+infer_handle_reference_value(CdlTransaction transaction, CdlValuable valuable, CdlSimpleValue& goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_reference", "result %d");
+    CYG_REPORT_FUNCARG3XV(transaction, valuable, level);
+
+    bool result = false;
+
+    if (0 == valuable) {
+        if (goal == (cdl_int) 0) {
+            result = true;
+        }
+    } else {
+    
+        bool active = transaction->is_active(valuable);
+        if (!active) {
+            if (goal == (cdl_int) 0) {
+                result = true;
+            }
+        } else {
+            result = infer_set_valuable_value(transaction, valuable, goal, level);
+        }
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_handle_string_constant()           
+
+// ----------------------------------------------------------------------------
+// Somewhere in the expression processing we have encountered a string
+// constant. The expression cannot be changed, so either the goal matches
+// the constant or it does not.
+static bool
+infer_handle_string_constant_bool(CdlSimpleValue& constant, bool goal)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_string_constant_bool", "result %d");
+    bool result = false;
+
+    if (goal) {
+        if (("" != constant.get_value()) && ("0" != constant.get_value())) {
+            result = true;
+        }
+    } else {
+        if (("" == constant.get_value()) || ("0" == constant.get_value())) {
+            result = true;
+        }
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+static bool
+infer_handle_string_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_string_constant_value", "result %d");
+    bool result = false;
+
+    if (constant.get_value() == goal.get_value()) {
+        result = true;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_handle_integer_constant()          
+
+// ----------------------------------------------------------------------------
+// Integers are also fairly straightforward.
+static bool
+infer_handle_integer_constant_bool(CdlSimpleValue& constant, bool goal)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_integer_constant_bool", "result %d");
+    CYG_PRECONDITIONC(constant.has_integer_value());
+    bool result = false;
+
+    if (goal) {
+        if (0 != constant.get_integer_value()) {
+            result = true;
+        }
+    } else {
+        if (0 == constant.get_integer_value()) {
+            result = true;
+        }
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+static bool
+infer_handle_integer_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_integer_constant_value", "result %d");
+    CYG_PRECONDITIONC(constant.has_integer_value());
+    bool result = false;
+
+    if (goal.has_integer_value() && (constant.get_integer_value() == goal.get_integer_value())) {
+        result = true;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_handle_double_constant()           
+
+// ----------------------------------------------------------------------------
+// Doubles are also straightforward, except than an exact comparision may
+// be too strict. There is not a lot that can be done about this right now.
+// Future enhancements to CDL may support tolerances.
+static bool
+infer_handle_double_constant_bool(CdlSimpleValue& constant, bool goal)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_double_constant_bool", "result %d");
+    CYG_PRECONDITIONC(constant.has_double_value());
+    bool result = false;
+
+    if (goal) {
+        if (0.0 != constant.get_double_value()) {
+            result = true;
+        }
+    } else {
+        if (0.0 == constant.get_double_value()) {
+            result = true;
+        }
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+static bool
+infer_handle_double_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_double_constant_value", "result %d");
+    CYG_PRECONDITIONC(constant.has_double_value());
+    bool result = false;
+
+    if (goal.has_double_value() && (constant.get_double_value() == goal.get_double_value())) {
+        result = true;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_handle_logical_NOT()               
+
+// ----------------------------------------------------------------------------
+// Logical not simply involves inverting the goal and then trying to infer
+// the rest of the sub-expression. There is little point in touching
+// the other arguments.
+static bool
+infer_handle_logical_NOT_bool(CdlTransaction transaction, CdlExpression expr, unsigned int index, bool goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_logical_NOT_bool", "result %d");
+
+    bool result = infer_subexpr_bool(transaction, expr, index, !goal, level);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+static bool
+infer_handle_logical_NOT_value(CdlTransaction transaction, CdlExpression expr, unsigned int index,
+                               CdlSimpleValue& goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_logical_NOT_value", "result %d");
+
+    bool new_goal = false;
+    if (("0" == goal.get_value()) || ("" == goal.get_value())) {
+        new_goal = true;
+    }
+
+    bool result = infer_subexpr_bool(transaction, expr, index, new_goal, level);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_handle_AND()                       
+
+// ----------------------------------------------------------------------------
+// Depending on the goal, we want either both sides of the AND to evaluate to
+// true, or we want one of the sides to evaluate to false.
+
+static bool
+infer_handle_AND_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+                      bool goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_AND_bool", "result %d");
+    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(expr);
+    CYG_PRECONDITIONC(lhs != rhs);
+
+    bool result = false;
+    
+    if (goal) {
+        // Both sides must be true in the same transaction, in case
+        // the solutions overlap in conflicting ways.
+        // NOTE: this leaves the transaction argument in an indeterminate
+        // state. Care has to be taken in the calling code.
+        if (infer_subexpr_bool(transaction, expr, lhs, true, level) &&
+            infer_subexpr_bool(transaction, expr, rhs, true, level)) {
+            result = true;
+        }
+    } else {
+        // We need to try out both sides of the OR and see which one is preferable.
+        // An optimization would be to only try the LHS, but trying both allows
+        // for a more informed choice.
+        CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
+        CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
+        bool lhs_result = infer_subexpr_bool(lhs_transaction, expr, lhs, false, level);
+        bool rhs_result = infer_subexpr_bool(rhs_transaction, expr, rhs, false, level);
+
+        if (lhs_result && !rhs_result) {
+            // Only the lhs succeeded.
+            rhs_transaction->cancel();
+            lhs_transaction->commit();
+            result = true;
+        } else if (!lhs_result && rhs_result) {
+            // Only the rhs succeeded.
+            lhs_transaction->cancel();
+            rhs_transaction->commit();
+            result = true;
+        } else if (lhs_result && rhs_result) {
+            // Both sides succeeded. Next check for user_confirmation.
+            bool lhs_confirm_needed = lhs_transaction->user_confirmation_required();
+            bool rhs_confirm_needed = rhs_transaction->user_confirmation_required();
+            if (lhs_confirm_needed && !rhs_confirm_needed) {
+                lhs_transaction->cancel();
+                rhs_transaction->commit();
+                result = true;
+            } else if (!lhs_confirm_needed && rhs_confirm_needed) {
+                rhs_transaction->cancel();
+                lhs_transaction->commit();
+                result = true;
+            } else {
+                // Neither or both of the two sides need user confirmation, so they
+                // are equal in that respect
+                if (lhs_transaction->is_preferable_to(rhs_transaction)) {
+                    rhs_transaction->cancel();
+                    lhs_transaction->commit();
+                    result = true;
+                } else {
+                    lhs_transaction->cancel();
+                    rhs_transaction->commit();
+                    result = true;
+                }
+            }
+        } else {
+            // Neither side succeeded.
+            lhs_transaction->cancel();
+            rhs_transaction->cancel();
+        }
+
+        // Zero or one of these transactions will have been committed,
+        // neither is still necessary.
+        delete lhs_transaction;
+        delete rhs_transaction;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+static bool
+infer_handle_AND_value(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+                       CdlSimpleValue& goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_AND_value", "result %d");
+
+    bool new_goal = true;
+    if (("0" == goal.get_value()) || ("" == goal.get_value())) {
+        new_goal = false;
+    }
+    bool result = infer_handle_AND_bool(transaction, expr, lhs, rhs, new_goal, level);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_handle_OR()                        
+
+// ----------------------------------------------------------------------------
+// The support for OR logic is much the same as for AND
+
+static bool
+infer_handle_OR_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+                     bool goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_OR_bool", "result %d");
+    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(expr);
+    CYG_PRECONDITIONC(lhs != rhs);
+
+
+    bool result = false;
+    
+    if (goal) {
+        // We need to try out both sides of the OR and see which one is preferable.
+        // An optimization would be to only try the LHS, but trying both allows
+        // for a more informed choice.
+        CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
+        CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
+        bool lhs_result = infer_subexpr_bool(lhs_transaction, expr, lhs, true, level);
+        bool rhs_result = infer_subexpr_bool(rhs_transaction, expr, rhs, true, level);
+
+        if (lhs_result && !rhs_result) {
+            // Only the lhs succeeded.
+            rhs_transaction->cancel();
+            lhs_transaction->commit();
+            result = true;
+        } else if (!lhs_result && rhs_result) {
+            // Only the rhs succeeded.
+            lhs_transaction->cancel();
+            rhs_transaction->commit();
+            result = true;
+        } else if (lhs_result && rhs_result) {
+            // Both sides succeeded. Next check for user_confirmation.
+            bool lhs_confirm_needed = lhs_transaction->user_confirmation_required();
+            bool rhs_confirm_needed = rhs_transaction->user_confirmation_required();
+            if (lhs_confirm_needed && !rhs_confirm_needed) {
+                lhs_transaction->cancel();
+                rhs_transaction->commit();
+                result = true;
+            } else if (!lhs_confirm_needed && rhs_confirm_needed) {
+                rhs_transaction->cancel();
+                lhs_transaction->commit();
+                result = true;
+            } else {
+                // Neither or both of the two sides need user confirmation, so they
+                // are equal in that respect
+                if (lhs_transaction->is_preferable_to(rhs_transaction)) {
+                    rhs_transaction->cancel();
+                    lhs_transaction->commit();
+                    result = true;
+                } else {
+                    lhs_transaction->cancel();
+                    rhs_transaction->commit();
+                    result = true;
+                }
+            }
+        } else {
+            // Neither side succeeded
+            lhs_transaction->cancel();
+            rhs_transaction->cancel();
+        }
+        
+        // Zero or one of these transactions will have been committed,
+        // neither is still necessary.
+        delete lhs_transaction;
+        delete rhs_transaction;
+    } else {
+        
+        // !(A || B) -> !A && !B
+        if (infer_subexpr_bool(transaction, expr, lhs, false, level) &&
+            infer_subexpr_bool(transaction, expr, rhs, false, level)) {
+            result = true;
+        }
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+static bool
+infer_handle_OR_value(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+                CdlSimpleValue& goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_OR_value", "result %d");
+
+    bool new_goal = true;
+    if (("0" == goal.get_value()) || ("" == goal.get_value())) {
+        new_goal = false;
+    }
+    bool result = infer_handle_OR_bool(transaction, expr, lhs, rhs, new_goal, level);
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_handle_Equal()                     
+
+// ----------------------------------------------------------------------------
+// Handle expressions of the form A == B. If B is constant then this
+// corresponds to an infer_subexpr_value() for A. Alternatively if
+// A is constant then this corresponds to an infer_subexpr_value() for B.
+//
+// There are other possibilities. If B has a calculated value then that
+// could be treated as if it were a constant. We could try two
+// sub-transactions, either setting A to the current value of subexpr B,
+// or vice versa. And so on.
+
+static bool
+infer_handle_equal_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+                        bool goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_handle_equal_bool", "result %d");
+    CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(expr);
+    CYG_PRECONDITIONC(lhs != rhs);
+
+    bool result = false;
+    
+    CdlSubexpression& lhs_subexpr = expr->sub_expressions[lhs];
+    CdlSubexpression& rhs_subexpr = expr->sub_expressions[rhs];
+    
+    // For now we can only deal with equality, not inequality.
+    if (goal) {
+        if ((CdlExprOp_StringConstant  == lhs_subexpr.op) ||
+            (CdlExprOp_IntegerConstant == lhs_subexpr.op) ||
+            (CdlExprOp_DoubleConstant  == lhs_subexpr.op)) {
+
+            result = infer_subexpr_value(transaction, expr, rhs, lhs_subexpr.constants, level);
+                
+        } else if ((CdlExprOp_StringConstant  == rhs_subexpr.op) ||
+                   (CdlExprOp_IntegerConstant == rhs_subexpr.op) ||
+                   (CdlExprOp_DoubleConstant  == rhs_subexpr.op)) {
+
+            result = infer_subexpr_value(transaction, expr, lhs, rhs_subexpr.constants, level);
+        }
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_subexpr_bool()                     
+
+// ----------------------------------------------------------------------------
+static bool
+infer_subexpr_bool(CdlTransaction transaction, CdlExpression expr, unsigned int index, bool goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_subexpr_bool", "result %d");
+    CYG_REPORT_FUNCARG5XV(transaction, expr, index, goal, level);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(expr);
+    CYG_PRECONDITIONC((0 <= index) && (index < expr->sub_expressions.size()));
+    
+    bool result = false;
+    CdlSubexpression& subexpr = expr->sub_expressions[index];
+
+    switch(subexpr.op) {
+        
+      case CdlExprOp_Reference :
+          // The most common case. Follow the reference, and call the appropriate function.
+          // Note that the reference may be unbound.
+          {
+              CdlNode node = expr->references[subexpr.reference_index].get_destination();
+              CdlValuable valuable = 0;
+              if (0 != node) {
+                  valuable = dynamic_cast<CdlValuable>(node);
+              }
+              result = infer_handle_reference_bool(transaction, valuable, goal, level);
+              break;
+          }
+          
+      case CdlExprOp_StringConstant :
+          result = infer_handle_string_constant_bool(subexpr.constants, goal);
+          break;
+                                                
+      case CdlExprOp_IntegerConstant :
+          result = infer_handle_integer_constant_bool(subexpr.constants, goal);
+          break;
+          
+      case CdlExprOp_DoubleConstant :
+          result = infer_handle_double_constant_bool(subexpr.constants, goal);
+          break;
+
+      case CdlExprOp_LogicalNot :
+          result = infer_handle_logical_NOT_bool(transaction, expr, subexpr.lhs_index, goal, level);
+          break;
+          
+      case CdlExprOp_And :
+          result = infer_handle_AND_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+          break;
+          
+      case CdlExprOp_Or :
+          result = infer_handle_OR_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+          break;
+
+      case CdlExprOp_Equal :
+          result = infer_handle_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+          break;
+          
+      default:
+          // No other inferences are implemented at this stage.
+          break;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+//{{{  infer_subexpr_value()                    
+
+static bool
+infer_subexpr_value(CdlTransaction transaction, CdlExpression expr, unsigned int index, CdlSimpleValue& goal, int level)
+{
+    CYG_REPORT_FUNCNAMETYPE("infer_subexpr_value", "result %d");
+    CYG_REPORT_FUNCARG4XV(transaction, expr, index, level);
+    CYG_PRECONDITION_CLASSC(transaction);
+    CYG_PRECONDITION_CLASSC(expr);
+    CYG_PRECONDITIONC((0 <= index) && (index < expr->sub_expressions.size()));
+    
+    bool result = false;
+    CdlSubexpression& subexpr = expr->sub_expressions[index];
+
+    switch(subexpr.op) {
+        
+      case CdlExprOp_Reference          :
+          // The most common case. Follow the reference, and call the appropriate function.
+          // Note that the reference may be unbound.
+          {
+              CdlNode node = expr->references[subexpr.reference_index].get_destination();
+              CdlValuable valuable = 0;
+              if (0 != node) {
+                  valuable = dynamic_cast<CdlValuable>(node);
+              }
+              result = infer_handle_reference_value(transaction, valuable, goal, level);
+              break;
+          }
+          
+      case CdlExprOp_StringConstant     :
+          result = infer_handle_string_constant_value(subexpr.constants, goal);
+          break;
+                                                
+      case CdlExprOp_IntegerConstant    :
+          result = infer_handle_integer_constant_value(subexpr.constants, goal);
+          break;
+          
+      case CdlExprOp_DoubleConstant     :
+          result = infer_handle_double_constant_value(subexpr.constants, goal);
+          break;
+
+      case CdlExprOp_LogicalNot         :
+          result = infer_handle_logical_NOT_value(transaction, expr, subexpr.lhs_index, goal, level);
+          break;
+          
+      case CdlExprOp_And         :
+          result = infer_handle_OR_value(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+          break;
+          
+      case CdlExprOp_Or          :
+          result = infer_handle_AND_value(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+          break;
+
+      default:
+          // No other inferences are implemented at this stage.
+          break;
+    }
+
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+
+//}}}
+//{{{  CdlConflict_Requires::inner_resolve()    
+
+// ----------------------------------------------------------------------------
+// Resolving a requires conflict. There are three ways of tackling
+// this problem, in order of preference:
+//
+// 1) change the terms in the expression to make it evaluate to
+//    true.
+// 2) disable the source so that the requires property is no longer
+//    relevant.
+// 3) make the source inactive, with the same effect.
+//
+// The first one should always be tried. If it is entirely successful
+// then there is no point in looking any further. If user confirmation
+// is required then the second approach should be tried. If that is
+// entirely successful then there is no point in looking further.
+// If user confirmation is required then the third approach should
+// be tried.
+
+bool
+CdlConflict_RequiresBody::inner_resolve(CdlTransaction transaction, int level)
+{
+    CYG_REPORT_FUNCNAME("CdlConflict_Requires::inner_resolve");
+    CYG_REPORT_FUNCARG3XV(this, transaction, level);
+    CYG_PRECONDITION_THISC();
+    CYG_PRECONDITION_CLASSC(transaction);
+
+    bool result = false;
+    
+    CdlProperty_GoalExpression gexpr = dynamic_cast<CdlProperty_GoalExpression>(this->get_property());
+    CdlExpression expr  = gexpr->get_expression();
+    
+    // Only create the sub-transactions when needed
+    CdlTransaction expr_transaction     = 0;
+    CdlTransaction disable_transaction  = 0;
+    CdlTransaction inactive_transaction = 0;
+
+    // Keep track of the preferred solution found to date.
+    CdlTransaction preferred_transaction = 0;
+    
+    expr_transaction     = transaction->make(this);
+    if (!infer_subexpr_bool(expr_transaction, expr, expr->first_subexpression, true, level)) {
+        // No luck here.
+        expr_transaction->cancel();
+        delete expr_transaction;
+        expr_transaction = 0;
+    } else {
+        // We have a possible solution. How acceptable is it?
+        if (!expr_transaction->user_confirmation_required()) {
+            // Whoopee.
+            expr_transaction->commit();
+            delete expr_transaction;
+            result = true;
+            CYG_REPORT_RETVAL(result);
+            return result;
+        } else {
+            // Maybe we can do better.
+            preferred_transaction = expr_transaction;
+            expr_transaction = 0;
+        }
+    }
+
+    // Disabling the source only makes sense if we have a bool or booldata item.
+    CdlValuable valuable = dynamic_cast<CdlValuable>(this->get_node());
+    CYG_ASSERT_CLASSC(valuable);
+
+    if ((CdlValueFlavor_Bool == valuable->get_flavor()) || (CdlValueFlavor_BoolData == valuable->get_flavor())) {
+        disable_transaction = transaction->make(this);
+        if (!infer_set_valuable_bool(disable_transaction, valuable, false, level)) {
+            // No luck here either.
+            disable_transaction->cancel();
+            delete disable_transaction;
+            disable_transaction = 0;
+        } else {
+            if (!disable_transaction->user_confirmation_required()) {
+                disable_transaction->commit();
+                delete disable_transaction;
+                if (0 != preferred_transaction) {
+                    preferred_transaction->cancel();
+                    delete preferred_transaction;
+                    preferred_transaction = 0;
+                }
+                result = true;
+                CYG_REPORT_RETVAL(result);
+                return result;
+            } else if (0 == preferred_transaction) {
+                preferred_transaction = disable_transaction;
+            } else if (disable_transaction->is_preferable_to(preferred_transaction)) {
+                preferred_transaction->cancel(); 
+                delete preferred_transaction;
+                preferred_transaction = disable_transaction;
+                disable_transaction = 0;
+            } else {
+                disable_transaction->cancel();
+                delete disable_transaction;
+                disable_transaction = 0;
+            }
+        }
+    }
+
+    // Now try for the inactive approach. This may work in cases where the disable
+    // approach does not if e.g. there are dependencies between two nodes in the
+    // same container, or if the source of the conflict is not boolean.
+    inactive_transaction = transaction->make(this);
+    if (!infer_make_inactive(inactive_transaction, valuable, level)) {
+        inactive_transaction->cancel();
+        delete inactive_transaction;
+        inactive_transaction = 0;
+    } else {
+        if (!inactive_transaction->user_confirmation_required()) {
+            inactive_transaction->commit();
+            delete inactive_transaction;
+            if (0 != preferred_transaction) {
+                preferred_transaction->cancel();
+                delete preferred_transaction;
+                preferred_transaction = 0;
+            }
+            result = true;
+            CYG_REPORT_RETVAL(result);
+            return result;
+        } else if (0 == preferred_transaction) {
+            preferred_transaction = inactive_transaction;
+        } else if (inactive_transaction->is_preferable_to(preferred_transaction)) {
+            preferred_transaction->cancel(); 
+            delete preferred_transaction;
+            preferred_transaction = inactive_transaction;
+            inactive_transaction = 0;
+        } else {
+            inactive_transaction->cancel();
+            delete inactive_transaction;
+            inactive_transaction = 0;
+        }
+    }
+
+    // Is there any solution at all? If so then use the currently-preferred one.
+    if (0 != preferred_transaction) {
+        preferred_transaction->commit();
+        delete preferred_transaction;
+        preferred_transaction = 0;
+        result = true;
+    }
+    
+    CYG_REPORT_RETVAL(result);
+    return result;
+}
+
+//}}}
+
+//}}}