/// <summary> /// Parse the specified string as a static value. It may be a numeric literal, or a /// reference to a scalar field on the module, or a parameterized static expression. /// Throws if there's a problem. /// </summary> public static double Parse(PartModule module, string text) { if (string.IsNullOrEmpty(text)) { throw new ArgumentException("No value provided"); } // Is it a parameterized source? ParsedParameters parameters = ParsedParameters.TryParse(text); if (parameters != null) { for (int i = 0; i < PARSEABLE_SOURCES.Length; ++i) { double value = PARSEABLE_SOURCES[i](module, parameters); if (!double.IsNaN(value)) { return(value); } } throw new ArgumentException("Unknown static function '" + parameters.Identifier + "'"); } // Try to parse it as a literal numeric value. try { return(double.Parse(text)); } catch (FormatException e) { throw new ArgumentException("'" + text + "' on " + module.ClassName + " of " + module.part.GetTitle() + " can't be parsed as a number: " + e.Message); } }
/// <summary> /// Provides high-level validation looking for obvious syntactic errors, throwing /// ArgumentException if found. Note that just because a string passes this function, /// that doesn't guarantee that it will work when used on a part module. That's /// because *real* validation needs the module as input, so it can hunt for fields, /// other modules, etc. This function's validation only covers the *syntax*, not /// the content. /// </summary> /// <param name="sourceID"></param> public static void Validate(string sourceID) { if (string.IsNullOrEmpty(sourceID)) { throw new ArgumentException("Null or empty color source"); } // Maybe it's a color string. if (Colors.IsColorString(sourceID)) { return; } // Is it a parameterized source? if (ParsedParameters.TryParse(sourceID) != null) { return; } // Is it an identifier? (e.g. module or field name) if (Identifiers.IsSimpleIdentifier(sourceID)) { return; } // Could it be a numeric value? try { double.Parse(sourceID); return; } catch (FormatException) { // nope, not a number } // If we made it this far, it doesn't appear to be syntactically valid. throw new ArgumentException("Invalid color source syntax: " + sourceID); }
/// <summary> /// Parse an IToggle from the specified text. Throws ArgumentException if there's a problem. /// </summary> /// <param name="module"></param> /// <param name="text"></param> /// <returns></returns> public static IToggle Require(PartModule module, string text) { if (string.IsNullOrEmpty(text)) { throw new ArgumentException("must supply a value"); } text = text.Trim(); // Check for constants. if (text == "true") { return(Constant.TRUE); } if (text == "false") { return(Constant.FALSE); } // Perhaps it's an inverted toggle? if (text.StartsWith(NOT_OPERATOR)) { return(Inverter.of(Require(module, text.Substring(NOT_OPERATOR.Length)))); } // Maybe it's an identifier for a toggle. IToggle found = Identifiers.FindFirst <IToggle>(module.part, text); if (found != null) { return(found); } // Could it be a named-field reference? Identifiers.IFieldEvaluator field = Identifiers.FindKSPField(module.part, text); if (field != null) { if (!typeof(bool).IsAssignableFrom(field.FieldType)) { throw new ArgumentException("Can't use " + text + " as a boolean field (it's of type " + field.FieldType.Name + ")"); } return(new NamedField(field)); } // Perhaps it's a parameterized expression? ParsedParameters parsedParams = ParsedParameters.TryParse(text); if (parsedParams != null) { for (int i = 0; i < PARSEABLE_TOGGLES.Length; ++i) { IToggle parsed = PARSEABLE_TOGGLES[i](module, parsedParams); if (parsed != null) { return(parsed); } } } // Nope, not parseable. throw new ArgumentException("Invalid toggle syntax \"" + text + "\""); }
/// <summary> /// Given a color source ID (which might be a literal color, the ID of a controller, /// or a parameterized source), try to find the appropriate source. If it can't /// be parsed or found, throws a ColorSourceException. private static IColorSource FindPrivate(PartModule module, string sourceID) { if (string.IsNullOrEmpty(sourceID)) { throw new ColorSourceException(module, "Null or empty color source"); } // Maybe it's a color string. if (Colors.IsColorString(sourceID)) { return(Constant(Colors.Parse(sourceID, Color.black))); } // Is it a parameterized source? ParsedParameters parsedParams = ParsedParameters.TryParse(sourceID); if (parsedParams != null) { // It has the right syntax for a parameterizeed source. Do we recognize it? for (int i = 0; i < PARSEABLE_SOURCES.Length; ++i) { IColorSource candidate = PARSEABLE_SOURCES[i](module, parsedParams); if (candidate != null) { return(candidate); } } throw new ColorSourceException(module, "Unknown function type '" + parsedParams.Identifier + "'"); } // Maybe it's another field on the module? for (int i = 0; i < module.Fields.Count; ++i) { BaseField field = module.Fields[i]; if (!sourceID.Equals(field.name)) { continue; } if (!ColorSourceIDField.Is(field)) { throw new ColorSourceException( module, sourceID + " field on " + module.ClassName + " of " + module.part.GetTitle() + " is not a color source ID field"); } return(Find(module, field.GetValue <string>(module))); } // Maybe it's a module on the part? for (int i = 0; i < module.part.Modules.Count; ++i) { IColorSource candidate = module.part.Modules[i] as IColorSource; if (candidate == null) { continue; } if (sourceID.Equals(candidate.ColorSourceID)) { return(candidate); } } // not found throw new ColorSourceException(module, "Can't find a color source named '" + sourceID + "'"); }
/// <summary> /// Parse an IScalar from the specified text. Throws ArgumentException if there's a problem. /// </summary> /// <param name="module"></param> /// <param name="text"></param> /// <returns></returns> public static IScalar Require(PartModule module, string text) { if (string.IsNullOrEmpty(text)) { throw new ArgumentException("must supply a value"); } text = text.Trim(); // Perhaps it's an inverted scalar? if (text.StartsWith(INVERT_OPERATOR)) { return(LinearTransform.Invert(Require(module, text.Substring(INVERT_OPERATOR.Length)))); } // Maybe it's an identifier for a scalar. IScalar found = Identifiers.FindFirst <IScalar>(module.part, text); if (found != null) { return(found); } // Could it be a named-field reference? Identifiers.IFieldEvaluator field = Identifiers.FindKSPField(module.part, text); if (field != null) { if (NamedSingleField.Is(field)) { return(new NamedSingleField(field)); } if (NamedDoubleField.Is(field)) { return(new NamedDoubleField(field)); } if (NamedIntegerField.Is(field)) { return(new NamedIntegerField(field)); } if (NamedShortField.Is(field)) { return(new NamedShortField(field)); } if (NamedLongField.Is(field)) { return(new NamedLongField(field)); } throw new ArgumentException("Can't use " + text + " as a scalar field (it's of type " + field.FieldType.Name + ")"); } // Perhaps it's a parameterized expression? ParsedParameters parsedParams = ParsedParameters.TryParse(text); if (parsedParams != null) { for (int i = 0; i < PARSEABLE_SCALARS.Length; ++i) { IScalar parsed = PARSEABLE_SCALARS[i](module, parsedParams); if (parsed != null) { return(parsed); } } } // Last chance: it could be a static value. return(Constant.Of(Statics.Parse(module, text))); }