/// <summary> /// Returns true if this node evaluates to an empty string, /// otherwise false. /// It may be cheaper to determine whether an expression will evaluate /// to empty than to fully evaluate it. /// Implementations should cache the result so that calls after the first are free. /// </summary> internal override bool EvaluatesToEmpty(IConditionEvaluationState state) { if (_cachedExpandedValue == null) { if (_expandable) { string expandBreakEarly = state.ExpandIntoStringBreakEarly(_value); if (expandBreakEarly == null) { // It broke early: we can't store the value, we just // know it's non empty return(false); } // It didn't break early, the result is accurate, // so store it so the work isn't done again. _cachedExpandedValue = expandBreakEarly; } else { _cachedExpandedValue = _value; } } return(_cachedExpandedValue.Length == 0); }
/// <summary> /// Evaluates as boolean and evaluates children as boolean, numeric, or string. /// Order in which comparisons are attempted is numeric, boolean, then string. /// Updates conditioned properties table. /// </summary> internal override bool BoolEvaluate(IConditionEvaluationState state) { // It's sometimes possible to bail out of expansion early if we just need to know whether // the result is empty string. // If at least one of the left or the right hand side will evaluate to empty, // and we know which do, then we already have enough information to evaluate this expression. // That means we don't have to fully expand a condition like " '@(X)' == '' " // which is a performance advantage if @(X) is a huge item list. if (this.LeftChild.EvaluatesToEmpty(state) || this.RightChild.EvaluatesToEmpty(state)) { UpdateConditionedProperties(state); return(Compare(this.LeftChild.EvaluatesToEmpty(state), this.RightChild.EvaluatesToEmpty(state))); } if (this.LeftChild.CanNumericEvaluate(state) && this.RightChild.CanNumericEvaluate(state)) { return(Compare(this.LeftChild.NumericEvaluate(state), this.RightChild.NumericEvaluate(state))); } else if (this.LeftChild.CanBoolEvaluate(state) && this.RightChild.CanBoolEvaluate(state)) { return(Compare(this.LeftChild.BoolEvaluate(state), this.RightChild.BoolEvaluate(state))); } else // string comparison { string leftExpandedValue = this.LeftChild.GetExpandedValue(state); string rightExpandedValue = this.RightChild.GetExpandedValue(state); UpdateConditionedProperties(state); return(Compare(leftExpandedValue, rightExpandedValue)); } }
/// <summary> /// Evaluate as boolean /// </summary> internal override bool BoolEvaluate(IConditionEvaluationState state) { bool isLeftNum = this.LeftChild.CanNumericEvaluate(state); bool isLeftVersion = this.LeftChild.CanVersionEvaluate(state); bool isRightNum = this.RightChild.CanNumericEvaluate(state); bool isRightVersion = this.RightChild.CanVersionEvaluate(state); bool isNumeric = isLeftNum && isRightNum; bool isVersion = isLeftVersion && isRightVersion; // If the values identify as numeric, make that comparison instead of the Version comparison since numeric has a stricter definition if (isNumeric) { return(Compare(this.LeftChild.NumericEvaluate(state), this.RightChild.NumericEvaluate(state))); } else if (isVersion) { return(Compare(this.LeftChild.VersionEvaluate(state), this.RightChild.VersionEvaluate(state))); } // If the numbers are of a mixed type, call that specific Compare method if (isLeftNum && isRightVersion) { return(Compare(this.LeftChild.NumericEvaluate(state), this.RightChild.VersionEvaluate(state))); } else if (isLeftVersion && isRightNum) { return(Compare(this.LeftChild.VersionEvaluate(state), this.RightChild.NumericEvaluate(state))); } // Throw error here as this code should be unreachable ErrorUtilities.ThrowInternalErrorUnreachable(); return(false); }
/// <summary> /// Updates the conditioned properties table if it hasn't already been done. /// </summary> private void UpdateConditionedProperties(IConditionEvaluationState state) { if (!this._conditionedPropertiesUpdated && state.ConditionedPropertiesInProject != null) { string leftUnexpandedValue = this.LeftChild.GetUnexpandedValue(state); string rightUnexpandedValue = this.RightChild.GetUnexpandedValue(state); if (leftUnexpandedValue != null) { ConditionEvaluator.UpdateConditionedPropertiesTable (state.ConditionedPropertiesInProject, leftUnexpandedValue, this.RightChild.GetExpandedValue(state)); } if (rightUnexpandedValue != null) { ConditionEvaluator.UpdateConditionedPropertiesTable (state.ConditionedPropertiesInProject, rightUnexpandedValue, this.LeftChild.GetExpandedValue(state)); } this._conditionedPropertiesUpdated = true; } }
/// <summary> /// Whether it can be evaluated as a Version /// </summary> internal override bool CanVersionEvaluate(IConditionEvaluationState state) { // Check if the value can be formatted as a Version number // This is needed for nodes that identify as Numeric but can't be parsed as numbers (e.g. 8.1.1.0 vs 8.1) Version unused; return(Version.TryParse(_value, out unused)); }
/// <summary> /// Evaluate as boolean /// </summary> internal override bool BoolEvaluate(IConditionEvaluationState state) { if (LeftChild.BoolEvaluate(state)) { // Short circuit return(true); } else { return(RightChild.BoolEvaluate(state)); } }
/// <summary> /// Value after any item and property expressions are expanded /// </summary> /// <returns></returns> internal override string GetExpandedValue(IConditionEvaluationState state) { if (_cachedExpandedValue == null) { if (_expandable) { _cachedExpandedValue = state.ExpandIntoString(_value); } else { _cachedExpandedValue = _value; } } return(_cachedExpandedValue); }
/// <summary> /// Evaluate node as boolean /// </summary> internal override bool BoolEvaluate(IConditionEvaluationState state) { if (String.Compare(_functionName, "exists", StringComparison.OrdinalIgnoreCase) == 0) { return(true); } if (String.Compare(_functionName, "HasTrailingSlash", StringComparison.OrdinalIgnoreCase) == 0) { // often used to append slash to path so return false to enable this codepath return(false); } // We haven't implemented any other "functions" return(false); }
/// <summary> /// Value before any item and property expressions are expanded /// </summary> /// <returns></returns> internal override string GetUnexpandedValue(IConditionEvaluationState state) { return(null); }
internal abstract Version VersionEvaluate(IConditionEvaluationState state);
/// <summary> /// Whether it can be evaluated as a boolean: never allowed for numerics /// </summary> internal override bool CanBoolEvaluate(IConditionEvaluationState state) { // Numeric expressions are never allowed to be treated as booleans. return(false); }
/// <summary> /// Value before any item and property expressions are expanded /// </summary> /// <returns></returns> internal abstract string GetUnexpandedValue(IConditionEvaluationState state);
internal override bool CanVersionEvaluate(IConditionEvaluationState state) { Version unused; return(Version.TryParse(GetExpandedValue(state), out unused)); }
internal override bool CanNumericEvaluate(IConditionEvaluationState state) { return(ConversionUtilities.ValidDecimalOrHexNumber(GetExpandedValue(state))); }
internal override bool CanBoolEvaluate(IConditionEvaluationState state) { return(ConversionUtilities.CanConvertStringToBool(GetExpandedValue(state))); }
internal override Version VersionEvaluate(IConditionEvaluationState state) { return(Version.Parse(GetExpandedValue(state))); }
/// <summary> /// The main evaluate entry point for expression trees /// </summary> /// <param name="state"></param> /// <returns></returns> internal bool Evaluate(IConditionEvaluationState state) { return(BoolEvaluate(state)); }
internal override bool CanBoolEvaluate(IConditionEvaluationState state) { return(this.LeftChild.CanBoolEvaluate(state)); }
/// <summary> /// Returns expanded value with '!' prepended. Useful for error messages. /// </summary> internal override string GetExpandedValue(IConditionEvaluationState state) { return("!" + this.LeftChild.GetExpandedValue(state)); }
/// <summary> /// Evaluate as numeric /// </summary> internal override double NumericEvaluate(IConditionEvaluationState state) { return(ConversionUtilities.ConvertDecimalOrHexToDouble(_value)); }
/// <summary> /// Numeric evaluation is never allowed for operators /// </summary> internal override double NumericEvaluate(IConditionEvaluationState state) { // Should be unreachable: all calls check CanNumericEvaluate() first ErrorUtilities.VerifyThrow(false, "Cannot numeric evaluate an operator"); return(0.0D); }
/// <summary> /// Returns true if this node evaluates to an empty string, /// otherwise false. /// (It may be cheaper to determine whether an expression will evaluate /// to empty than to fully evaluate it.) /// Implementations should cache the result so that calls after the first are free. /// </summary> internal virtual bool EvaluatesToEmpty(IConditionEvaluationState state) { return(false); }
/// <summary> /// Evaluate as boolean /// </summary> internal override bool BoolEvaluate(IConditionEvaluationState state) { // Should be unreachable: all calls check CanBoolEvaluate() first ErrorUtilities.VerifyThrow(false, "Can't evaluate a numeric expression as boolean."); return(false); }
/// <summary> /// Version evaluation is never allowed for operators /// </summary> internal override Version VersionEvaluate(IConditionEvaluationState state) { ErrorUtilities.VerifyThrow(false, "Cannot version evaluate an operator"); return(null); }
/// <summary> /// Evaluate as a Version /// </summary> internal override Version VersionEvaluate(IConditionEvaluationState state) { return(Version.Parse(_value)); }
/// <summary> /// Whether boolean evaluation is allowed: always allowed for operators /// </summary> internal override bool CanBoolEvaluate(IConditionEvaluationState state) { return(true); }
/// <summary> /// Whether it can be evaluated as numeric /// </summary> internal override bool CanNumericEvaluate(IConditionEvaluationState state) { // It is not always possible to numerically evaluate even a numerical expression - // for example, it may overflow a double. So check here. return(ConversionUtilities.ValidDecimalOrHexNumber(_value)); }
/// <summary> /// Whether the node can be evaluated as a version: by default, /// this is not allowed /// </summary> internal override bool CanVersionEvaluate(IConditionEvaluationState state) { return(false); }
/// <summary> /// Get the expanded value /// </summary> internal override string GetExpandedValue(IConditionEvaluationState state) { return(_value); }
internal abstract double NumericEvaluate(IConditionEvaluationState state);