/// <summary> /// Sets the value(s) of the specified property by name. If the /// <see cref="ObjectProperty"/> is null, then a new instance is /// created with the inital values provided. /// </summary> /// <remarks> /// This method should only be used when the ObjectProperty is null /// outside of the ScriptEngine, since it uses heavy relection. /// </remarks> /// <param name="propertyName">The <see cref="PropertyName"/> to assign</param> /// <param name="values">The values to set in the <see cref="ObjectProperty"/></param> public void SetValue(string propertyName, params object[] values) { // Fetch our property that we are setting the value to KeyValuePair <string, PropertyInfo> prop = GetProperty(propertyName); // If the first values argument is null, then we remove this property if (values == null) { Remove(prop.Value); return; } // If the value is null, then we create a new object property instance var obj = (ObjectProperty)prop.Value.GetValue(this); if (obj == null) { // === // Since the property is null, we must create a con script command. // So here, we will use the ReferenceType of this object, and property // name to create a fake token, and use that to set the value // === StringBuilder builder = new StringBuilder(); builder.Append($"{Token.TokenArgs.ReferenceType.Name}.{propertyName}"); foreach (object value in values) { builder.Append($" {value}"); } // Create a new Token Token token = Tokenizer.Tokenize(builder.ToString()); token.File = File; // Create the ObjectProperty instance, and set this Property to that instance obj = ObjectProperty.Create(prop.Value, this, token); // Add entry? IObjectPropertyCollection's add thier own properties Type type = obj.GetType(); if (type.GetInterface("IObjectPropertyCollection") == null) { Token.File?.AddProperty(obj); } } // Set the new object property values obj.SetValues(values); prop.Value.SetValue(this, obj); }
/// <summary> /// This method is responsible for parsing an object property /// reference line from inside of a .con or .ai file, and converting /// the property into a C# property /// </summary> /// <remarks> /// The type of value is figured out within the method, but only certain types of /// object types can be parsed here. /// </remarks> /// <param name="token">The token for this ObjectProperty</param> /// <param name="objectLevel">Specifies the nested object level for this Property</param> public virtual void Parse(Token token, int objectLevel = 0) { // Seperate our property name and values TokenArgs tokenArgs = token.TokenArgs; string propName = tokenArgs.PropertyNames[objectLevel]; // Fetch our property that we are setting the value to KeyValuePair <string, PropertyInfo> prop = GetProperty(propName); Type propType = prop.Value.PropertyType; bool isCollection = propType.GetInterface("IObjectPropertyCollection") != null; // Get the value that is set var value = prop.Value.GetValue(this); // Is this an object method, or Object Property? if (propType.BaseType.Name == "ObjectMethod") { // Object methods are always instantiated in the object's constructor ObjectMethod method = (ObjectMethod)value; ConFileEntry item = method.Invoke(token); // Add item to the file entry list if (item != null) { token.File?.AddEntry(item, token); } } else { // If the value is null, then we create a new object property instance ObjectProperty obj = (ObjectProperty)value; if (obj == null) { // Create instance and add it to the entries list obj = ObjectProperty.Create(prop.Value, this, token); // Add entry? Property Collections add thier own properties if (!isCollection) { token.File?.AddProperty(obj); } } // Create our instance, and parse obj.SetValue(token, objectLevel); prop.Value.SetValue(this, obj); } }
/// <summary> /// Creates a component (Sub Object) for an ObjectTemplate /// </summary> /// <param name="token"></param> /// <param name="comment"></param> protected virtual ConFileEntry Method_CreateComponent(Token token, string name) { Type type = this.GetType(); // Token correction token.Kind = TokenType.Component; // Ensure we have a map of components => property if (!ComponentTypes.ContainsKey(name)) { throw new Exception($"Unregistered component type \"{name}\""); } else if (!ComponentMap.ContainsKey(type.Name)) { throw new Exception($"Object type \"{type.Name}\" does not support Components"); } else if (!ComponentMap[type.Name].ContainsKey(name)) { throw new Exception($"Unsupported Component type \"{name}\" in \"{type.Name}\""); } // Get our field PropertyInfo property = ComponentMap[type.Name][name]; //Type componentType = property.PropertyType.GenericTypeArguments[0]; Type componentType = ComponentTypes[name]; var args = new object[] { name, token }; // Create instances var component = (ConFileObject)Activator.CreateInstance(componentType, args); //var objProperty = ObjectProperty.Create(property, component, token); var objProperty = ObjectProperty.Create(property, this, token); // Use reflection to set the value of the new component instance objProperty.SetValues(new object[] { component }, token); // Set value of this.{name} property.SetValue(this, objProperty); // Add component to file entries by returning it return(objProperty); }
/// <summary> /// Converts the object value into the Typed variant of the Specified type parameter, /// and returns the <see cref="ValueInfo{T}"/> object for this token property. /// This method also sets the value of the <see cref="ValueInfo{T}.Expression"/> field /// if this property value is a variable/constant reference. /// </summary> /// <param name="Value">The current property index value</param> /// <returns></returns> public static ValueInfo <K> CreateValueInfo <K>(ObjectProperty property, object Value) { // Grab the type of K Type PropertyType = typeof(K); string strValue = Value.ToString(); Expression exp = null; // Check for variable or constant... names must begin with v_ or c_ if (strValue.StartsWithAny("v_", "c_")) { // We use the File's expression reference and NOT the Scope, // because this object property could be created AFTER the inital // parse of the file exp = property.Owner.File.GetExpressionReference(strValue, property); Value = exp.Value; } K newValue = ConvertValue <K>(Value, PropertyType); return(new ValueInfo <K>(newValue, exp)); }
/// <summary> /// Action method for adding child templates to this object /// </summary> /// <param name="token"></param> /// <param name="name"></param> /// <returns></returns> protected virtual ConFileEntry Method_AddTemplate(Token token, string name) { // Get the internal property, and check if templates is null var info = GetProperty("__addTemplate").Value; if (Templates == null) { Templates = new ObjectPropertyList <ChildTemplate>("addTemplate", token, info, this); } // Create the object template value, and the object property ChildTemplate ct = new ChildTemplate(token.TokenArgs.Arguments.Last(), token); var prop = new ObjectProperty <ChildTemplate>("addTemplate", token, info, this); // We must also manually define the ValueInfo, because ChildTemplate // is NOT a RefernceType object prop.Argument = new ValueInfo <ChildTemplate>(ct); Templates.Items.Add(prop); return(prop); }