public static ValueOutput CompatibleValueOutput(this IUnit unit, Type inputType) { Ensure.That(nameof(inputType)).IsNotNull(inputType); return(unit.valueOutputs .Where(valueOutput => ConversionUtility.CanConvert(valueOutput.type, inputType, false)) .OrderBy((valueOutput) => { var exactType = inputType == valueOutput.type; var free = !valueOutput.hasValidConnection; if (free && exactType) { return 1; } else if (free) { return 2; } else if (exactType) { return 3; } else { return 4; } }).FirstOrDefault()); }
public static bool HasCompatibleValueOutput(this IUnitOption option, Type inputType) { Ensure.That(nameof(inputType)).IsNotNull(inputType); foreach (var valueOutputType in option.valueOutputTypes) { if (ConversionUtility.CanConvert(valueOutputType, inputType, false)) { return(true); } } return(false); }
private bool CanPredict(ValueInput input) { if (!input.hasValidConnection) { if (!TryGetDefaultValue(input, out var defaultValue)) { return(false); } if (typeof(Component).IsAssignableFrom(input.type)) { defaultValue = defaultValue?.ConvertTo(input.type); } if (!input.allowsNull && defaultValue == null) { return(false); } return(true); } var output = input.validConnectedPorts.Single(); if (!CanPredict(output)) { return(false); } var connectedValue = GetValue(output); if (!ConversionUtility.CanConvert(connectedValue, input.type, false)) { return(false); } if (typeof(Component).IsAssignableFrom(input.type)) { connectedValue = connectedValue?.ConvertTo(input.type); } if (!input.allowsNull && connectedValue == null) { return(false); } return(true); }
private IEnumerable <Warning> ValueInputWarnings(ValueInput valueInput) { // We can disable null reference check if no self is available // and the port requires an owner, for example in macros. var trustFutureOwner = valueInput.nullMeansSelf && reference.self == null; var checkForNullReference = BoltFlow.Configuration.predictPotentialNullReferences && !valueInput.allowsNull && !trustFutureOwner; var checkForMissingComponent = BoltFlow.Configuration.predictPotentialMissingComponents && typeof(Component).IsAssignableFrom(valueInput.type); // Note that we cannot directly check the input's predicted value, because it // will return false for safeguard specifically because it might be missing requirements. // Therefore, we first check the connected value, then the default value. // If the port is connected to a predictable output, use the connected value to perform checks. if (valueInput.hasValidConnection) { var valueOutput = valueInput.validConnectedPorts.Single(); if (Flow.CanPredict(valueOutput, reference)) { if (checkForNullReference) { if (Flow.Predict(valueOutput, reference) == null) { yield return(Warning.Severe($"{PortLabel(valueInput)} cannot be null.")); } } if (checkForMissingComponent) { var connectedPredictedValue = Flow.Predict(valueOutput, reference); // This check is necessary, because the predicted value could be // incompatible as connections with non-guaranteed conversions are allowed. if (ConversionUtility.CanConvert(connectedPredictedValue, typeof(GameObject), true)) { var gameObject = ConversionUtility.Convert <GameObject>(connectedPredictedValue); if (gameObject != null) { var component = (Component)ConversionUtility.Convert(gameObject, valueInput.type); if (component == null) { yield return(Warning.Caution($"{PortLabel(valueInput)} is missing a {valueInput.type.DisplayName()} component.")); } } } } } } // If the port isn't connected but has a default value, use the default value to perform checks. else if (valueInput.hasDefaultValue) { if (checkForNullReference) { if (Flow.Predict(valueInput, reference) == null) { yield return(Warning.Severe($"{PortLabel(valueInput)} cannot be null.")); } } if (checkForMissingComponent) { var unconnectedPredictedValue = Flow.Predict(valueInput, reference); if (ConversionUtility.CanConvert(unconnectedPredictedValue, typeof(GameObject), true)) { var gameObject = ConversionUtility.Convert <GameObject>(unconnectedPredictedValue); if (gameObject != null) { var component = (Component)ConversionUtility.Convert(gameObject, valueInput.type); if (component == null) { yield return(Warning.Caution($"{PortLabel(valueInput)} is missing a {valueInput.type.DisplayName()} component.")); } } } } } // The value isn't connected and has no default value, // therefore it is certain to be missing at runtime. else { yield return(Warning.Severe($"{PortLabel(valueInput)} is missing.")); } }